mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
Merge pull request #3492 from nrq6993-a11y/stream/vc-feat1-g11-proxy-paint-drop-3467
G11: Copy fill/stroke by dragging between proxies (#3467)
This commit is contained in:
@@ -382,6 +382,12 @@ pub(crate) fn proxy(app: &mut VectorcraftApp, ui: &mut Ui, size: f32) {
|
||||
if let Some(stroke) = c.pick {
|
||||
app.run("ui.colorPicker", json!({ "stroke": stroke })).ok();
|
||||
}
|
||||
if let Some(p) = c.dropped_fill {
|
||||
app.run("paint.setFill", p).ok();
|
||||
}
|
||||
if let Some(p) = c.dropped_stroke {
|
||||
app.run("paint.setStroke", p).ok();
|
||||
}
|
||||
}
|
||||
|
||||
/// The Fill (or `stroke`) chip of the Control bar and the Properties panel, `size` square (with a
|
||||
@@ -538,6 +544,43 @@ pub(crate) fn empty_state(ui: &mut Ui, icon: &str, title: &str, body: &str) {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn dropping_a_proxy_paint_on_the_other_proxy_applies_it() {
|
||||
use egui::{Event, Modifiers, PointerButton};
|
||||
use vectorcraft_color::{Color, Paint};
|
||||
|
||||
use crate::widgets::PanelDrag;
|
||||
|
||||
let mut app = crate::VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 200, "height": 200})).unwrap();
|
||||
app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 40, "height": 40})).unwrap();
|
||||
app.run("paint.setFill", json!({"color": "#ff0000"})).unwrap();
|
||||
app.run("paint.setStroke", json!({"color": "#0000ff"})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
let stroke_at = std::cell::Cell::new(egui::Pos2::ZERO);
|
||||
let frame = |app: &mut crate::VectorcraftApp, events: Vec<Event>| {
|
||||
let mut out = ctx.run_ui(egui::RawInput { events, ..Default::default() }, |ui| {
|
||||
proxy(app, ui, 36.0);
|
||||
if let Some(c) = ctx.read_response(ui.id().with("stroke-proxy")).map(|r| r.rect.center()) {
|
||||
stroke_at.set(c);
|
||||
}
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
};
|
||||
frame(&mut app, vec![]);
|
||||
let at = stroke_at.get();
|
||||
let red = PanelDrag::paint(Paint::solid(Color::from_hex("#ff0000").unwrap()));
|
||||
egui::DragAndDrop::set_payload(&ctx, red);
|
||||
let release = Event::PointerButton { pos: at, button: PointerButton::Primary, pressed: false, modifiers: Modifiers::NONE };
|
||||
frame(&mut app, vec![Event::PointerMoved(at), release]);
|
||||
let stroke = app.session.shown_stroke().unwrap();
|
||||
assert_eq!(stroke.paint.color().map(|c| c.to_hex()), Some("#ff0000".into()), "{}", app.ui.status);
|
||||
let st = app.session.active().unwrap();
|
||||
let id = st.selection.objects[0];
|
||||
assert_eq!(st.doc.node(id).unwrap().appearance.fill_paint().color().map(|c| c.to_hex()), Some("#ff0000".into()));
|
||||
assert!(egui::DragAndDrop::payload::<PanelDrag>(&ctx).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paint_params_roundtrip_shapes() {
|
||||
assert_eq!(paint_params(&Paint::None), json!({"none": true}));
|
||||
|
||||
@@ -839,7 +839,7 @@ pub fn chip_button_named(ui: &mut Ui, size: f32, chevron: bool, tip: &str, draw:
|
||||
}
|
||||
|
||||
/// What the user did on a [`fill_stroke_proxy`].
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ProxyClicks {
|
||||
pub fill: bool,
|
||||
pub stroke: bool,
|
||||
@@ -847,6 +847,10 @@ pub struct ProxyClicks {
|
||||
pub default: bool,
|
||||
/// Double-clicked a square: open the Color Picker for it (`true` = the stroke).
|
||||
pub pick: Option<bool>,
|
||||
/// A paint dragged from the other proxy (or a swatch) dropped on the fill square.
|
||||
pub dropped_fill: Option<serde_json::Value>,
|
||||
/// A paint dropped on the stroke square.
|
||||
pub dropped_stroke: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
/// The Fill/Stroke proxy pair (two overlapping squares). `mixed` (fill, stroke) draws a "?" square
|
||||
@@ -859,6 +863,8 @@ pub fn fill_stroke_proxy(ui: &mut Ui, fill: &Paint, stroke: &Paint, mixed: (bool
|
||||
let stroke_r = Rect::from_min_size(rect.max - Vec2::splat(s), Vec2::splat(s));
|
||||
let stroke_resp = ui.interact(stroke_r, ui.id().with("stroke-proxy"), Sense::click_and_drag());
|
||||
let fill_resp = ui.interact(fill_r, ui.id().with("fill-proxy"), Sense::click_and_drag());
|
||||
let dropped_fill = if mixed.0 { None } else { proxy_paint_drop(ui, &fill_resp) };
|
||||
let dropped_stroke = if mixed.1 { None } else { proxy_paint_drop(ui, &stroke_resp) };
|
||||
// A proxy drags its paint (onto the Swatches panel or art), unless it shows "?".
|
||||
for (resp, paint, mixed) in [(&fill_resp, fill, mixed.0), (&stroke_resp, stroke, mixed.1)] {
|
||||
if !mixed {
|
||||
@@ -915,9 +921,26 @@ pub fn fill_stroke_proxy(ui: &mut Ui, fill: &Paint, stroke: &Paint, mixed: (bool
|
||||
swap: swap.on_hover_text(tl!("Swap Fill and Stroke (Shift+X)")).clicked(),
|
||||
default: def.on_hover_text(tl!("Default Fill and Stroke (D)")).clicked(),
|
||||
pick,
|
||||
dropped_fill,
|
||||
dropped_stroke,
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`PanelDrag::Paint`] released on a Fill/Stroke proxy square: highlight while held over it.
|
||||
fn proxy_paint_drop(ui: &Ui, resp: &Response) -> Option<serde_json::Value> {
|
||||
if let Some(drag) = resp.dnd_hover_payload::<PanelDrag>()
|
||||
&& let PanelDrag::Paint { params, .. } = &*drag
|
||||
&& !params.is_null()
|
||||
{
|
||||
let t = Tokens::get(ui.ctx());
|
||||
ui.painter().rect_stroke(resp.rect, 0.0, Stroke::new(2.0, t.accent), StrokeKind::Outside);
|
||||
}
|
||||
resp.dnd_release_payload::<PanelDrag>().and_then(|d| {
|
||||
let PanelDrag::Paint { params, .. } = &*d else { return None };
|
||||
(!params.is_null()).then_some(params.clone())
|
||||
})
|
||||
}
|
||||
|
||||
/// A compact dropdown of interface labels (shown in the UI language). Returns the chosen index.
|
||||
pub fn dropdown(ui: &mut Ui, id: impl std::hash::Hash + std::fmt::Debug, current: &str, options: &[&str], width: f32) -> Option<usize> {
|
||||
dropdown_with(ui, id, current, options, width, |_| true)
|
||||
|
||||
Reference in New Issue
Block a user