blockworx_kernel/
dispatch.rs1use blockworx_doc::{block_model::Asset, id::BlockId};
15use blockworx_editor::{
16 edit::{assets, describe::Label, lower::asset_within_limit},
17 shape::ShapeId,
18};
19use blockworx_paint::{TextLayout, ZoomStep};
20use blockworx_tools::{
21 SelectTool,
22 commands::{ScriptedApply, apply_transition},
23 resize_block::ResizeBlock,
24 tool::{Action, Tool, Transition},
25};
26
27use crate::{handoff::Handoff, session::Session};
28
29impl Session {
30 pub fn dispatch(&mut self, transition: impl Into<Transition>, layout: &dyn TextLayout) {
37 let transition = transition.into();
38 let label = transition.label();
39 let outcome = self.commit_gesture(Label::verb(label), |drawing| {
40 apply_transition(transition, drawing)
41 });
42 let action = match outcome {
43 ScriptedApply::Applied(settles_on) => {
44 if let Some(tool) = settles_on {
45 self.tool = tool;
46 }
47 return;
48 }
49 ScriptedApply::NeedsApp(action) => *action,
50 ScriptedApply::NotApplicable(effect) => {
51 unreachable!("the shell performs {} itself", effect.named())
52 }
53 };
54 match action {
55 Action::Copy(shapes) => {
56 let clip = self.drawing().copy_selection(&shapes);
57 self.copied(clip);
58 }
59 Action::CopyPins(pins) => {
60 let clip = self.drawing().copy_pins(&pins);
61 self.copied(clip);
62 }
63 Action::Cut(shapes) => {
64 let from = self.doc.session();
65 let clip =
66 self.commit_gesture(Label::verb(label), |d| d.cut_selection(&shapes, from));
67 self.copied(clip);
68 self.tool = Tool::Select(SelectTool);
69 }
70 Action::CutPins(pins) => {
71 let from = self.doc.session();
72 let clip = self.commit_gesture(Label::verb(label), |d| d.cut_pins(&pins, from));
73 self.copied(clip);
74 self.tool = Tool::Select(SelectTool);
75 }
76 Action::Paste(text) => self.paste(&text),
77 Action::ExpandBlock(rid) => {
78 self.path.push(rid);
79 self.after_navigate();
80 }
81 Action::GoToPath(path) => {
82 self.path = path;
83 self.after_navigate();
84 }
85 Action::NavSelect { block, extend } => self.nav_select(block, extend.into()),
86 Action::FrameRect(rect) => self.fit_to_rect(rect),
89 Action::AcknowledgeFailure(failure) => {
90 self.failures
91 .acknowledge(crate::chrome::Acknowledged(failure));
92 }
93 Action::GoUp => {
94 if self.path.pop().is_some() {
95 self.after_navigate();
96 }
97 }
98 Action::Undo => self.step_history(blockworx_tools::history::Direction::Back),
99 Action::Redo => self.step_history(blockworx_tools::history::Direction::Forward),
100 Action::Nudge { dx, dy } => self.nudge_selection(dx, dy),
101 Action::ResetView => self.fit_view(),
102 Action::ToggleDiagnostic => self.diagnostic = self.diagnostic.toggled(),
103 Action::ToggleFrameRate => self.frame_rate = self.frame_rate.toggled(),
104 Action::Zoom(step) => self.zoom(step),
105 Action::ViewRev(rev) => self.view_rev(rev),
106 Action::ViewHead => self.view_head(),
107 Action::TagRev { at, name, how } => self.tag_rev(at, &name, how),
108 Action::SetPalette(palette) => self.paint_in(palette),
109 Action::SetIcon { block, asset } => self.set_icon(block, &asset, label),
110 Action::Export { format, selection } => {
111 let content = self.export_content(layout, format, selection);
112 let name = self.sheet.name.clone();
113 self.hands_back(Handoff::Export { content, name });
114 }
115 Action::PlaceImage { asset } => self.place_picked_image(&asset, label),
116 Action::StampTool { .. }
117 | Action::Arm(_)
118 | Action::ArmAddRouteLabel(_)
119 | Action::OpenEditor(_)
120 | Action::Delete(_)
121 | Action::SetPinTags { .. }
122 | Action::SetShapeTagHidden { .. }
123 | Action::FlipShapePins(_)
124 | Action::FlipBlockVertical(_)
125 | Action::SetBlockLocked { .. }
126 | Action::Reroute(_)
127 | Action::RerouteBlock(_)
128 | Action::SetRole { .. }
129 | Action::SetPinsKind { .. } => {
130 unreachable!("{label} is not the dispatcher's to apply")
131 }
132 }
133 }
134
135 fn set_icon(&mut self, block: BlockId, asset: &Asset, label: &'static str) {
139 let shape = if asset_within_limit(asset, "the picked icon") {
140 self.commit_gesture(Label::verb(label), |d| d.set_icon(block, asset));
141 ShapeId::Icon(block)
142 } else {
143 ShapeId::Rect(block)
144 };
145 self.tool = ResizeBlock::Selected { shape }.into();
146 }
147
148 fn place_picked_image(&mut self, asset: &Asset, label: &'static str) {
152 let placed = asset_within_limit(asset, "the picked image")
153 .then(|| self.place_image(asset, label))
154 .flatten();
155 self.tool = match placed {
156 Some(shape) => ResizeBlock::Selected { shape }.into(),
157 None => Tool::Select(SelectTool),
158 };
159 }
160
161 fn place_image(&mut self, asset: &Asset, label: &'static str) -> Option<ShapeId> {
166 let intrinsic = blockworx_paint::image::image_intrinsic_size(asset)
167 .inspect_err(|_| tracing::error!("Failed to import image: not a valid image"))
168 .ok()?;
169 let placed = assets::Placement::Centered {
170 center: self.paste_target(),
171 intrinsic,
172 };
173 Some(self.commit_gesture(Label::verb(label), |d| d.add_image(placed, asset)))
174 }
175
176 fn zoom(&mut self, step: ZoomStep) {
179 let pointer = self.pointer_screen();
180 self.zoom_step(step, pointer);
181 }
182
183 fn copied(&mut self, clip: Option<blockworx_editor::edit::clipboard::Clipboard>) {
185 if let Some(json) = clip.and_then(|clip| clip.to_json()) {
186 self.hands_back(Handoff::Clipboard(json));
187 }
188 }
189}