fix(ui): eliminate preview flickering when hovering presets and profiles
- Retain active preset preview when hovering within the scroll or panel area across headers and gaps. - Prevent premature dropping of hover preview textures during asynchronous rerendering in detail view. - Clear cached hover state when clicking an item or when the pointer leaves the panel. - Add headless regression tests covering preset hover persistence and transitions. Signed-off-by: benlukka <68825026+benlukka@users.noreply.github.com>
This commit is contained in:
parent
65da36675f
commit
ad2525ece2
|
|
@ -413,6 +413,15 @@ mod tests {
|
|||
assert_eq!(after.develop, before.develop);
|
||||
assert_eq!(after.history.len(), before.history.len(), "no history entry while hovering");
|
||||
assert!(h.app.session.undo.is_empty());
|
||||
// hovering down across group headers within the presets list retains the preview (no flicker)
|
||||
h.request("ui.hoverWidget", json!({"id": "presetGroup:Creative"}), t);
|
||||
h.step();
|
||||
assert_eq!(h.app.hover_preview.as_ref().map(|p| p.label.as_str()), Some("Preset: High Contrast B&W"));
|
||||
// hovering another preset switches smoothly to it
|
||||
h.request("ui.hoverWidget", json!({"id": "preset:lc.warm-glow"}), t);
|
||||
h.settle(SETTLE);
|
||||
h.step();
|
||||
assert_eq!(h.app.hover_preview.as_ref().map(|p| p.label.as_str()), Some("Preset: Warm Glow"));
|
||||
// thumbnails: variant textures for the visible presets
|
||||
h.app.ui.preset_thumbs = true;
|
||||
h.request("ui.move", json!({"x": 5, "y": 500}), t);
|
||||
|
|
|
|||
|
|
@ -280,8 +280,8 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
|
|||
} else if show_before {
|
||||
shown = draw(Slot::Before, img_rect);
|
||||
p.text(pos2(img_rect.left() + 8.0, img_rect.top() + 14.0), Align2::LEFT_CENTER, crate::i18n::tr("Before"), t.font(12.0), t.text);
|
||||
} else if let Some((key, label)) = &hover_key
|
||||
&& let Some(tex) = app.renderer.textures.get(&Slot::Hover).filter(|t| t.photo == id && t.key == *key)
|
||||
} else if let Some((_key, label)) = &hover_key
|
||||
&& let Some(tex) = app.renderer.textures.get(&Slot::Hover).filter(|t| t.photo == id)
|
||||
{
|
||||
p.image(tex.tex.id(), img_rect, Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), Color32::WHITE);
|
||||
let g = p.layout_no_wrap(label.clone(), t.font(12.0), Color32::WHITE);
|
||||
|
|
|
|||
|
|
@ -78,7 +78,8 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
|
|||
let current = active.and_then(|id| app.session.develop_of(id)).map(|d| (*d).clone());
|
||||
let thumbs = app.ui.preset_thumbs && active.is_some();
|
||||
let row_h = if thumbs { 54.0 } else { 26.0 };
|
||||
egui::ScrollArea::vertical().id_salt("presets-scroll").auto_shrink([false, false]).show(ui, |ui| {
|
||||
let scroll = egui::ScrollArea::vertical().id_salt("presets-scroll").auto_shrink([false, false]).show(ui, |ui| {
|
||||
ui.spacing_mut().item_spacing.y = 0.0;
|
||||
let mut groups: BTreeMap<String, Vec<Preset>> = BTreeMap::new();
|
||||
for p in &app.session.presets {
|
||||
groups.entry(p.group.clone()).or_default().push(p.clone());
|
||||
|
|
@ -141,11 +142,16 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
|
|||
if resp.hovered()
|
||||
&& let Some(s) = look
|
||||
{
|
||||
app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Preset: {name}", name = name), settings: s });
|
||||
let label = crate::i18n::tr_format!("Preset: {name}", name = name);
|
||||
app.hover_preview = Some(HoverPreview { label: label.clone(), settings: s });
|
||||
if let Some(id) = active {
|
||||
ui.data_mut(|d| d.insert_temp(egui::Id::new("last-preset-hover"), (id, pr.clone(), label)));
|
||||
}
|
||||
}
|
||||
if resp.clicked() {
|
||||
let _ = app.run("preset.apply", json!({"id": pid, "amount": 100}));
|
||||
ui.data_mut(|d| d.insert_temp(amt_id, (pid.clone(), 100.0)));
|
||||
ui.data_mut(|d| d.remove::<(lightcraft_catalog::PhotoId, Preset, String)>(egui::Id::new("last-preset-hover")));
|
||||
app.toast(ui.ctx(), crate::i18n::tr_format!("Preset: {name}", name = name));
|
||||
}
|
||||
let (builtin, group) = (pr.builtin, pr.group.clone());
|
||||
|
|
@ -207,6 +213,20 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
|
|||
}
|
||||
ui.add_space(30.0);
|
||||
});
|
||||
let last_hover_id = egui::Id::new("last-preset-hover");
|
||||
let pointer_in_scroll = ui.ctx().input(|i| i.pointer.hover_pos()).is_some_and(|pos| scroll.inner_rect.contains(pos));
|
||||
if pointer_in_scroll {
|
||||
if app.hover_preview.is_none()
|
||||
&& let (Some(id), Some(curr)) = (active, current.as_ref())
|
||||
&& let Some((saved_id, saved_preset, saved_label)) =
|
||||
ui.data(|d| d.get_temp::<(lightcraft_catalog::PhotoId, Preset, String)>(last_hover_id))
|
||||
&& saved_id == id
|
||||
{
|
||||
app.hover_preview = Some(HoverPreview { label: saved_label, settings: saved_preset.apply(curr, 1.0) });
|
||||
}
|
||||
} else {
|
||||
ui.data_mut(|d| d.remove::<(lightcraft_catalog::PhotoId, Preset, String)>(last_hover_id));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,20 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) {
|
|||
ui.add_space(6.0);
|
||||
}
|
||||
ui.add_space(30.0);
|
||||
let last_hover_id = egui::Id::new("last-profile-hover");
|
||||
let pointer_in_profiles = ui.ctx().input(|i| i.pointer.hover_pos()).is_some_and(|pos| ui.max_rect().contains(pos));
|
||||
if pointer_in_profiles {
|
||||
if app.hover_preview.is_none()
|
||||
&& let Some((saved_id, saved_profile_id)) = ui.data(|m| m.get_temp::<(PhotoId, String)>(last_hover_id))
|
||||
&& saved_id == id
|
||||
&& let Some(p) = profile(&saved_profile_id)
|
||||
{
|
||||
let s = with_profile(&d, p);
|
||||
app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", p.name), settings: s });
|
||||
}
|
||||
} else {
|
||||
ui.data_mut(|m| m.remove::<(PhotoId, String)>(last_hover_id));
|
||||
}
|
||||
}
|
||||
|
||||
/// `d` with profile `p` (at 100 % unless it is the applied one).
|
||||
|
|
@ -127,6 +141,7 @@ fn cell_ui(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings, id:
|
|||
}
|
||||
if resp.hovered() && !applied && !over_star {
|
||||
app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", p.name), settings: s.clone() });
|
||||
ui.data_mut(|m| m.insert_temp(egui::Id::new("last-profile-hover"), (id, p.id.to_string())));
|
||||
}
|
||||
let resp = resp.on_hover_text(format!("{} ({})", p.name, p.group));
|
||||
if resp.clicked() {
|
||||
|
|
@ -134,6 +149,7 @@ fn cell_ui(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings, id:
|
|||
let _ = app.run("profile.favorite", json!({"id": p.id}));
|
||||
} else if !applied {
|
||||
let _ = app.run("develop.profile", json!({"id": p.id, "amount": 100}));
|
||||
ui.data_mut(|m| m.remove::<(PhotoId, String)>(egui::Id::new("last-profile-hover")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue