mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-05 18:18:49 +00:00
Cryptomatte eyedropper: arm it from the Selection in Effect Controls (#473)
An eyedropper next to the Cryptomatte effect's Selection arms the viewer pick, and the status line says how to use it. It stays armed until Esc or the button (Esc in a text field or a menu only ends that); disarming, New, Open and Revert end it and let go of the session's probe. The app runs the pick's bookkeeping at the start and end of each pass, the viewer hands the pointer to the pick while it is armed and opens its context menu for a right-click the pick forwarded, and a Selection cut off by a narrow Effect Controls panel shows whole on hover. UI tests: ui_crypto_pick (arming, hover, clicks, modifiers, drags, waiting clicks, status, highlight) and ui_crypto_pick_undo (one undo step per pick, the probe kept over undo).
This commit is contained in:
1 parent
6b0f72e4be
commit
7cea1625f6
7 files changed
+3252
-11
No files matched your search
@@ -327,7 +327,9 @@ impl EffectcraftApp {
|
||||
ctx.request_repaint();
|
||||
return;
|
||||
}
|
||||
panels::crypto_pick::pass_start(self, &ctx);
|
||||
self.frame(ui);
|
||||
panels::crypto_pick::upkeep(self, &ctx);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pointer_wrap::finish_native(&ctx, frame);
|
||||
// wasm32: no frame threads; render queued frames now, between UI frames.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::common::{Actions, DIAL_ROW, PARAM_NAME_X, ROW, TREE_INDENT, gesture_key};
|
||||
use super::{
|
||||
Align2, Color32, EffectcraftApp, EvalCtx, FxPick, Icon, Layer, Node, ParamUi, PropGroup, Property, Rect, Sense, Stroke, Tokens, Value, fw, icons, json,
|
||||
pos2, vec2, widgets,
|
||||
Align2, Color32, EffectcraftApp, EvalCtx, FxPick, GroupKind, Icon, Layer, Node, ParamUi, PropGroup, Property, Rect, Sense, Stroke, Tokens, Value, fw,
|
||||
icons, json, pos2, vec2, widgets,
|
||||
};
|
||||
|
||||
fn triangle(p: &egui::Painter, c: egui::Pos2, left: bool, col: Color32) {
|
||||
@@ -65,6 +65,9 @@ pub(crate) fn toggle_pick(app: &mut EffectcraftApp, kind: &str, layer: &Layer, p
|
||||
app.ui.text_color_pick = None;
|
||||
app.ui.status = if kind == "point" {
|
||||
format!("Click in the Composition panel to set {}", prop.name)
|
||||
} else if kind == crate::panels::crypto_pick::KIND {
|
||||
let keys = crate::panels::crypto_pick::remove_keys(egui::os::OperatingSystem::from_target_os());
|
||||
crate::i18n::tr_args("Click IDs in the Composition panel: Shift adds, {} removes, Esc ends", &[&keys])
|
||||
} else {
|
||||
format!("Click in the Composition panel to sample {}", prop.name)
|
||||
};
|
||||
@@ -485,11 +488,27 @@ fn value_text(app: &mut EffectcraftApp, ui: &mut egui::Ui, cx: &PropCx, value: &
|
||||
let Value::Str(s) = value else {
|
||||
return;
|
||||
};
|
||||
let PropCx { t, uid, cy, vx, r, layer, prop, .. } = *cx;
|
||||
let fr = Rect::from_min_size(pos2(vx, cy - 9.0), vec2((r.max.x - vx - 12.0).clamp(80.0, 320.0), 18.0));
|
||||
let PropCx { t, uid, cy, vx, r, layer, group, prop, .. } = *cx;
|
||||
// Cryptomatte's Selection also has an eyedropper that picks IDs in the viewer (#473).
|
||||
let crypto = matches!(&group.kind, GroupKind::Effect { effect } if effect == "ec.3d.cryptomatte") && prop.match_id == "selection";
|
||||
let fr = Rect::from_min_size(pos2(vx, cy - 9.0), vec2((r.max.x - vx - if crypto { 36.0 } else { 12.0 }).clamp(80.0, 320.0), 18.0));
|
||||
if crypto {
|
||||
let er = Rect::from_center_size(pos2(fr.max.x + 14.0, cy), vec2(18.0, 18.0));
|
||||
let active = app.ui.fx_pick.as_ref().is_some_and(|k| k.prop == uid && k.kind == crate::panels::crypto_pick::KIND);
|
||||
if fw::eyedropper_button(ui, er, active, egui::Id::unique(("ec-eye", uid)), &t)
|
||||
.on_hover_text(crate::i18n::tr("Eyedropper: click, then click in the Composition panel"))
|
||||
.clicked()
|
||||
{
|
||||
toggle_pick(app, crate::panels::crypto_pick::KIND, layer, prop);
|
||||
}
|
||||
app.auto.add(&format!("effectControls.prop.{uid}.eyedropper"), er, &prop.name);
|
||||
}
|
||||
let bid = egui::Id::unique(("ec-text", uid));
|
||||
let mut buf: String = ui.data(|d| d.get_temp(bid)).unwrap_or_else(|| s.to_owned());
|
||||
let resp = widgets::text_field(ui, fr, &mut buf, "", &t);
|
||||
// Cut off by a narrow panel (a long Selection): the whole text on hover.
|
||||
let cut = !resp.has_focus() && ui.painter().layout_no_wrap(buf.clone(), egui::TextStyle::Body.resolve(ui.style()), t.text).size().x > fr.width() - 17.0;
|
||||
let resp = if cut { resp.on_hover_text(buf.clone()) } else { resp };
|
||||
// The field has no visible hint: give the screen reader the parameter's name.
|
||||
resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::TextInput, ui.is_enabled(), &prop.name));
|
||||
app.auto.add(&format!("effectControls.prop.{uid}.value"), fr, &prop.name);
|
||||
|
||||
@@ -221,9 +221,19 @@ pub fn viewer_hook(
|
||||
let ctx = ui.ctx().clone();
|
||||
crate::color_picker::note_viewer(&ctx, map.area);
|
||||
let mut actions: Actions = vec![];
|
||||
let point_preview = match fx_pick_session(app, ui, painter, map, ectx, l2c, &mut actions) {
|
||||
ControlFlow::Break(()) => return,
|
||||
ControlFlow::Continue(p) => p,
|
||||
let point_preview = if let Some(pick) = app.ui.fx_pick.clone().filter(|p| p.kind == crate::panels::crypto_pick::KIND) {
|
||||
match ectx.comp.layer(effectcraft_engine::project::LayerId(pick.layer)) {
|
||||
Some(l) => {
|
||||
crate::panels::crypto_pick::viewer_hook(app, ui, painter, map, ectx, l, &crate::panels::viewer::from_effect_space(ectx, l, l2c(ectx, l)), &pick)
|
||||
}
|
||||
None => app.ui.fx_pick = None,
|
||||
}
|
||||
None
|
||||
} else {
|
||||
match fx_pick_session(app, ui, painter, map, ectx, l2c, &mut actions) {
|
||||
ControlFlow::Break(()) => return,
|
||||
ControlFlow::Continue(p) => p,
|
||||
}
|
||||
};
|
||||
text_color_pick(app, ui, painter, map, ectx, &mut actions);
|
||||
popup_eyedropper(app, ui, painter, map);
|
||||
|
||||
@@ -830,9 +830,9 @@ fn phase_menu(app: &mut EffectcraftApp, ui: &mut egui::Ui, ectx: &EvalCtx, v: &m
|
||||
// Interaction.
|
||||
let resp = ui.interact(area, egui::Id::unique("viewer-interact"), Sense::click_and_drag());
|
||||
let menu_hits_id = egui::Id::unique(("viewer-menu-hits", cid.0));
|
||||
if resp.secondary_clicked()
|
||||
&& let Some(pos) = resp.interact_pointer_pos()
|
||||
{
|
||||
// A right-click an overlay took (the Cryptomatte eyedropper's) opens the menu too.
|
||||
let forwarded = ui.data_mut(|d| d.remove_temp::<egui::Pos2>(egui::Id::unique(FORWARDED_SECONDARY)));
|
||||
if let Some(pos) = resp.interact_pointer_pos().filter(|_| resp.secondary_clicked()).or(forwarded) {
|
||||
// Picking an already selected layer preserves the group; an unselected hit
|
||||
// becomes the target, using the same visible/unlocked hit test as left-click.
|
||||
pick(app, ectx, map.to_comp(pos), false);
|
||||
@@ -844,7 +844,9 @@ fn phase_menu(app: &mut EffectcraftApp, ui: &mut egui::Ui, ectx: &EvalCtx, v: &m
|
||||
app.session.state.selected_layers.iter().filter(|id| comp.layer(**id).is_some_and(|l| !l.switches.locked)).map(|id| id.0).collect();
|
||||
let mut context_actions = Vec::new();
|
||||
let copy_frame = crate::i18n::label(app, "comp.copyFrame", "Copy Frame to Clipboard");
|
||||
crate::widgets::context_menu(&resp, |ui| {
|
||||
let menu = egui::Popup::context_menu(&resp).style(crate::widgets::menu_style);
|
||||
let open = forwarded.map(|_| egui::containers::SetOpenCommand::Bool(true));
|
||||
if open.is_some() { menu.open_memory(open) } else { menu }.show(|ui| {
|
||||
let hits = ui.data(|d| d.get_temp::<Vec<(u64, String)>>(menu_hits_id)).unwrap_or_default();
|
||||
if !hits.is_empty() {
|
||||
ui.menu_button(crate::i18n::tr("Select"), |ui| {
|
||||
|
||||
@@ -23,6 +23,8 @@ mod ui_color_hex;
|
||||
mod ui_comp_presets;
|
||||
mod ui_contrast;
|
||||
mod ui_control_modifiers;
|
||||
mod ui_crypto_pick;
|
||||
mod ui_crypto_pick_undo;
|
||||
mod ui_dialog_preview;
|
||||
mod ui_dock_resize;
|
||||
mod ui_drag_drop;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user