| author | |
| committer | |
| log | 0fe456a8909d9b9991afbe28c28f915d031ca44f |
| tree | 2a4f6a8e8278499eb545761682cebed6895d558b |
| parent | a4a0ccf055b14aa599b027e96a679054fea6b5a2 |
| signature | Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU |
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.59 files changed, 55 insertions(+), 16 deletions(-)
crates/snowbound/src/art.rs-2| ... | @@ -110,7 +110,6 @@ pub const CLOSE_NOTEBOOK: &[&str] = art!( | ... | @@ -110,7 +110,6 @@ pub const CLOSE_NOTEBOOK: &[&str] = art!( |
| 110 | pub const CODE: &[&str] = art!("icons/code"); | 110 | pub const CODE: &[&str] = art!("icons/code"); |
| 111 | pub const COMMAND_PALETTE: &[&str] = art!("icons/palette", "icons/badge-command"); | 111 | pub const COMMAND_PALETTE: &[&str] = art!("icons/palette", "icons/badge-command"); |
| 112 | pub const COPY_LINK: &[&str] = art!("icons/page", "icons/badge-link"); | 112 | pub const COPY_LINK: &[&str] = art!("icons/page", "icons/badge-link"); |
| 113 | pub const CUSTOMIZE_THEME: &[&str] = art!("icons/theme", "icons/badge-edit"); | ||
| 114 | pub const CUSTOMIZE_TAGS: &[&str] = art!("icons/tag", "icons/badge-edit"); | 113 | pub const CUSTOMIZE_TAGS: &[&str] = art!("icons/tag", "icons/badge-edit"); |
| 115 | pub const DELETE: &[&str] = art!("icons/delete"); | 114 | pub const DELETE: &[&str] = art!("icons/delete"); |
| 116 | pub const EMPTY_RECYCLE_BIN: &[&str] = art!("icons/recycle-bin", "icons/badge-remove"); | 115 | 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"); | ... | @@ -195,7 +194,6 @@ pub const SNAP_TO_GRID: &[&str] = art!("icons/snap-to-grid"); |
| 195 | pub const STOP: &[&str] = art!("icons/stop"); | 194 | pub const STOP: &[&str] = art!("icons/stop"); |
| 196 | pub const SUBPAGE: &[&str] = art!("icons/subpage"); | 195 | pub const SUBPAGE: &[&str] = art!("icons/subpage"); |
| 197 | pub const SYNC_NOW: &[&str] = art!("icons/sync-now"); | 196 | pub const SYNC_NOW: &[&str] = art!("icons/sync-now"); |
| 198 | pub const THEME: &[&str] = art!("icons/theme"); | ||
| 199 | pub const TO_DO: &[&str] = art!("icons/to-do"); | 197 | pub const TO_DO: &[&str] = art!("icons/to-do"); |
| 200 | pub const UPDATE: &[&str] = art!("icons/update"); | 198 | pub const UPDATE: &[&str] = art!("icons/update"); |
| 201 | pub const ZOOM_ACTUAL: &[&str] = art!("icons/zoom-actual"); | 199 | pub const ZOOM_ACTUAL: &[&str] = art!("icons/zoom-actual"); |
crates/snowbound/src/background.rs+3| ... | @@ -89,18 +89,21 @@ pub fn menu( | ... | @@ -89,18 +89,21 @@ pub fn menu( |
| 89 | let groups = [ | 89 | let groups = [ |
| 90 | ui::popup::Group { | 90 | ui::popup::Group { |
| 91 | heading: "Page Color", | 91 | heading: "Page Color", |
| 92 | ruled: false, | ||
| 92 | cells: 2 + PAGE_COLORS.len(), | 93 | cells: 2 + PAGE_COLORS.len(), |
| 93 | columns: COLUMNS, | 94 | columns: COLUMNS, |
| 94 | size: SWATCH, | 95 | size: SWATCH, |
| 95 | }, | 96 | }, |
| 96 | ui::popup::Group { | 97 | ui::popup::Group { |
| 97 | heading: "Background", | 98 | heading: "Background", |
| 99 | ruled: false, | ||
| 98 | cells: 2 + ART.len(), | 100 | cells: 2 + ART.len(), |
| 99 | columns: 3, | 101 | columns: 3, |
| 100 | size: [TILE[0] + 8.0, TILE[1] + 8.0], | 102 | size: [TILE[0] + 8.0, TILE[1] + 8.0], |
| 101 | }, | 103 | }, |
| 102 | ui::popup::Group { | 104 | ui::popup::Group { |
| 103 | heading: "Rule Lines", | 105 | heading: "Rule Lines", |
| 106 | ruled: false, | ||
| 104 | cells: 1 + RULE_LINES.len(), | 107 | cells: 1 + RULE_LINES.len(), |
| 105 | columns: COLUMNS, | 108 | columns: COLUMNS, |
| 106 | size: [RULES; 2], | 109 | size: [RULES; 2], |
crates/snowbound/src/commands.rs+1-1| ... | @@ -596,7 +596,7 @@ pub const COMMANDS: &[Command] = &[ | ... | @@ -596,7 +596,7 @@ pub const COMMANDS: &[Command] = &[ |
| 596 | row!(Id::Style(8), "Quote", NONE, NONE), | 596 | row!(Id::Style(8), "Quote", NONE, NONE), |
| 597 | row!(Id::Style(9), "Code", NONE, NONE), | 597 | row!(Id::Style(9), "Code", NONE, NONE), |
| 598 | row!(Id::Style(10), "Normal", NONE, NONE), | 598 | row!(Id::Style(10), "Normal", NONE, NONE), |
| 599 | row!(Id::Themes, "Customize Theme", NONE, NONE), | 599 | row!(Id::Themes, "Customize Font Styles", NONE, NONE), |
| 600 | row!(Id::CustomizeTags, "Customize Tags", NONE, NONE), | 600 | row!(Id::CustomizeTags, "Customize Tags", NONE, NONE), |
| 601 | row!( | 601 | row!( |
| 602 | Id::RemoveTags, | 602 | Id::RemoveTags, |
crates/snowbound/src/main.rs+3-1| ... | @@ -5184,6 +5184,7 @@ fn list_gallery( | ... | @@ -5184,6 +5184,7 @@ fn list_gallery( |
| 5184 | let groups = [(headings[0], recent.len()), (headings[1], count + 1)].map(|(heading, cells)| { | 5184 | let groups = [(headings[0], recent.len()), (headings[1], count + 1)].map(|(heading, cells)| { |
| 5185 | ui::popup::Group { | 5185 | ui::popup::Group { |
| 5186 | heading, | 5186 | heading, |
| 5187 | ruled: false, | ||
| 5187 | cells, | 5188 | cells, |
| 5188 | columns: 5, | 5189 | columns: 5, |
| 5189 | size, | 5190 | size, |
| ... | @@ -5228,6 +5229,7 @@ fn pen_gallery( | ... | @@ -5228,6 +5229,7 @@ fn pen_gallery( |
| 5228 | const CELL: [f32; 2] = [40.0, 24.0]; | 5229 | const CELL: [f32; 2] = [40.0, 24.0]; |
| 5229 | let groups = [ui::popup::Group { | 5230 | let groups = [ui::popup::Group { |
| 5230 | heading: "Pens", | 5231 | heading: "Pens", |
| 5232 | ruled: false, | ||
| 5231 | cells: pens.len(), | 5233 | cells: pens.len(), |
| 5232 | columns: 5, | 5234 | columns: 5, |
| 5233 | size: CELL, | 5235 | size: CELL, |
| ... | @@ -5666,7 +5668,7 @@ fn artwork(id: commands::Id) -> Option<&'static [&'static str]> { | ... | @@ -5666,7 +5668,7 @@ fn artwork(id: commands::Id) -> Option<&'static [&'static str]> { |
| 5666 | Cmd::Style(8) => art::QUOTE, | 5668 | Cmd::Style(8) => art::QUOTE, |
| 5667 | Cmd::Style(9) => art::CODE, | 5669 | Cmd::Style(9) => art::CODE, |
| 5668 | Cmd::Style(_) => art::NORMAL, | 5670 | Cmd::Style(_) => art::NORMAL, |
| 5669 | Cmd::Themes => art::CUSTOMIZE_THEME, | 5671 | Cmd::Themes => art::STYLES, |
| 5670 | Cmd::OpenShared => art::OPEN_SHARED, | 5672 | Cmd::OpenShared => art::OPEN_SHARED, |
| 5671 | Cmd::LiveShare => art::LIVE_SHARE, | 5673 | Cmd::LiveShare => art::LIVE_SHARE, |
| 5672 | Cmd::MarkdownShortcuts => art::MARKDOWN, | 5674 | Cmd::MarkdownShortcuts => art::MARKDOWN, |
crates/snowbound/src/menus.rs+11-5| ... | @@ -151,7 +151,7 @@ impl Action { | ... | @@ -151,7 +151,7 @@ impl Action { |
| 151 | Action::NewGroup => art::NEW_SECTION_GROUP, | 151 | Action::NewGroup => art::NEW_SECTION_GROUP, |
| 152 | Action::Reveal => art::FOLDER, | 152 | Action::Reveal => art::FOLDER, |
| 153 | Action::Close => art::CLOSE_NOTEBOOK, | 153 | Action::Close => art::CLOSE_NOTEBOOK, |
| 154 | Action::Theme => art::THEME, | 154 | Action::Theme => art::STYLES, |
| 155 | Action::Colors => art::SECTION_COLOR, | 155 | Action::Colors => art::SECTION_COLOR, |
| 156 | Action::Sync => art::SYNC_NOW, | 156 | Action::Sync => art::SYNC_NOW, |
| 157 | Action::NewNotebook => art::NEW_NOTEBOOK, | 157 | Action::NewNotebook => art::NEW_NOTEBOOK, |
| ... | @@ -334,6 +334,11 @@ impl State { | ... | @@ -334,6 +334,11 @@ impl State { |
| 334 | (action, item) | 334 | (action, item) |
| 335 | }; | 335 | }; |
| 336 | let nowhere = || self.destinations(target).is_empty(); | 336 | 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 | }; | ||
| 337 | match target { | 342 | match target { |
| 338 | // OneNote's Move or Copy takes a page out of the bin; nothing else changes it. | 343 | // OneNote's Move or Copy takes a page out of the bin; nothing else changes it. |
| 339 | Target::Page { path, .. } if crate::recycle::binned(path) => vec![ | 344 | Target::Page { path, .. } if crate::recycle::binned(path) => vec![ |
| ... | @@ -360,11 +365,11 @@ impl State { | ... | @@ -360,11 +365,11 @@ impl State { |
| 360 | item(Action::NewPage, "New Page", false, true), | 365 | item(Action::NewPage, "New Page", false, true), |
| 361 | // OneNote offers this beside New Page, which is a plain + here. | 366 | // OneNote offers this beside New Page, which is a plain + here. |
| 362 | item(Action::NewSubpage, "New Subpage", false, false), | 367 | item(Action::NewSubpage, "New Subpage", false, false), |
| 363 | item(Action::Theme, "Theme", false, true), | ||
| 364 | ]; | 368 | ]; |
| 365 | // Levels and versions are known once the section is open. | 369 | // Levels and versions are known once the section is open. |
| 366 | let Some(session) = self.session.as_ref().filter(|_| self.open(library, path)) | 370 | let Some(session) = self.session.as_ref().filter(|_| self.open(library, path)) |
| 367 | else { | 371 | else { |
| 372 | actions.push(styles()); | ||
| 368 | return actions; | 373 | return actions; |
| 369 | }; | 374 | }; |
| 370 | let pages = &session.pages; | 375 | let pages = &session.pages; |
| ... | @@ -393,6 +398,7 @@ impl State { | ... | @@ -393,6 +398,7 @@ impl State { |
| 393 | session.page_versions(*space).is_empty(), | 398 | session.page_versions(*space).is_empty(), |
| 394 | true, | 399 | true, |
| 395 | ), | 400 | ), |
| 401 | styles(), | ||
| 396 | ]); | 402 | ]); |
| 397 | actions | 403 | actions |
| 398 | } | 404 | } |
| ... | @@ -423,7 +429,7 @@ impl State { | ... | @@ -423,7 +429,7 @@ impl State { |
| 423 | section_file(library, path).is_none(), | 429 | section_file(library, path).is_none(), |
| 424 | false, | 430 | false, |
| 425 | ), | 431 | ), |
| 426 | item(Action::Theme, "Theme", false, false), | 432 | styles(), |
| 427 | ]); | 433 | ]); |
| 428 | } | 434 | } |
| 429 | actions | 435 | actions |
| ... | @@ -493,12 +499,11 @@ impl State { | ... | @@ -493,12 +499,11 @@ impl State { |
| 493 | library.folder().is_none(), | 499 | library.folder().is_none(), |
| 494 | true, | 500 | true, |
| 495 | ), | 501 | ), |
| 496 | item(Action::Theme, "Theme", false, false), | ||
| 497 | item( | 502 | item( |
| 498 | Action::RecycleBin, | 503 | Action::RecycleBin, |
| 499 | "Notebook Recycle Bin", | 504 | "Notebook Recycle Bin", |
| 500 | library.catalog().is_none(), | 505 | library.catalog().is_none(), |
| 501 | true, | 506 | false, |
| 502 | ), | 507 | ), |
| 503 | item( | 508 | item( |
| 504 | Action::Properties, | 509 | Action::Properties, |
| ... | @@ -506,6 +511,7 @@ impl State { | ... | @@ -506,6 +511,7 @@ impl State { |
| 506 | library.catalog().is_none(), | 511 | library.catalog().is_none(), |
| 507 | false, | 512 | false, |
| 508 | ), | 513 | ), |
| 514 | styles(), | ||
| 509 | ] | 515 | ] |
| 510 | } | 516 | } |
| 511 | Target::Closed(_) | Target::Server(_) => { | 517 | 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< | ... | @@ -82,12 +82,14 @@ pub fn gallery(ui: &mut Ui, id: Id, anchor: Anchor, symbols: &[char]) -> Option< |
| 82 | let groups = [ | 82 | let groups = [ |
| 83 | ui::popup::Group { | 83 | ui::popup::Group { |
| 84 | heading: "", | 84 | heading: "", |
| 85 | ruled: false, | ||
| 85 | cells: symbols.len(), | 86 | cells: symbols.len(), |
| 86 | columns: 5, | 87 | columns: 5, |
| 87 | size: [SIZE; 2], | 88 | size: [SIZE; 2], |
| 88 | }, | 89 | }, |
| 89 | ui::popup::Group { | 90 | ui::popup::Group { |
| 90 | heading: "", | 91 | heading: "", |
| 92 | ruled: true, | ||
| 91 | cells: 1, | 93 | cells: 1, |
| 92 | columns: 1, | 94 | columns: 1, |
| 93 | size: [5.0 * SIZE, 28.0], | 95 | size: [5.0 * SIZE, 28.0], |
crates/snowbound/src/themes.rs+10-7| ... | @@ -1,8 +1,8 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | //! Style themes in the app (`resources/styles.md`): the page wears its own theme, else its | 1 | //! Style themes in the app (`resources/styles.md`): the page wears its own theme, else its |
| 2 | //! section's, else its notebook's, as its paragraph style objects; the Styles gallery | 2 | //! 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 | 3 | //! applies a style in the page's theme, and its Customize Font Styles opens the dialog of |
| 4 | //! edits a notebook's own themes with a live preview and gives one to the page, the section | 4 | //! that name, which edits a notebook's own themes with a live preview and gives one to the |
| 5 | //! or the notebook. | 5 | //! page, the section or the notebook. |
| 6 | 6 | ||
| 7 | use crate::{Library, State, filetime}; | 7 | use crate::{Library, State, filetime}; |
| 8 | use accesskit::Role; | 8 | use accesskit::Role; |
| ... | @@ -255,12 +255,13 @@ impl State { | ... | @@ -255,12 +255,13 @@ impl State { |
| 255 | ..Spec::default() | 255 | ..Spec::default() |
| 256 | }, | 256 | }, |
| 257 | ); | 257 | ); |
| 258 | let named = crate::commands::command(crate::commands::Id::Themes).title; | ||
| 258 | if let Some(node) = ui.access(id()) { | 259 | if let Some(node) = ui.access(id()) { |
| 259 | node.set_label("Themes"); | 260 | node.set_label(named); |
| 260 | } | 261 | } |
| 261 | let title = match &dialog.targets[..] { | 262 | let title = match &dialog.targets[..] { |
| 262 | [(scope, _)] => format!("Theme for {}", scope.name()), | 263 | [(scope, _)] => format!("{named} for {}", scope.name()), |
| 263 | _ => "Themes".to_owned(), | 264 | _ => named.to_owned(), |
| 264 | }; | 265 | }; |
| 265 | ui.leaf( | 266 | ui.leaf( |
| 266 | "title", | 267 | "title", |
| ... | @@ -770,12 +771,14 @@ pub(crate) fn gallery( | ... | @@ -770,12 +771,14 @@ pub(crate) fn gallery( |
| 770 | .enumerate() | 771 | .enumerate() |
| 771 | .map(|(at, (name, _))| ui::popup::Group { | 772 | .map(|(at, (name, _))| ui::popup::Group { |
| 772 | heading: if at == 0 { &sheet.name } else { "" }, | 773 | heading: if at == 0 { &sheet.name } else { "" }, |
| 774 | ruled: false, | ||
| 773 | cells: 1, | 775 | cells: 1, |
| 774 | columns: 1, | 776 | columns: 1, |
| 775 | size: row(shown_size(&sheet.styles[*name], GALLERY_LARGEST) * 1.25), | 777 | size: row(shown_size(&sheet.styles[*name], GALLERY_LARGEST) * 1.25), |
| 776 | }) | 778 | }) |
| 777 | .chain([ui::popup::Group { | 779 | .chain([ui::popup::Group { |
| 778 | heading: "", | 780 | heading: "", |
| 781 | ruled: true, | ||
| 779 | cells: 1, | 782 | cells: 1, |
| 780 | columns: 1, | 783 | columns: 1, |
| 781 | size: row(16.0), | 784 | size: row(16.0), |
| ... | @@ -807,7 +810,7 @@ pub(crate) fn gallery( | ... | @@ -807,7 +810,7 @@ pub(crate) fn gallery( |
| 807 | "customize", | 810 | "customize", |
| 808 | Spec { | 811 | Spec { |
| 809 | size: [fill(), fill()], | 812 | size: [fill(), fill()], |
| 810 | text: Some("Customize"), | 813 | text: Some(crate::commands::command(Cmd::Themes).title), |
| 811 | ..Spec::default() | 814 | ..Spec::default() |
| 812 | }, | 815 | }, |
| 813 | ); | 816 | ); |
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 | ... | @@ -1038,6 +1038,8 @@ pub fn table_picker(ui: &mut Ui, id: Id, anchor: Anchor, size: [usize; 2]) -> Op |
| 1038 | #[derive(Clone, Copy, Debug)] | 1038 | #[derive(Clone, Copy, Debug)] |
| 1039 | pub struct Group<'a> { | 1039 | pub struct Group<'a> { |
| 1040 | pub heading: &'a str, | 1040 | pub heading: &'a str, |
| 1041 | /// Starts under a rule, as a gallery's command after its choices. | ||
| 1042 | pub ruled: bool, | ||
| 1041 | pub cells: usize, | 1043 | pub cells: usize, |
| 1042 | pub columns: usize, | 1044 | pub columns: usize, |
| 1043 | pub size: [f32; 2], | 1045 | pub size: [f32; 2], |
| ... | @@ -1096,7 +1098,29 @@ pub fn gallery( | ... | @@ -1096,7 +1098,29 @@ pub fn gallery( |
| 1096 | width + 2.0 * ui.theme.menu().pad, | 1098 | width + 2.0 * ui.theme.menu().pad, |
| 1097 | ); | 1099 | ); |
| 1098 | let mut index = 0; | 1100 | let mut index = 0; |
| 1101 | let style = theme.menu(); | ||
| 1099 | for (number, group) in groups.iter().enumerate() { | 1102 | 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 | } | ||
| 1100 | if !group.heading.is_empty() { | 1124 | if !group.heading.is_empty() { |
| 1101 | ui.leaf( | 1125 | ui.leaf( |
| 1102 | ("heading", number), | 1126 | ("heading", number), |
crates/ui/src/tests.rs+1| ... | @@ -2911,6 +2911,7 @@ fn galleries_choose_across_their_groups_by_keys_and_clicks() { | ... | @@ -2911,6 +2911,7 @@ fn galleries_choose_across_their_groups_by_keys_and_clicks() { |
| 2911 | let gallery = Id::ROOT.child("gallery"); | 2911 | let gallery = Id::ROOT.child("gallery"); |
| 2912 | let groups = [("Recent", 2), ("Library", 5)].map(|(heading, cells)| popup::Group { | 2912 | let groups = [("Recent", 2), ("Library", 5)].map(|(heading, cells)| popup::Group { |
| 2913 | heading, | 2913 | heading, |
| 2914 | ruled: false, | ||
| 2914 | cells, | 2915 | cells, |
| 2915 | columns: 3, | 2916 | columns: 3, |
| 2916 | size: [30.0, 30.0], | 2917 | size: [30.0, 30.0], |