mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-23 07:34:04 -04:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e31d855d57 | ||
|
|
04678ee240 | ||
|
|
97e6578de1 |
No files matched your search
@@ -1253,7 +1253,7 @@ fn tabs(app: &mut PhotocraftApp, ui: &mut egui::Ui) -> TabStrip {
|
||||
let cut = name_g.size().x + 0.5 < natural_w - STUDIO_TAB_PAD + STUDIO_TAB_GAP - meta_g.size().x;
|
||||
let resp = ui.interact(r, ui.id().with(("dtab", i)), Sense::click());
|
||||
resp.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::SelectableLabel, true, sel, &st.doc.name));
|
||||
doc_tabs.push(r);
|
||||
doc_tabs.push((i, r));
|
||||
if sel {
|
||||
ui.painter().rect_filled(r, t.radius_sm, t.card);
|
||||
ui.painter().rect_stroke(r, t.radius_sm, Stroke::new(1.0, t.card_border), egui::StrokeKind::Inside);
|
||||
@@ -1476,7 +1476,7 @@ fn pro_tabs(app: &mut PhotocraftApp, ui: &mut egui::Ui) -> TabStrip {
|
||||
resp.context_menu(|ui| {
|
||||
tab_action = tab_context_menu(ui, i, tab_count);
|
||||
});
|
||||
doc_tabs.push(r);
|
||||
doc_tabs.push((i, r));
|
||||
}
|
||||
for &(i, r) in placed.iter().filter(|(i, _)| *i >= tab_count) {
|
||||
let Some((job, _, frac)) = opening.get(i - tab_count) else { continue };
|
||||
@@ -1535,14 +1535,19 @@ fn pro_tabs(app: &mut PhotocraftApp, ui: &mut egui::Ui) -> TabStrip {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TabStrip {
|
||||
pub rect: Rect,
|
||||
/// The document tabs, left to right.
|
||||
pub tabs: Vec<Rect>,
|
||||
/// The document tabs shown, left to right, with their document index. When the tabs overflow
|
||||
/// into the » menu, some documents have no tab here.
|
||||
pub tabs: Vec<(usize, Rect)>,
|
||||
}
|
||||
|
||||
impl TabStrip {
|
||||
/// The tab position a drop at `x` opens at: before the first tab whose middle is right of it.
|
||||
/// The document position a drop at `x` opens at: before the first shown tab whose middle is
|
||||
/// right of it, else after the last shown tab.
|
||||
pub fn slot(&self, x: f32) -> usize {
|
||||
self.tabs.iter().filter(|r| r.center().x < x).count()
|
||||
match self.tabs.iter().find(|(_, r)| r.center().x >= x) {
|
||||
Some(&(i, _)) => i,
|
||||
None => self.tabs.last().map_or(0, |&(i, _)| i.saturating_add(1)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1554,7 +1559,9 @@ fn drop_slot_line(app: &mut PhotocraftApp, ui: &egui::Ui) {
|
||||
let at = app.services.cursor_pos.as_mut().and_then(|f| f(ui.ctx()));
|
||||
let crate::file_open::DropTarget::Tabs(slot) = app.drop_target(ui.ctx(), at) else { return };
|
||||
let Some(tabs) = app.tab_strip.as_ref().map(|s| &s.tabs) else { return };
|
||||
let Some((r, after)) = tabs.get(slot).map(|r| (*r, false)).or_else(|| tabs.last().map(|r| (*r, true))) else { return };
|
||||
let Some((r, after)) = tabs.iter().find(|(i, _)| *i == slot).map(|&(_, r)| (r, false)).or_else(|| tabs.last().map(|&(_, r)| (r, true))) else {
|
||||
return;
|
||||
};
|
||||
crate::widgets::drop_line(ui, r, after, true, &crate::theme::Tokens::get(ui.ctx()));
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ fn overflowing_tabs_stay_on_the_strip_and_the_rest_move_to_the_menu() {
|
||||
assert_eq!(h.state().session.documents().len(), 14);
|
||||
let s = strip(&h);
|
||||
assert!(s.tabs.len() < 14, "some tabs must overflow into the menu: {} shown", s.tabs.len());
|
||||
for r in &s.tabs {
|
||||
for (_, r) in &s.tabs {
|
||||
assert!(s.rect.contains_rect(*r), "a tab runs past the strip: {r:?} in {:?}", s.rect);
|
||||
}
|
||||
assert!(has_menu(&h), "the strip carries a » menu while tabs are hidden");
|
||||
@@ -78,7 +78,7 @@ fn studio_tabs_overflow_into_the_same_menu() {
|
||||
let h = harness(14, ThemeKind::Studio);
|
||||
let s = strip(&h);
|
||||
assert!(s.tabs.len() < 14, "{} shown", s.tabs.len());
|
||||
for r in &s.tabs {
|
||||
for (_, r) in &s.tabs {
|
||||
assert!(s.rect.contains_rect(*r), "a tab runs past the strip: {r:?} in {:?}", s.rect);
|
||||
}
|
||||
assert!(has_menu(&h), "the Studio strip carries a » menu too");
|
||||
|
||||
@@ -441,7 +441,7 @@ fn files_dropped_on_the_tabs_open_at_that_slot() {
|
||||
// One tab at (100, 20)–(200, 46): its left half is slot 0, its right half and beyond slot 1.
|
||||
let tab = egui::Rect::from_min_max(egui::pos2(100.0, 20.0), egui::pos2(200.0, 46.0));
|
||||
let strip = egui::Rect::from_min_max(egui::pos2(92.0, 14.0), egui::pos2(700.0, 50.0));
|
||||
app.tab_strip = Some(crate::canvas::TabStrip { rect: strip, tabs: vec![tab] });
|
||||
app.tab_strip = Some(crate::canvas::TabStrip { rect: strip, tabs: vec![(0, tab)] });
|
||||
assert_eq!(app.drop_target(&ctx, Some(egui::pos2(120.0, 30.0))), crate::file_open::DropTarget::Tabs(0));
|
||||
assert_eq!(app.drop_target(&ctx, Some(egui::pos2(180.0, 30.0))), crate::file_open::DropTarget::Tabs(1));
|
||||
assert_eq!(app.drop_target(&ctx, Some(egui::pos2(600.0, 30.0))), crate::file_open::DropTarget::Tabs(1));
|
||||
@@ -452,12 +452,27 @@ fn files_dropped_on_the_tabs_open_at_that_slot() {
|
||||
assert_eq!(app.ui.views.len(), 3);
|
||||
assert_eq!(app.ui.views[2].zoom, 3.0, "views move with their documents");
|
||||
// Past the last tab: opened at the end.
|
||||
let tabs = (0..3).map(|i| tab.translate(egui::vec2(104.0 * i as f32, 0.0))).collect();
|
||||
let tabs = (0..3).map(|i| (i, tab.translate(egui::vec2(104.0 * i as f32, 0.0)))).collect();
|
||||
app.tab_strip = Some(crate::canvas::TabStrip { rect: strip, tabs });
|
||||
app.open_dropped(&ctx, vec![dropped("last.png", Ok(b"x".to_vec()))], Some(egui::pos2(600.0, 30.0)));
|
||||
assert_eq!(app.session.documents().last().map(|d| d.doc.name.as_str()), Some("last.png"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_drop_slot_counts_documents_not_the_tabs_shown() {
|
||||
// #1276: when the tabs overflow into the » menu, the shown tabs aren't documents 0, 1, 2…
|
||||
// Documents 4, 5 and 6 shown: a drop before document 5's tab opens at position 5.
|
||||
let tab = |i: usize, x: f32| (i, egui::Rect::from_min_max(egui::pos2(x, 20.0), egui::pos2(x + 100.0, 46.0)));
|
||||
let strip = crate::canvas::TabStrip {
|
||||
rect: egui::Rect::from_min_max(egui::pos2(0.0, 14.0), egui::pos2(700.0, 50.0)),
|
||||
tabs: vec![tab(4, 0.0), tab(5, 104.0), tab(6, 208.0)],
|
||||
};
|
||||
assert_eq!(strip.slot(20.0), 4);
|
||||
assert_eq!(strip.slot(130.0), 5);
|
||||
assert_eq!(strip.slot(500.0), 7, "past the last shown tab: after it");
|
||||
assert_eq!(crate::canvas::TabStrip { rect: strip.rect, tabs: vec![] }.slot(10.0), 0);
|
||||
}
|
||||
|
||||
/// The whole app (eframe harness, real layout): over the tab strip, the pointer read from the OS
|
||||
/// service picks a slot; files dropped there open (in the background) at it; dropped on the canvas they're placed in Free Transform, and Esc takes the place back.
|
||||
#[test]
|
||||
@@ -483,7 +498,7 @@ fn drag_and_drop_in_the_running_app() {
|
||||
let strip = h.state().tab_strip.clone().expect("the tab strip is drawn");
|
||||
assert_eq!(strip.tabs.len(), 2);
|
||||
// Hovering over the first tab's left half: slot 0.
|
||||
let over_first = egui::pos2(strip.tabs[0].left() + 4.0, strip.tabs[0].center().y);
|
||||
let over_first = egui::pos2(strip.tabs[0].1.left() + 4.0, strip.tabs[0].1.center().y);
|
||||
pointer.set(over_first);
|
||||
h.input_mut().hovered_files.push(egui::HoveredFile { path: Some(file.clone()), ..Default::default() });
|
||||
h.step();
|
||||
|
||||
Reference in new issue
Block a user