authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-02 21:40:11-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-03 01:50:33-07:00
log0fe456a8909d9b9991afbe28c28f915d031ca44f
tree2a4f6a8e8278499eb545761682cebed6895d558b
parenta4a0ccf055b14aa599b027e96a679054fea6b5a2
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

fix: the Themes dialog and its entry points are Customize Font Styles, with the Styles icon

Clover: the right-click menus' Theme, the Format menu's and palette's Customize Theme and the Styles gallery's Customize all open the same dialog, so they and the dialog share one name, and the Styles button's Aa icon; scoped, the dialog reads Customize Font Styles for This Section. In the gallery it sits under a rule, where OneNote 2010 keeps Clear Formatting, and in context menus it comes last, under a rule. A gallery group can start under a rule, which More Symbols gains too. Assisted-by: claude-opus-5.5

9 files changed, 55 insertions(+), 16 deletions(-)

crates/snowbound/src/art.rs-2
......@@ -110,7 +110,6 @@ pub const CLOSE_NOTEBOOK: &[&str] = art!(
110110pub const CODE: &[&str] = art!("icons/code");
111111pub const COMMAND_PALETTE: &[&str] = art!("icons/palette", "icons/badge-command");
112112pub const COPY_LINK: &[&str] = art!("icons/page", "icons/badge-link");
113pub const CUSTOMIZE_THEME: &[&str] = art!("icons/theme", "icons/badge-edit");
114113pub const CUSTOMIZE_TAGS: &[&str] = art!("icons/tag", "icons/badge-edit");
115114pub const DELETE: &[&str] = art!("icons/delete");
116115pub const EMPTY_RECYCLE_BIN: &[&str] = art!("icons/recycle-bin", "icons/badge-remove");
......@@ -195,7 +194,6 @@ pub const SNAP_TO_GRID: &[&str] = art!("icons/snap-to-grid");
195194pub const STOP: &[&str] = art!("icons/stop");
196195pub const SUBPAGE: &[&str] = art!("icons/subpage");
197196pub const SYNC_NOW: &[&str] = art!("icons/sync-now");
198pub const THEME: &[&str] = art!("icons/theme");
199197pub const TO_DO: &[&str] = art!("icons/to-do");
200198pub const UPDATE: &[&str] = art!("icons/update");
201199pub const ZOOM_ACTUAL: &[&str] = art!("icons/zoom-actual");
crates/snowbound/src/background.rs+3
......@@ -89,18 +89,21 @@ pub fn menu(
8989 let groups = [
9090 ui::popup::Group {
9191 heading: "Page Color",
92 ruled: false,
9293 cells: 2 + PAGE_COLORS.len(),
9394 columns: COLUMNS,
9495 size: SWATCH,
9596 },
9697 ui::popup::Group {
9798 heading: "Background",
99 ruled: false,
98100 cells: 2 + ART.len(),
99101 columns: 3,
100102 size: [TILE[0] + 8.0, TILE[1] + 8.0],
101103 },
102104 ui::popup::Group {
103105 heading: "Rule Lines",
106 ruled: false,
104107 cells: 1 + RULE_LINES.len(),
105108 columns: COLUMNS,
106109 size: [RULES; 2],
crates/snowbound/src/commands.rs+1-1
......@@ -596,7 +596,7 @@ pub const COMMANDS: &[Command] = &[
596596 row!(Id::Style(8), "Quote", NONE, NONE),
597597 row!(Id::Style(9), "Code", NONE, NONE),
598598 row!(Id::Style(10), "Normal", NONE, NONE),
599 row!(Id::Themes, "Customize Theme", NONE, NONE),
599 row!(Id::Themes, "Customize Font Styles", NONE, NONE),
600600 row!(Id::CustomizeTags, "Customize Tags", NONE, NONE),
601601 row!(
602602 Id::RemoveTags,
crates/snowbound/src/main.rs+3-1
......@@ -5184,6 +5184,7 @@ fn list_gallery(
51845184 let groups = [(headings[0], recent.len()), (headings[1], count + 1)].map(|(heading, cells)| {
51855185 ui::popup::Group {
51865186 heading,
5187 ruled: false,
51875188 cells,
51885189 columns: 5,
51895190 size,
......@@ -5228,6 +5229,7 @@ fn pen_gallery(
52285229 const CELL: [f32; 2] = [40.0, 24.0];
52295230 let groups = [ui::popup::Group {
52305231 heading: "Pens",
5232 ruled: false,
52315233 cells: pens.len(),
52325234 columns: 5,
52335235 size: CELL,
......@@ -5666,7 +5668,7 @@ fn artwork(id: commands::Id) -> Option<&'static [&'static str]> {
56665668 Cmd::Style(8) => art::QUOTE,
56675669 Cmd::Style(9) => art::CODE,
56685670 Cmd::Style(_) => art::NORMAL,
5669 Cmd::Themes => art::CUSTOMIZE_THEME,
5671 Cmd::Themes => art::STYLES,
56705672 Cmd::OpenShared => art::OPEN_SHARED,
56715673 Cmd::LiveShare => art::LIVE_SHARE,
56725674 Cmd::MarkdownShortcuts => art::MARKDOWN,
crates/snowbound/src/menus.rs+11-5
......@@ -151,7 +151,7 @@ impl Action {
151151 Action::NewGroup => art::NEW_SECTION_GROUP,
152152 Action::Reveal => art::FOLDER,
153153 Action::Close => art::CLOSE_NOTEBOOK,
154 Action::Theme => art::THEME,
154 Action::Theme => art::STYLES,
155155 Action::Colors => art::SECTION_COLOR,
156156 Action::Sync => art::SYNC_NOW,
157157 Action::NewNotebook => art::NEW_NOTEBOOK,
......@@ -334,6 +334,11 @@ impl State {
334334 (action, item)
335335 };
336336 let nowhere = || self.destinations(target).is_empty();
337 // Last on a page, section or notebook, under a rule.
338 let styles = || {
339 let title = commands::command(commands::Id::Themes).title;
340 item(Action::Theme, title, false, true)
341 };
337342 match target {
338343 // OneNote's Move or Copy takes a page out of the bin; nothing else changes it.
339344 Target::Page { path, .. } if crate::recycle::binned(path) => vec![
......@@ -360,11 +365,11 @@ impl State {
360365 item(Action::NewPage, "New Page", false, true),
361366 // OneNote offers this beside New Page, which is a plain + here.
362367 item(Action::NewSubpage, "New Subpage", false, false),
363 item(Action::Theme, "Theme", false, true),
364368 ];
365369 // Levels and versions are known once the section is open.
366370 let Some(session) = self.session.as_ref().filter(|_| self.open(library, path))
367371 else {
372 actions.push(styles());
368373 return actions;
369374 };
370375 let pages = &session.pages;
......@@ -393,6 +398,7 @@ impl State {
393398 session.page_versions(*space).is_empty(),
394399 true,
395400 ),
401 styles(),
396402 ]);
397403 actions
398404 }
......@@ -423,7 +429,7 @@ impl State {
423429 section_file(library, path).is_none(),
424430 false,
425431 ),
426 item(Action::Theme, "Theme", false, false),
432 styles(),
427433 ]);
428434 }
429435 actions
......@@ -493,12 +499,11 @@ impl State {
493499 library.folder().is_none(),
494500 true,
495501 ),
496 item(Action::Theme, "Theme", false, false),
497502 item(
498503 Action::RecycleBin,
499504 "Notebook Recycle Bin",
500505 library.catalog().is_none(),
501 true,
506 false,
502507 ),
503508 item(
504509 Action::Properties,
......@@ -506,6 +511,7 @@ impl State {
506511 library.catalog().is_none(),
507512 false,
508513 ),
514 styles(),
509515 ]
510516 }
511517 Target::Closed(_) | Target::Server(_) => {
crates/snowbound/src/symbol.rs+2
......@@ -82,12 +82,14 @@ pub fn gallery(ui: &mut Ui, id: Id, anchor: Anchor, symbols: &[char]) -> Option<
8282 let groups = [
8383 ui::popup::Group {
8484 heading: "",
85 ruled: false,
8586 cells: symbols.len(),
8687 columns: 5,
8788 size: [SIZE; 2],
8889 },
8990 ui::popup::Group {
9091 heading: "",
92 ruled: true,
9193 cells: 1,
9294 columns: 1,
9395 size: [5.0 * SIZE, 28.0],
crates/snowbound/src/themes.rs+10-7
......@@ -1,8 +1,8 @@
11//! Style themes in the app (`resources/styles.md`): the page wears its own theme, else its
22//! section's, else its notebook's, as its paragraph style objects; the Styles gallery
3//! applies a style in the page's theme, and its Customize… opens the Themes dialog, which
4//! edits a notebook's own themes with a live preview and gives one to the page, the section
5//! or the notebook.
3//! applies a style in the page's theme, and its Customize Font Styles opens the dialog of
4//! that name, which edits a notebook's own themes with a live preview and gives one to the
5//! page, the section or the notebook.
66
77use crate::{Library, State, filetime};
88use accesskit::Role;
......@@ -255,12 +255,13 @@ impl State {
255255 ..Spec::default()
256256 },
257257 );
258 let named = crate::commands::command(crate::commands::Id::Themes).title;
258259 if let Some(node) = ui.access(id()) {
259 node.set_label("Themes");
260 node.set_label(named);
260261 }
261262 let title = match &dialog.targets[..] {
262 [(scope, _)] => format!("Theme for {}", scope.name()),
263 _ => "Themes".to_owned(),
263 [(scope, _)] => format!("{named} for {}", scope.name()),
264 _ => named.to_owned(),
264265 };
265266 ui.leaf(
266267 "title",
......@@ -770,12 +771,14 @@ pub(crate) fn gallery(
770771 .enumerate()
771772 .map(|(at, (name, _))| ui::popup::Group {
772773 heading: if at == 0 { &sheet.name } else { "" },
774 ruled: false,
773775 cells: 1,
774776 columns: 1,
775777 size: row(shown_size(&sheet.styles[*name], GALLERY_LARGEST) * 1.25),
776778 })
777779 .chain([ui::popup::Group {
778780 heading: "",
781 ruled: true,
779782 cells: 1,
780783 columns: 1,
781784 size: row(16.0),
......@@ -807,7 +810,7 @@ pub(crate) fn gallery(
807810 "customize",
808811 Spec {
809812 size: [fill(), fill()],
810 text: Some("Customize"),
813 text: Some(crate::commands::command(Cmd::Themes).title),
811814 ..Spec::default()
812815 },
813816 );
crates/ui/src/popup.rs+24
......@@ -1038,6 +1038,8 @@ pub fn table_picker(ui: &mut Ui, id: Id, anchor: Anchor, size: [usize; 2]) -> Op
10381038#[derive(Clone, Copy, Debug)]
10391039pub struct Group<'a> {
10401040 pub heading: &'a str,
1041 /// Starts under a rule, as a gallery's command after its choices.
1042 pub ruled: bool,
10411043 pub cells: usize,
10421044 pub columns: usize,
10431045 pub size: [f32; 2],
......@@ -1096,7 +1098,29 @@ pub fn gallery(
10961098 width + 2.0 * ui.theme.menu().pad,
10971099 );
10981100 let mut index = 0;
1101 let style = theme.menu();
10991102 for (number, group) in groups.iter().enumerate() {
1103 if group.ruled {
1104 ui.open(
1105 ("rule", number),
1106 Spec {
1107 size: [fill(), px(style.rule_band)],
1108 ..Spec::default()
1109 },
1110 );
1111 ui.leaf(
1112 "line",
1113 Spec {
1114 flags: Flags::FLOAT,
1115 size: [fill(), px(1.0)],
1116 position: [0.0, (style.rule_band - 1.0) / 2.0],
1117 inset: [style.rule_inset, 0.0, style.rule_inset, 0.0],
1118 fill: Some(style.rule),
1119 ..Spec::default()
1120 },
1121 );
1122 ui.close();
1123 }
11001124 if !group.heading.is_empty() {
11011125 ui.leaf(
11021126 ("heading", number),
crates/ui/src/tests.rs+1
......@@ -2911,6 +2911,7 @@ fn galleries_choose_across_their_groups_by_keys_and_clicks() {
29112911 let gallery = Id::ROOT.child("gallery");
29122912 let groups = [("Recent", 2), ("Library", 5)].map(|(heading, cells)| popup::Group {
29132913 heading,
2914 ruled: false,
29142915 cells,
29152916 columns: 3,
29162917 size: [30.0, 30.0],