From 0fe456a8909d9b9991afbe28c28f915d031ca44f Mon Sep 17 00:00:00 2001 From: clover caruso Date: Fri, 2 Oct 2026 21:40:11 -0700 Subject: [PATCH] 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 --- crates/snowbound/src/art.rs | 2 -- crates/snowbound/src/background.rs | 3 +++ crates/snowbound/src/commands.rs | 2 +- crates/snowbound/src/main.rs | 4 +++- crates/snowbound/src/menus.rs | 16 +++++++++++----- crates/snowbound/src/symbol.rs | 2 ++ crates/snowbound/src/themes.rs | 17 ++++++++++------- crates/ui/src/popup.rs | 24 ++++++++++++++++++++++++ crates/ui/src/tests.rs | 1 + 9 files changed, 55 insertions(+), 16 deletions(-) diff --git a/crates/snowbound/src/art.rs b/crates/snowbound/src/art.rs index 68988569e7a511920eb7a1a344236255dcdb2063..871e3baa0d7638bff072976e03731c2c29026eea 100644 --- a/crates/snowbound/src/art.rs +++ b/crates/snowbound/src/art.rs @@ -110,7 +110,6 @@ pub const CLOSE_NOTEBOOK: &[&str] = art!( pub const CODE: &[&str] = art!("icons/code"); pub const COMMAND_PALETTE: &[&str] = art!("icons/palette", "icons/badge-command"); pub const COPY_LINK: &[&str] = art!("icons/page", "icons/badge-link"); -pub const CUSTOMIZE_THEME: &[&str] = art!("icons/theme", "icons/badge-edit"); pub const CUSTOMIZE_TAGS: &[&str] = art!("icons/tag", "icons/badge-edit"); pub const DELETE: &[&str] = art!("icons/delete"); pub 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"); pub const STOP: &[&str] = art!("icons/stop"); pub const SUBPAGE: &[&str] = art!("icons/subpage"); pub const SYNC_NOW: &[&str] = art!("icons/sync-now"); -pub const THEME: &[&str] = art!("icons/theme"); pub const TO_DO: &[&str] = art!("icons/to-do"); pub const UPDATE: &[&str] = art!("icons/update"); pub const ZOOM_ACTUAL: &[&str] = art!("icons/zoom-actual"); diff --git a/crates/snowbound/src/background.rs b/crates/snowbound/src/background.rs index 5c117d2aea14d2a748d6bfb6c23282d707496472..4285d9bba55543073e7bc118acdae9e9a2066003 100644 --- a/crates/snowbound/src/background.rs +++ b/crates/snowbound/src/background.rs @@ -89,18 +89,21 @@ pub fn menu( let groups = [ ui::popup::Group { heading: "Page Color", + ruled: false, cells: 2 + PAGE_COLORS.len(), columns: COLUMNS, size: SWATCH, }, ui::popup::Group { heading: "Background", + ruled: false, cells: 2 + ART.len(), columns: 3, size: [TILE[0] + 8.0, TILE[1] + 8.0], }, ui::popup::Group { heading: "Rule Lines", + ruled: false, cells: 1 + RULE_LINES.len(), columns: COLUMNS, size: [RULES; 2], diff --git a/crates/snowbound/src/commands.rs b/crates/snowbound/src/commands.rs index 4efcb12cf2acbc6b160c06930414ff5ccbed9011..fc62ff27f030456b6b3afdd61db36866bcee7e71 100644 --- a/crates/snowbound/src/commands.rs +++ b/crates/snowbound/src/commands.rs @@ -596,7 +596,7 @@ pub const COMMANDS: &[Command] = &[ row!(Id::Style(8), "Quote", NONE, NONE), row!(Id::Style(9), "Code", NONE, NONE), row!(Id::Style(10), "Normal", NONE, NONE), - row!(Id::Themes, "Customize Theme", NONE, NONE), + row!(Id::Themes, "Customize Font Styles", NONE, NONE), row!(Id::CustomizeTags, "Customize Tags", NONE, NONE), row!( Id::RemoveTags, diff --git a/crates/snowbound/src/main.rs b/crates/snowbound/src/main.rs index 5a26893f1f838b6e0a7146f187cec972013cece1..34ad2534187cc2d2c6dfec016661dc09a3d4750a 100644 --- a/crates/snowbound/src/main.rs +++ b/crates/snowbound/src/main.rs @@ -5184,6 +5184,7 @@ fn list_gallery( let groups = [(headings[0], recent.len()), (headings[1], count + 1)].map(|(heading, cells)| { ui::popup::Group { heading, + ruled: false, cells, columns: 5, size, @@ -5228,6 +5229,7 @@ fn pen_gallery( const CELL: [f32; 2] = [40.0, 24.0]; let groups = [ui::popup::Group { heading: "Pens", + ruled: false, cells: pens.len(), columns: 5, size: CELL, @@ -5666,7 +5668,7 @@ fn artwork(id: commands::Id) -> Option<&'static [&'static str]> { Cmd::Style(8) => art::QUOTE, Cmd::Style(9) => art::CODE, Cmd::Style(_) => art::NORMAL, - Cmd::Themes => art::CUSTOMIZE_THEME, + Cmd::Themes => art::STYLES, Cmd::OpenShared => art::OPEN_SHARED, Cmd::LiveShare => art::LIVE_SHARE, Cmd::MarkdownShortcuts => art::MARKDOWN, diff --git a/crates/snowbound/src/menus.rs b/crates/snowbound/src/menus.rs index 04d0cc247e1110a9c8bbbc8b1a3abea2ef5913f5..c1670c2cbae061f65fbc443775ad098df7483f65 100644 --- a/crates/snowbound/src/menus.rs +++ b/crates/snowbound/src/menus.rs @@ -151,7 +151,7 @@ impl Action { Action::NewGroup => art::NEW_SECTION_GROUP, Action::Reveal => art::FOLDER, Action::Close => art::CLOSE_NOTEBOOK, - Action::Theme => art::THEME, + Action::Theme => art::STYLES, Action::Colors => art::SECTION_COLOR, Action::Sync => art::SYNC_NOW, Action::NewNotebook => art::NEW_NOTEBOOK, @@ -334,6 +334,11 @@ impl State { (action, item) }; let nowhere = || self.destinations(target).is_empty(); + // Last on a page, section or notebook, under a rule. + let styles = || { + let title = commands::command(commands::Id::Themes).title; + item(Action::Theme, title, false, true) + }; match target { // OneNote's Move or Copy takes a page out of the bin; nothing else changes it. Target::Page { path, .. } if crate::recycle::binned(path) => vec![ @@ -360,11 +365,11 @@ impl State { item(Action::NewPage, "New Page", false, true), // OneNote offers this beside New Page, which is a plain + here. item(Action::NewSubpage, "New Subpage", false, false), - item(Action::Theme, "Theme", false, true), ]; // Levels and versions are known once the section is open. let Some(session) = self.session.as_ref().filter(|_| self.open(library, path)) else { + actions.push(styles()); return actions; }; let pages = &session.pages; @@ -393,6 +398,7 @@ impl State { session.page_versions(*space).is_empty(), true, ), + styles(), ]); actions } @@ -423,7 +429,7 @@ impl State { section_file(library, path).is_none(), false, ), - item(Action::Theme, "Theme", false, false), + styles(), ]); } actions @@ -493,12 +499,11 @@ impl State { library.folder().is_none(), true, ), - item(Action::Theme, "Theme", false, false), item( Action::RecycleBin, "Notebook Recycle Bin", library.catalog().is_none(), - true, + false, ), item( Action::Properties, @@ -506,6 +511,7 @@ impl State { library.catalog().is_none(), false, ), + styles(), ] } Target::Closed(_) | Target::Server(_) => { diff --git a/crates/snowbound/src/symbol.rs b/crates/snowbound/src/symbol.rs index a6181a3779f81a0be331a0d7ec59e16f7ae94441..279191c22e4acbba781933628f8fcdc4dca17122 100644 --- a/crates/snowbound/src/symbol.rs +++ b/crates/snowbound/src/symbol.rs @@ -82,12 +82,14 @@ pub fn gallery(ui: &mut Ui, id: Id, anchor: Anchor, symbols: &[char]) -> Option< let groups = [ ui::popup::Group { heading: "", + ruled: false, cells: symbols.len(), columns: 5, size: [SIZE; 2], }, ui::popup::Group { heading: "", + ruled: true, cells: 1, columns: 1, size: [5.0 * SIZE, 28.0], diff --git a/crates/snowbound/src/themes.rs b/crates/snowbound/src/themes.rs index 8d4c9dec6c6eb7f8cda74dd976d32dfab5194691..349dc7d497ef6ea2cbf3e9d9cd723fe0516ef0c1 100644 --- a/crates/snowbound/src/themes.rs +++ b/crates/snowbound/src/themes.rs @@ -1,8 +1,8 @@ //! Style themes in the app (`resources/styles.md`): the page wears its own theme, else its //! section's, else its notebook's, as its paragraph style objects; the Styles gallery -//! applies a style in the page's theme, and its Customize… opens the Themes dialog, which -//! edits a notebook's own themes with a live preview and gives one to the page, the section -//! or the notebook. +//! applies a style in the page's theme, and its Customize Font Styles opens the dialog of +//! that name, which edits a notebook's own themes with a live preview and gives one to the +//! page, the section or the notebook. use crate::{Library, State, filetime}; use accesskit::Role; @@ -255,12 +255,13 @@ impl State { ..Spec::default() }, ); + let named = crate::commands::command(crate::commands::Id::Themes).title; if let Some(node) = ui.access(id()) { - node.set_label("Themes"); + node.set_label(named); } let title = match &dialog.targets[..] { - [(scope, _)] => format!("Theme for {}", scope.name()), - _ => "Themes".to_owned(), + [(scope, _)] => format!("{named} for {}", scope.name()), + _ => named.to_owned(), }; ui.leaf( "title", @@ -770,12 +771,14 @@ pub(crate) fn gallery( .enumerate() .map(|(at, (name, _))| ui::popup::Group { heading: if at == 0 { &sheet.name } else { "" }, + ruled: false, cells: 1, columns: 1, size: row(shown_size(&sheet.styles[*name], GALLERY_LARGEST) * 1.25), }) .chain([ui::popup::Group { heading: "", + ruled: true, cells: 1, columns: 1, size: row(16.0), @@ -807,7 +810,7 @@ pub(crate) fn gallery( "customize", Spec { size: [fill(), fill()], - text: Some("Customize"), + text: Some(crate::commands::command(Cmd::Themes).title), ..Spec::default() }, ); diff --git a/crates/ui/src/popup.rs b/crates/ui/src/popup.rs index 54411e33d5c3bf75bbd766782755f2664bb91cf1..9877df46429111dc25210cb1eaacf449c6c10fd6 100644 --- a/crates/ui/src/popup.rs +++ b/crates/ui/src/popup.rs @@ -1038,6 +1038,8 @@ pub fn table_picker(ui: &mut Ui, id: Id, anchor: Anchor, size: [usize; 2]) -> Op #[derive(Clone, Copy, Debug)] pub struct Group<'a> { pub heading: &'a str, + /// Starts under a rule, as a gallery's command after its choices. + pub ruled: bool, pub cells: usize, pub columns: usize, pub size: [f32; 2], @@ -1096,7 +1098,29 @@ pub fn gallery( width + 2.0 * ui.theme.menu().pad, ); let mut index = 0; + let style = theme.menu(); for (number, group) in groups.iter().enumerate() { + if group.ruled { + ui.open( + ("rule", number), + Spec { + size: [fill(), px(style.rule_band)], + ..Spec::default() + }, + ); + ui.leaf( + "line", + Spec { + flags: Flags::FLOAT, + size: [fill(), px(1.0)], + position: [0.0, (style.rule_band - 1.0) / 2.0], + inset: [style.rule_inset, 0.0, style.rule_inset, 0.0], + fill: Some(style.rule), + ..Spec::default() + }, + ); + ui.close(); + } if !group.heading.is_empty() { ui.leaf( ("heading", number), diff --git a/crates/ui/src/tests.rs b/crates/ui/src/tests.rs index cf47717c99d6d3d91f4bddaca44d2cec11dd3f93..dc04cd03df62ac4d95c447a3ef6de1f9a09ef805 100644 --- a/crates/ui/src/tests.rs +++ b/crates/ui/src/tests.rs @@ -2911,6 +2911,7 @@ fn galleries_choose_across_their_groups_by_keys_and_clicks() { let gallery = Id::ROOT.child("gallery"); let groups = [("Recent", 2), ("Library", 5)].map(|(heading, cells)| popup::Group { heading, + ruled: false, cells, columns: 3, size: [30.0, 30.0], -- 2.54.0