fix(ui): show brush tip thumbnails while dragging presets (#5203)

* fix(ui): show brush tip thumbnails while dragging presets

* test(app): wait for autosave sidecar revision before recovery assertions
This commit is contained in:
Ivan · VanTan authored and GitHub committed 2026-10-14 13:08:05 -04:00
1 parent 11c46b7121
commit 5ae3ee0ae5
3 files changed
+69 -2

No files matched your search

+22 -1
View File
@@ -920,7 +920,28 @@ pub fn preset_list(ui: &mut egui::Ui, presets: &[BrushPreset], current: Option<&
egui::Area::new(egui::Id::new(("brush-drag-label", layout.id))).order(egui::Order::Tooltip).fixed_pos(p + vec2(12.0, 8.0)).interactable(false).show(
ui.ctx(),
|ui| {
egui::Frame::popup(ui.style()).show(ui, |ui| ui.label(name));
if matches!(&*d, BrushDrag::Preset(_)) {
ui.set_opacity(0.8);
}
egui::Frame::popup(ui.style()).show(ui, |ui| {
ui.horizontal(|ui| {
if let BrushDrag::Preset(n) = &*d
&& let Some(preset) = presets.iter().find(|preset| preset.name == *n)
{
let brush = &preset.brush;
let tip = brush_preview::tip_texture(
ui.ctx(),
&format!("brushes-drag-tip:{}", preset.name),
&brush.tip,
(brush.hardness, brush.angle, brush.roundness),
36,
Tokens::get(ui.ctx()).text,
);
ui.add(egui::Image::new(&tip).fit_to_exact_size(vec2(36.0, 36.0)).alt_text(&preset.name));
}
ui.label(name);
});
});
},
);
}
+44
View File
@@ -7,6 +7,50 @@ use egui_kittest::kittest::{NodeT, Queryable};
use super::*;
#[test]
fn dragging_a_brush_shows_its_tip_preview() {
for theme in crate::theme::ThemeKind::ALL {
for mode in 0..3 {
let mut app = PhotocraftApp::new(photocraft_engine::Session::new(), Default::default());
app.run("prefs.set", json!({"path": "interface.theme", "value": theme.id()})).unwrap();
app.ui.panels.brush_settings = mode != 2;
app.ui.brush_tab = 1;
app.ui.brushes_panel.view = if mode == 1 { BrushesView::Grid } else { BrushesView::List };
if mode == 2 {
app.ui.brush_picker = Some([300.0, 120.0]);
}
let name = app.session.tools.presets[0].name.clone();
let brush = app.session.tools.brush.clone();
let presets = app.session.tools.presets.clone();
let mut h = Harness::builder().with_size(vec2(1200.0, 900.0)).build_eframe(move |cc| {
PhotocraftApp::setup_context(&cc.egui_ctx, theme);
app
});
h.run_steps(4);
let start = h.get_by_role_and_label(Role::Button, &name).rect().center();
h.hover_at(start);
h.run_steps(1);
h.event(egui::Event::PointerButton { pos: start, button: egui::PointerButton::Primary, pressed: true, modifiers: egui::Modifiers::NONE });
h.run_steps(1);
let end = start + vec2(50.0, 10.0);
h.event(egui::Event::PointerMoved(end));
h.run_steps(4);
assert!(matches!(egui::DragAndDrop::payload::<BrushDrag>(&h.ctx).as_deref(), Some(BrushDrag::Preset(n)) if n == &name));
assert!(h.query_by_role_and_label(Role::Image, &name).is_some(), "dragged brush needs a real tip thumbnail, not only its name");
let renders = brush_preview::render_count(&h.ctx);
h.run_steps(2);
assert_eq!(brush_preview::render_count(&h.ctx), renders, "unchanged drag preview stays cached");
h.event(egui::Event::PointerMoved(start));
h.run_steps(1);
h.event(egui::Event::PointerButton { pos: start, button: egui::PointerButton::Primary, pressed: false, modifiers: egui::Modifiers::NONE });
h.run_steps(4);
assert!(h.query_by_role_and_label(Role::Image, &name).is_none(), "preview disappears on release");
assert_eq!(h.state().session.tools.brush, brush, "preview does not change brush settings");
assert_eq!(h.state().session.tools.presets, presets, "dropping on the source preserves the library");
}
}
}
#[test]
fn grid_columns_centre_whole_columns_past_the_indent() {
// 6 × 44 + 5 × 3 = 279 of the 290 past a 4 pt indent: 5 spare on each side (rounded down).
+3 -1
View File
@@ -1,6 +1,6 @@
# Brush and painting parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-13 · **Change:** minor (Hue Jitter amount track; Brush Settings minimum fields follow their Control; checkbox-only rows and non-destructive Smoothing switch; shaped-tip cursor follows the view; Color Replacement is a live toolbar tool; no brush-feel estimate changed) · **Target:** Adobe Photoshop 2026
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** minor (dragged preset tip preview; Hue Jitter amount track; Brush Settings minimum fields follow their Control; checkbox-only rows and non-destructive Smoothing switch; shaped-tip cursor follows the view; Color Replacement is a live toolbar tool; no brush-feel estimate changed) · **Target:** Adobe Photoshop 2026
Painting is where Photoshop users notice differences first, so it gets its own checklist: the
brush engine (`crates/paint/src/brush.rs`), the Brush Settings UI
@@ -108,6 +108,7 @@ tablets against Photoshop side by side.
|---|---|---|
| .abr import (v1/v2/v6) and export, persistent presets, folders | done | `psd/src/abr.rs` |
| Brushes panel: drag a folder into a folder | missing | #2014 |
| Dragged brush preset preview | done | Cached, translucent tip thumbnail plus name in panel list/grid and the preset picker; `ui-egui/src/brushes_tab.rs`, real pointer-drag test in `brushes_tab_tests.rs` (#4225, partial) |
| Tool presets (.tpl) | partial | Tool Presets panel exists; .tpl import not found |
| Import 500 brushes without blocking the UI | not run | P24 |
@@ -122,3 +123,4 @@ tablets against Photoshop side by side.
| 2026-10-12 | minor | A shaped tip's cursor outline turns and flips with a rotated or flipped view, and follows pixel aspect correction |
| 2026-10-13 | minor | Shape Dynamics and Transfer minimum inputs follow their Control and retain their values while disabled (#3825, partial). No brush-feel estimate changed. |
| 2026-10-13 | minor | Hue Jitter uses the same themed amount slider as other jitter controls (#3825, partial). No brush-feel estimate changed. |
| 2026-10-13 | minor | Dragging a brush preset shows its cached tip thumbnail and name in the Brushes panel and preset picker (#4225, partial). No brush-feel estimate changed. |