mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 09:59:47 -06:00
126 lines
6.4 KiB
Rust
126 lines
6.4 KiB
Rust
use super::outline::{hit_at, hit_with, selection_hit_at};
|
|
use super::{Ui, VectorcraftApp, Xf, json, widgets};
|
|
|
|
/// Right-click: the object under the pointer is selected first unless it already is, then the
|
|
/// context menu lists what applies to the selection ([`crate::menus::context_items`]). The locked
|
|
/// objects under the pointer ([`vectorcraft_doc::hit::locked_objects_at`]) are kept in
|
|
/// `ui.context_locked` while the menu is open, for its Unlock submenu. With the Intertwine tool, a
|
|
/// right-click where three or more objects of the group overlap keeps the selection and puts what
|
|
/// it arranges in `ui.context_intertwine` ([`vectorcraft_tools::intertwine::menu_target`]).
|
|
pub(super) fn context_menu(app: &mut VectorcraftApp, resp: &egui::Response, xf: &Xf) {
|
|
if resp.secondary_clicked()
|
|
&& let Some(p) = resp.interact_pointer_pos()
|
|
{
|
|
let p = xf.to_doc(p);
|
|
let locked = hit_with(app, p, xf.zoom, |doc, p, opt| Some(vectorcraft_doc::hit::locked_objects_at(doc, p, opt)));
|
|
app.ui.context_locked = app.session.active().map(|st| st.uid).zip(locked);
|
|
app.ui.context_intertwine = app
|
|
.session
|
|
.active()
|
|
.filter(|_| app.session.tool_id() == "intertwine")
|
|
.and_then(|st| vectorcraft_tools::intertwine::menu_target(&st.doc, &st.selection.objects, p).map(|(g, m)| (st.uid, g, p, m)));
|
|
if app.ui.context_intertwine.is_none()
|
|
&& let Some(st) = app.session.active()
|
|
&& let Some(top) = selection_hit_at(app, p, xf.zoom).map(|h| h.top_object(st.isolation))
|
|
&& !st.selection.contains(top)
|
|
{
|
|
// A locked or hidden object can't be selected; the menu is then for the selection as is.
|
|
let _ = app.run("select.set", json!({ "ids": [top.0] }));
|
|
}
|
|
}
|
|
let mut clicked = None;
|
|
if resp.context_menu(|ui| crate::menus::context_menu_body(app, ui, &mut clicked)).is_none() {
|
|
app.ui.context_locked = None;
|
|
app.ui.context_intertwine = None;
|
|
}
|
|
if let Some((id, p)) = clicked {
|
|
crate::menus::invoke(app, &id, p);
|
|
}
|
|
}
|
|
|
|
/// A panel drag ([`widgets::PanelDrag`]) dropped on art acts on the object under the pointer,
|
|
/// selected or not: a paint (swatches, a Fill/Stroke proxy, the Gradient panel's thumbnail) goes
|
|
/// to its active proxy (`paint.setFill`/`paint.setStroke` with its `ids`; a gradient fits it), the
|
|
/// Appearance panel's thumbnail gives the object (the topmost one hit) the appearance it carries
|
|
/// (`appearance.copyFrom`), a graphic style is applied to it (`graphicStyle.apply`; with Alt, on
|
|
/// top of its appearance). A chip follows the pointer meanwhile.
|
|
pub(super) fn panel_drop(app: &mut VectorcraftApp, ui: &Ui, resp: &egui::Response, xf: &Xf) {
|
|
crate::panels::swatches::drag_preview(app, ui.ctx());
|
|
let Some(pos) = ui.input(|i| i.pointer.interact_pos()) else { return };
|
|
// Not through a floating panel over the canvas.
|
|
if ui.ctx().layer_id_at(pos).is_some_and(|l| l != resp.layer_id) {
|
|
return;
|
|
}
|
|
let Some(d) = resp.dnd_release_payload::<widgets::PanelDrag>() else { return };
|
|
// A Libraries panel graphic: a copy centred where it is dropped.
|
|
if let widgets::PanelDrag::LibraryGraphic { library, item } = &*d {
|
|
let at = xf.to_doc(pos);
|
|
if let Err(e) = app.run("library.use", json!({"library": library, "kind": "graphic", "item": item, "center": [at.x, at.y]})) {
|
|
app.status(e);
|
|
}
|
|
return;
|
|
}
|
|
// A symbol from the Symbols panel: an instance centred where it is dropped, on art or not.
|
|
if let widgets::PanelDrag::Symbol(name) = &*d {
|
|
let at = xf.to_doc(pos);
|
|
if let Err(e) = app.run("symbol.place", json!({"name": name, "x": at.x, "y": at.y})) {
|
|
app.status(e);
|
|
}
|
|
return;
|
|
}
|
|
// A symbol from a symbol library panel: added to the document's Symbols and placed there.
|
|
if let widgets::PanelDrag::LibrarySymbol { library, name } = &*d {
|
|
let at = xf.to_doc(pos);
|
|
if let Err(e) = app.run("symbol.addFromLibrary", json!({"library": library, "name": name, "place": true, "x": at.x, "y": at.y})) {
|
|
app.status(e);
|
|
}
|
|
return;
|
|
}
|
|
let Some(hit) = hit_at(app, xf.to_doc(pos), xf.zoom) else { return };
|
|
let Some(st) = app.session.active() else { return };
|
|
let (cmd, params) = match &*d {
|
|
widgets::PanelDrag::Paint { params, .. } => {
|
|
// Colour groups paint nothing.
|
|
if params.is_null() {
|
|
return;
|
|
}
|
|
let mut params = params.clone();
|
|
params["ids"] = json!([vectorcraft_tools::xform::paint_owner(&st.doc, hit.leaf).0]);
|
|
params["focus"] = json!(false);
|
|
(crate::panels::proxy_cmd(app, false), params)
|
|
}
|
|
widgets::PanelDrag::Appearance(source) => {
|
|
let target = hit.top_object(st.isolation);
|
|
if target == *source {
|
|
return;
|
|
}
|
|
("appearance.copyFrom", json!({"source": source.0, "ids": [target.0]}))
|
|
}
|
|
widgets::PanelDrag::GraphicStyle(name) => {
|
|
let add = ui.input(|i| i.modifiers.alt);
|
|
("graphicStyle.apply", json!({"name": name, "ids": [hit.top_object(st.isolation).0], "add": add}))
|
|
}
|
|
// A brush from the Brushes panel: the path it lands on takes it (a compound path as a whole).
|
|
widgets::PanelDrag::Brush { name, .. } => {
|
|
("brush.apply", json!({"name": name, "ids": [vectorcraft_tools::xform::paint_owner(&st.doc, hit.leaf).0]}))
|
|
}
|
|
// A brush from a brush library panel: added to the document's Brushes and applied to the
|
|
// path it lands on, in one step.
|
|
widgets::PanelDrag::LibraryBrush { library, name } => (
|
|
"brush.addFromLibrary",
|
|
json!({"library": library, "name": name, "apply": true, "ids": [vectorcraft_tools::xform::paint_owner(&st.doc, hit.leaf).0]}),
|
|
),
|
|
// Art dragged back onto the canvas: its move was already dropped. (A symbol was placed
|
|
// above.)
|
|
widgets::PanelDrag::Art(_)
|
|
| widgets::PanelDrag::Symbol(_)
|
|
| widgets::PanelDrag::LibrarySymbol { .. }
|
|
| widgets::PanelDrag::LibraryGraphic { .. } => return,
|
|
// A Libraries panel style or group only moves within that panel.
|
|
widgets::PanelDrag::LibraryItem { .. } | widgets::PanelDrag::LibraryGroup { .. } => return,
|
|
};
|
|
if let Err(e) = app.run(cmd, params) {
|
|
app.status(e);
|
|
}
|
|
}
|