diff --git a/crates/pdfs-gui/resources/style.css b/crates/pdfs-gui/resources/style.css index 957d114..e46a333 100644 --- a/crates/pdfs-gui/resources/style.css +++ b/crates/pdfs-gui/resources/style.css @@ -48,6 +48,9 @@ .badge-cloud { color: color-mix(in srgb, currentColor 55%, transparent); } .badge-issue { color: var(--error-color); } .browser-statusbar { background-color: alpha(currentColor, 0.025); } +/* The folder on screen, last in the breadcrumb trail: a flat button's side + * padding, so the "›" before it is spaced like the ones between buttons. */ +.crumb-current { padding: 0 10px; } scale.browser-status-meter trough { min-width: 104px; min-height: 6px; } .bulk-bar { padding: 6px 8px; border-radius: 12px; background: color-mix(in srgb, var(--accent-bg-color) 12%, transparent); } .dropzone { border: 2px dashed alpha(currentColor, 0.25); border-radius: 16px; padding: 28px 18px; transition: background 160ms ease, border-color 160ms ease; } diff --git a/crates/pdfs-gui/src/app/main.rs b/crates/pdfs-gui/src/app/main.rs index 9bf08a5..8feb958 100644 --- a/crates/pdfs-gui/src/app/main.rs +++ b/crates/pdfs-gui/src/app/main.rs @@ -34,6 +34,7 @@ use pages::verify::*; use reload::{LoadTicket, Loader, replace_items}; use theme::{load_resources, proton_theme_active, set_proton_theme}; use widgets::compat::*; +use widgets::crumb::*; use widgets::details::*; use widgets::file_list::*; use widgets::menu::*; diff --git a/crates/pdfs-gui/src/app/pages/browser.rs b/crates/pdfs-gui/src/app/pages/browser.rs index d9c4e41..c7d2521 100644 --- a/crates/pdfs-gui/src/app/pages/browser.rs +++ b/crates/pdfs-gui/src/app/pages/browser.rs @@ -1220,9 +1220,7 @@ pub(crate) fn repaint_crumb(ui: &Rc, path: &str) { )); let mut acc = String::new(); for (i, seg) in segments.iter().enumerate() { - let sep = gtk4::Label::new(Some("›")); - sep.add_css_class("dim-label"); - ui.browser.crumb.append(&sep); + ui.browser.crumb.append(&crumb_separator()); acc = if acc.is_empty() { seg.to_string() } else { @@ -1237,12 +1235,7 @@ pub(crate) fn repaint_crumb(ui: &Rc, path: &str) { /// flat button that navigates to `target` (clearing any active search first). pub(crate) fn crumb_node(ui: &Rc, label: &str, target: &str, current: bool) -> gtk4::Widget { if current { - let l = gtk4::Label::builder() - .label(label) - .ellipsize(gtk4::pango::EllipsizeMode::Start) - .build(); - l.add_css_class("heading"); - return l.upcast(); + return crumb_current(label); } let button = gtk4::Button::builder().label(label).build(); button.add_css_class("flat"); diff --git a/crates/pdfs-gui/src/app/pages/devices.rs b/crates/pdfs-gui/src/app/pages/devices.rs index 14e26d7..cdd28c7 100644 --- a/crates/pdfs-gui/src/app/pages/devices.rs +++ b/crates/pdfs-gui/src/app/pages/devices.rs @@ -487,9 +487,7 @@ fn repaint_device_crumb(ui: &Rc, nav: &[(String, String)]) { nav.is_empty(), )); for (i, (_, name)) in nav.iter().enumerate() { - let sep = gtk4::Label::new(Some("›")); - sep.add_css_class("dim-label"); - crumb.append(&sep); + crumb.append(&crumb_separator()); crumb.append(&device_crumb_node(ui, name, i + 1, i + 1 == nav.len())); } } @@ -498,12 +496,7 @@ fn repaint_device_crumb(ui: &Rc, nav: &[(String, String)]) { /// that goes back up to `depth` levels below the device list. fn device_crumb_node(ui: &Rc, label: &str, depth: usize, current: bool) -> gtk4::Widget { if current { - let l = gtk4::Label::builder() - .label(label) - .ellipsize(gtk4::pango::EllipsizeMode::Start) - .build(); - l.add_css_class("heading"); - return l.upcast(); + return crumb_current(label); } let button = gtk4::Button::builder().label(label).build(); button.add_css_class("flat"); diff --git a/crates/pdfs-gui/src/app/pages/shared.rs b/crates/pdfs-gui/src/app/pages/shared.rs index 197d96b..efc1352 100644 --- a/crates/pdfs-gui/src/app/pages/shared.rs +++ b/crates/pdfs-gui/src/app/pages/shared.rs @@ -484,9 +484,7 @@ fn repaint_shared_crumb(ui: &Rc, nav: &[(String, String)]) { nav.is_empty(), )); for (i, (_, name)) in nav.iter().enumerate() { - let sep = gtk4::Label::new(Some("›")); - sep.add_css_class("dim-label"); - crumb.append(&sep); + crumb.append(&crumb_separator()); crumb.append(&shared_crumb_node(ui, name, i + 1, i + 1 == nav.len())); } } @@ -495,12 +493,7 @@ fn repaint_shared_crumb(ui: &Rc, nav: &[(String, String)]) { /// that goes back up to `depth` folders below the top level. fn shared_crumb_node(ui: &Rc, label: &str, depth: usize, current: bool) -> gtk4::Widget { if current { - let l = gtk4::Label::builder() - .label(label) - .ellipsize(gtk4::pango::EllipsizeMode::Start) - .build(); - l.add_css_class("heading"); - return l.upcast(); + return crumb_current(label); } let button = gtk4::Button::builder().label(label).build(); button.add_css_class("flat"); diff --git a/crates/pdfs-gui/src/app/widgets/crumb.rs b/crates/pdfs-gui/src/app/widgets/crumb.rs new file mode 100644 index 0000000..f99fbdf --- /dev/null +++ b/crates/pdfs-gui/src/app/widgets/crumb.rs @@ -0,0 +1,21 @@ +use crate::*; + +/// The "›" between two breadcrumb segments. +pub(crate) fn crumb_separator() -> gtk4::Label { + let sep = gtk4::Label::new(Some("›")); + sep.add_css_class("dim-label"); + sep +} + +/// The last breadcrumb segment, the folder on screen: a heading rather than a +/// button. It carries a flat button's side padding (`.crumb-current`), so the +/// "›" before it sits as far from it as from the button before. +pub(crate) fn crumb_current(label: &str) -> gtk4::Widget { + let l = gtk4::Label::builder() + .label(label) + .ellipsize(gtk4::pango::EllipsizeMode::Start) + .build(); + l.add_css_class("heading"); + l.add_css_class("crumb-current"); + l.upcast() +} diff --git a/crates/pdfs-gui/src/app/widgets/file_list.rs b/crates/pdfs-gui/src/app/widgets/file_list.rs index 3007ec9..dac98cf 100644 --- a/crates/pdfs-gui/src/app/widgets/file_list.rs +++ b/crates/pdfs-gui/src/app/widgets/file_list.rs @@ -132,8 +132,13 @@ impl FileList { // and the grid collapses to a single column. Allowing a mid-word // break is what keeps the two-line-then-ellipsis budget below // enforceable for *every* name rather than only the ones that - // happen to have spaces. + // happen to have spaces. Pango marks such a break with a hyphen + // that is not in the name ("SteamSetup.e-xe"); a file name is + // not prose, so break it bare, as Nautilus does. + let no_hyphens = gtk4::pango::AttrList::new(); + no_hyphens.insert(gtk4::pango::AttrInt::new_insert_hyphens(false)); let label = gtk4::Label::builder() + .attributes(&no_hyphens) .ellipsize(gtk4::pango::EllipsizeMode::End) .justify(gtk4::Justification::Center) .max_width_chars(13) diff --git a/crates/pdfs-gui/src/app/widgets/mod.rs b/crates/pdfs-gui/src/app/widgets/mod.rs index 3e4d1a4..e0f5620 100644 --- a/crates/pdfs-gui/src/app/widgets/mod.rs +++ b/crates/pdfs-gui/src/app/widgets/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod compat; +pub(crate) mod crumb; pub(crate) mod details; pub(crate) mod file_list; pub(crate) mod menu;