Merge PR #549
This commit is contained in:
commit
e87863d48d
|
|
@ -1670,7 +1670,8 @@ mod tests {
|
|||
let parent_index = dir.to_string_lossy().split(['/', '\\']).filter(|p| !p.is_empty()).count() - 1;
|
||||
assert_eq!(h.request("ui.clickWidget", json!({"id": format!("crumb:{parent_index}")}), t)["ok"], true);
|
||||
h.settle(SETTLE);
|
||||
assert_eq!(h.app.session.browse.as_ref().unwrap().path, dir.to_string_lossy().replace('\\', "/"));
|
||||
// the same folder, whatever the platform's separators (issue #538)
|
||||
assert_eq!(std::path::Path::new(&h.app.session.browse.as_ref().unwrap().path), dir.as_path());
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -717,10 +717,33 @@ fn folder_header(app: &mut LightcraftApp, ui: &mut egui::Ui, b: &lightcraft_engi
|
|||
header.response.rect
|
||||
}
|
||||
|
||||
/// The folders a breadcrumb shows for `path`: each name with the path that opens it, spelled as
|
||||
/// in `path` (its separators, a leading `/` or a UNC `\\server\share`; issue #538). A bare drive
|
||||
/// (`C:`) gets its separator back: on its own it means the current folder on that drive.
|
||||
pub(crate) fn crumbs(path: &str) -> Vec<(&str, String)> {
|
||||
let mut out = Vec::new();
|
||||
let mut start = 0;
|
||||
for (i, c) in path.char_indices().chain(std::iter::once((path.len(), '/'))) {
|
||||
if c != '/' && c != '\\' {
|
||||
continue;
|
||||
}
|
||||
if let Some(part) = path.get(start..i).filter(|p| !p.is_empty()) {
|
||||
let mut to = path.get(..i).unwrap_or(path).to_string();
|
||||
if out.is_empty() && part.ends_with(':') && to == part {
|
||||
to.push(if path.contains('\\') { '\\' } else { '/' });
|
||||
}
|
||||
out.push((part, to));
|
||||
}
|
||||
start = i + c.len_utf8();
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn folder_breadcrumbs(app: &mut LightcraftApp, ui: &mut egui::Ui, path: &str) {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
ui.spacing_mut().item_spacing.x = 4.0;
|
||||
let parts: Vec<&str> = path.split(['/', '\\']).filter(|p| !p.is_empty()).collect();
|
||||
let crumbs = crumbs(path);
|
||||
let parts: Vec<&str> = crumbs.iter().map(|(name, _)| *name).collect();
|
||||
// With all sidebars open even four ellipses and their separators can exceed the row.
|
||||
// Prefer the current folder, showing more ancestors as space permits.
|
||||
let visible = ((ui.available_width() / 80.0) as usize).clamp(1, 4);
|
||||
|
|
@ -741,9 +764,10 @@ fn folder_breadcrumbs(app: &mut LightcraftApp, ui: &mut egui::Ui, path: &str) {
|
|||
if r.hovered() {
|
||||
ui.ctx().set_cursor_icon(egui::CursorIcon::PointingHand);
|
||||
}
|
||||
if r.clicked() {
|
||||
let prefix = if path.starts_with('/') { format!("/{}", parts[..=i].join("/")) } else { parts[..=i].join("/") };
|
||||
let _ = app.run("library.browse", json!({"path": prefix}));
|
||||
if r.clicked()
|
||||
&& let Some((_, to)) = crumbs.get(i)
|
||||
{
|
||||
let _ = app.run("library.browse", json!({"path": to}));
|
||||
}
|
||||
ui.label(egui::RichText::new("›").size(13.0).color(t.text_dim));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,21 @@ use egui::{Rect, vec2};
|
|||
|
||||
use crate::{LightcraftApp, Services, headless::HeadlessView, i18n::Locale};
|
||||
|
||||
/// Issue #538: a breadcrumb opens its folder spelled as the path is, keeping a UNC share's leading
|
||||
/// `\\` and giving a bare drive its root back.
|
||||
#[test]
|
||||
fn breadcrumbs_open_their_folder_as_spelled() {
|
||||
let to = |path: &str| super::crumbs(path).into_iter().map(|(name, to)| (name.to_string(), to)).collect::<Vec<_>>();
|
||||
let pairs = |v: &[(&str, &str)]| v.iter().map(|(a, b)| (a.to_string(), b.to_string())).collect::<Vec<_>>();
|
||||
assert_eq!(to("/Users/me/Pictures"), pairs(&[("Users", "/Users"), ("me", "/Users/me"), ("Pictures", "/Users/me/Pictures")]));
|
||||
assert_eq!(to(r"C:\Users\me"), pairs(&[("C:", r"C:\"), ("Users", r"C:\Users"), ("me", r"C:\Users\me")]));
|
||||
assert_eq!(to("C:/Users"), pairs(&[("C:", "C:/"), ("Users", "C:/Users")]));
|
||||
assert_eq!(to(r"\\server\share\photos"), pairs(&[("server", r"\\server"), ("share", r"\\server\share"), ("photos", r"\\server\share\photos")]));
|
||||
assert_eq!(to("relative/dir/"), pairs(&[("relative", "relative"), ("dir", "relative/dir")]));
|
||||
assert_eq!(to("/Fotos/día 1/写真"), pairs(&[("Fotos", "/Fotos"), ("día 1", "/Fotos/día 1"), ("写真", "/Fotos/día 1/写真")]));
|
||||
assert!(to("").is_empty() && to("/").is_empty() && to(r"\\").is_empty());
|
||||
}
|
||||
|
||||
fn text_bounds(shape: &egui::Shape, out: &mut Vec<Rect>) {
|
||||
match shape {
|
||||
egui::Shape::Text(text) => out.push(text.visual_bounding_rect()),
|
||||
|
|
|
|||
Loading…
Reference in New Issue