1 | // Aseprite |
2 | // Copyright (C) 2020-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2017 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/ui/editor/state_with_wheel_behavior.h" |
13 | |
14 | #include "app/app.h" |
15 | #include "app/commands/commands.h" |
16 | #include "app/commands/params.h" |
17 | #include "app/modules/palettes.h" |
18 | #include "app/pref/preferences.h" |
19 | #include "app/site.h" |
20 | #include "app/tools/active_tool.h" |
21 | #include "app/tools/tool_box.h" |
22 | #include "app/ui/color_bar.h" |
23 | #include "app/ui/context_bar.h" |
24 | #include "app/ui/editor/editor.h" |
25 | #include "app/ui/keyboard_shortcuts.h" |
26 | #include "app/ui/toolbar.h" |
27 | #include "app/ui_context.h" |
28 | #include "base/string.h" |
29 | #include "doc/brush.h" |
30 | #include "doc/layer.h" |
31 | #include "doc/palette.h" |
32 | #include "ui/message.h" |
33 | #include "ui/system.h" |
34 | #include "ui/theme.h" |
35 | |
36 | #include "app/tools/ink.h" |
37 | #include "app/ui/skin/skin_theme.h" |
38 | |
39 | namespace app { |
40 | |
41 | using namespace ui; |
42 | using PreciseWheel = StateWithWheelBehavior::PreciseWheel; |
43 | |
44 | template<typename T> |
45 | static inline void adjust_value(PreciseWheel preciseWheel, double dz, T& v, T min, T max) |
46 | { |
47 | if (preciseWheel == PreciseWheel::On) |
48 | v = std::clamp<T>(T(v+dz), min, max); |
49 | else |
50 | v = std::clamp<T>(T(v+dz*max/T(10)), min, max); |
51 | } |
52 | |
53 | template<typename T> |
54 | static inline void adjust_hue(PreciseWheel preciseWheel, double dz, T& v, T min, T max) |
55 | { |
56 | if (preciseWheel == PreciseWheel::On) |
57 | v = std::clamp<T>(T(v+dz), min, max); |
58 | else |
59 | v = std::clamp<T>(T(v+dz*T(10)), min, max); |
60 | } |
61 | |
62 | static inline void adjust_unit(PreciseWheel preciseWheel, double dz, double& v) |
63 | { |
64 | v = std::clamp<double>(v+(preciseWheel == PreciseWheel::On ? dz/100.0: dz/25.0), 0.0, 1.0); |
65 | } |
66 | |
67 | StateWithWheelBehavior::StateWithWheelBehavior() |
68 | { |
69 | } |
70 | |
71 | bool StateWithWheelBehavior::onMouseWheel(Editor* editor, MouseMessage* msg) |
72 | { |
73 | gfx::Point delta = msg->wheelDelta(); |
74 | double dz = delta.x + delta.y; |
75 | WheelAction wheelAction = WheelAction::None; |
76 | |
77 | if (KeyboardShortcuts::instance()->hasMouseWheelCustomization()) { |
78 | if (!Preferences::instance().editor.zoomWithSlide() && msg->preciseWheel()) |
79 | wheelAction = WheelAction::VScroll; |
80 | else |
81 | wheelAction = KeyboardShortcuts::instance() |
82 | ->getWheelActionFromMouseMessage(KeyContext::MouseWheel, msg); |
83 | } |
84 | // Default behavior |
85 | // TODO replace this code using KeyboardShortcuts::getDefaultMouseWheelTable() |
86 | else { |
87 | // Alt+mouse wheel changes the fg/bg colors |
88 | if (msg->altPressed()) { |
89 | bool tilemapMode = (editor->getSite().tilemapMode() == TilemapMode::Tiles); |
90 | if (msg->shiftPressed()) |
91 | wheelAction = (tilemapMode ? WheelAction::BgTile : WheelAction::BgColor); |
92 | else |
93 | wheelAction = (tilemapMode ? WheelAction::FgTile : WheelAction::FgColor); |
94 | } |
95 | // Normal behavior: mouse wheel zooms If the message is from a |
96 | // precise wheel i.e. a trackpad/touch-like device, we scroll by |
97 | // default. |
98 | else if (Preferences::instance().editor.zoomWithWheel() && !msg->preciseWheel()) { |
99 | if (msg->ctrlPressed() && msg->shiftPressed()) |
100 | wheelAction = WheelAction::Frame; |
101 | else if (msg->ctrlPressed()) |
102 | wheelAction = WheelAction::BrushSize; |
103 | else if (delta.x != 0 || msg->shiftPressed()) |
104 | wheelAction = WheelAction::HScroll; |
105 | else |
106 | wheelAction = WheelAction::Zoom; |
107 | } |
108 | // Zoom sliding two fingers |
109 | else if (Preferences::instance().editor.zoomWithSlide() && msg->preciseWheel()) { |
110 | if (msg->ctrlPressed() && msg->shiftPressed()) |
111 | wheelAction = WheelAction::Frame; |
112 | else if (msg->ctrlPressed()) |
113 | wheelAction = WheelAction::BrushSize; |
114 | else if (std::abs(delta.x) > std::abs(delta.y)) { |
115 | delta.y = 0; |
116 | dz = delta.x; |
117 | wheelAction = WheelAction::HScroll; |
118 | } |
119 | else if (msg->shiftPressed()) { |
120 | delta.x = 0; |
121 | dz = delta.y; |
122 | wheelAction = WheelAction::VScroll; |
123 | } |
124 | else { |
125 | delta.x = 0; |
126 | dz = delta.y; |
127 | wheelAction = WheelAction::Zoom; |
128 | } |
129 | } |
130 | // For laptops, it's convenient to that Ctrl+wheel zoom (because |
131 | // it's the "pinch" gesture). |
132 | else { |
133 | if (msg->ctrlPressed()) |
134 | wheelAction = WheelAction::Zoom; |
135 | else if (delta.x != 0 || msg->shiftPressed()) |
136 | wheelAction = WheelAction::HScroll; |
137 | else |
138 | wheelAction = WheelAction::VScroll; |
139 | } |
140 | } |
141 | |
142 | processWheelAction(editor, |
143 | wheelAction, |
144 | msg->position(), |
145 | delta, |
146 | dz, |
147 | // The possibility for big scroll steps was lost |
148 | // in history (it was possible using Shift key in |
149 | // very old versions, now Shift is used for |
150 | // horizontal scroll). |
151 | ScrollBigSteps::Off, |
152 | (msg->preciseWheel() ? |
153 | PreciseWheel::On: |
154 | PreciseWheel::Off), |
155 | FromMouseWheel::On); |
156 | return true; |
157 | } |
158 | |
159 | void StateWithWheelBehavior::processWheelAction( |
160 | Editor* editor, |
161 | WheelAction wheelAction, |
162 | const gfx::Point& position, |
163 | gfx::Point delta, |
164 | double dz, |
165 | const ScrollBigSteps scrollBigSteps, |
166 | const PreciseWheel preciseWheel, |
167 | const FromMouseWheel fromMouseWheel) |
168 | { |
169 | switch (wheelAction) { |
170 | |
171 | case WheelAction::None: |
172 | // Do nothing |
173 | break; |
174 | |
175 | case WheelAction::FgColor: { |
176 | int lastIndex = get_current_palette()->size()-1; |
177 | int newIndex = initialFgColor().getIndex() + int(dz); |
178 | newIndex = std::clamp(newIndex, 0, lastIndex); |
179 | changeFgColor(app::Color::fromIndex(newIndex)); |
180 | break; |
181 | } |
182 | |
183 | case WheelAction::BgColor: { |
184 | int lastIndex = get_current_palette()->size()-1; |
185 | int newIndex = initialBgColor().getIndex() + int(dz); |
186 | newIndex = std::clamp(newIndex, 0, lastIndex); |
187 | ColorBar::instance()->setBgColor(app::Color::fromIndex(newIndex)); |
188 | break; |
189 | } |
190 | |
191 | case WheelAction::FgTile: { |
192 | auto tilesView = ColorBar::instance()->getTilesView(); |
193 | int lastIndex = tilesView->tileset()->size()-1; |
194 | int newIndex = initialFgTileIndex() + int(dz); |
195 | newIndex = std::clamp(newIndex, 0, lastIndex); |
196 | ColorBar::instance()->setFgTile(newIndex); |
197 | break; |
198 | } |
199 | |
200 | case WheelAction::BgTile: { |
201 | auto tilesView = ColorBar::instance()->getTilesView(); |
202 | int lastIndex = tilesView->tileset()->size()-1; |
203 | int newIndex = initialBgTileIndex() + int(dz); |
204 | newIndex = std::clamp(newIndex, 0, lastIndex); |
205 | ColorBar::instance()->setBgTile(newIndex); |
206 | break; |
207 | } |
208 | |
209 | case WheelAction::Frame: { |
210 | frame_t deltaFrame = 0; |
211 | if (preciseWheel == PreciseWheel::On) { |
212 | if (dz < 0.0) |
213 | deltaFrame = +1; |
214 | else if (dz > 0.0) |
215 | deltaFrame = -1; |
216 | } |
217 | else { |
218 | deltaFrame = -dz; |
219 | } |
220 | |
221 | frame_t frame = initialFrame(editor) + deltaFrame; |
222 | frame_t nframes = editor->sprite()->totalFrames(); |
223 | while (frame < 0) |
224 | frame += nframes; |
225 | while (frame >= nframes) |
226 | frame -= nframes; |
227 | |
228 | editor->setFrame(frame); |
229 | break; |
230 | } |
231 | |
232 | case WheelAction::Zoom: { |
233 | render::Zoom zoom = initialZoom(editor); |
234 | |
235 | if (preciseWheel == PreciseWheel::On) { |
236 | dz /= 1.5; |
237 | if (dz < -1.0) dz = -1.0; |
238 | else if (dz > 1.0) dz = 1.0; |
239 | } |
240 | |
241 | zoom = render::Zoom::fromLinearScale(zoom.linearScale() - int(dz)); |
242 | |
243 | setZoom(editor, zoom, position); |
244 | break; |
245 | } |
246 | |
247 | case WheelAction::HScroll: |
248 | case WheelAction::VScroll: { |
249 | View* view = View::getView(editor); |
250 | gfx::Point scroll = initialScroll(editor); |
251 | |
252 | if (preciseWheel == PreciseWheel::Off) { |
253 | gfx::Rect vp = view->viewportBounds(); |
254 | |
255 | if (wheelAction == WheelAction::HScroll) { |
256 | delta.x = int(dz * vp.w); |
257 | } |
258 | else { |
259 | delta.y = int(dz * vp.h); |
260 | } |
261 | |
262 | if (scrollBigSteps == ScrollBigSteps::On) { |
263 | delta /= 2; |
264 | } |
265 | else { |
266 | delta /= 10; |
267 | } |
268 | } |
269 | |
270 | editor->setEditorScroll(scroll+delta); |
271 | break; |
272 | } |
273 | |
274 | case WheelAction::BrushSize: { |
275 | tools::Tool* tool = getActiveTool(); |
276 | ToolPreferences::Brush& brush = |
277 | Preferences::instance().tool(tool).brush; |
278 | |
279 | if (fromMouseWheel == FromMouseWheel::On) { |
280 | #if LAF_WINDOWS || LAF_LINUX |
281 | // By default on macOS the mouse wheel is correct, up increase |
282 | // brush size, and down decrease it. But on Windows and Linux |
283 | // it's inverted. |
284 | dz = -dz; |
285 | #endif |
286 | |
287 | // We can configure the mouse wheel for brush size to behave as |
288 | // in previous versions. |
289 | if (Preferences::instance().editor.invertBrushSizeWheel()) |
290 | dz = -dz; |
291 | } |
292 | |
293 | brush.size( |
294 | std::clamp( |
295 | int(initialBrushSize()+dz), |
296 | // If we use the "static const int" member directly here, |
297 | // we'll get a linker error (when compiling without |
298 | // optimizations) because we should need to define the |
299 | // address of these constants in "doc/brush.cpp" |
300 | int(doc::Brush::kMinBrushSize), |
301 | int(doc::Brush::kMaxBrushSize))); |
302 | break; |
303 | } |
304 | |
305 | case WheelAction::BrushAngle: { |
306 | tools::Tool* tool = getActiveTool(); |
307 | ToolPreferences::Brush& brush = |
308 | Preferences::instance().tool(tool).brush; |
309 | |
310 | int angle = initialBrushAngle()+dz; |
311 | while (angle < 0) |
312 | angle += 180; |
313 | angle %= 181; |
314 | |
315 | brush.angle(std::clamp(angle, 0, 180)); |
316 | break; |
317 | } |
318 | |
319 | case WheelAction::ToolSameGroup: { |
320 | const tools::Tool* tool = getInitialToolInActiveGroup(); |
321 | |
322 | auto toolBox = App::instance()->toolBox(); |
323 | std::vector<tools::Tool*> tools; |
324 | for (tools::Tool* t : *toolBox) { |
325 | if (tool->getGroup() == t->getGroup()) |
326 | tools.push_back(t); |
327 | } |
328 | |
329 | auto begin = tools.begin(); |
330 | auto end = tools.end(); |
331 | auto it = std::find(begin, end, tool); |
332 | if (it != end) { |
333 | int i = std::round(dz); |
334 | while (i < 0) { |
335 | ++i; |
336 | if (it == begin) |
337 | it = end; |
338 | --it; |
339 | } |
340 | while (i > 0) { |
341 | --i; |
342 | ++it; |
343 | if (it == end) |
344 | it = begin; |
345 | } |
346 | onToolChange(*it); |
347 | } |
348 | break; |
349 | } |
350 | |
351 | case WheelAction::ToolOtherGroup: { |
352 | const tools::Tool* tool = initialTool(); |
353 | |
354 | auto toolBox = App::instance()->toolBox(); |
355 | auto begin = toolBox->begin_group(); |
356 | auto end = toolBox->end_group(); |
357 | auto it = std::find(begin, end, tool->getGroup()); |
358 | if (it != end) { |
359 | int i = std::round(dz); |
360 | while (i < 0) { |
361 | ++i; |
362 | if (it == begin) |
363 | it = end; |
364 | --it; |
365 | } |
366 | while (i > 0) { |
367 | --i; |
368 | ++it; |
369 | if (it == end) |
370 | it = begin; |
371 | } |
372 | onToolGroupChange(editor, *it); |
373 | } |
374 | break; |
375 | } |
376 | |
377 | case WheelAction::Layer: { |
378 | int deltaLayer = 0; |
379 | if (preciseWheel == PreciseWheel::On) { |
380 | if (dz < 0.0) |
381 | deltaLayer = +1; |
382 | else if (dz > 0.0) |
383 | deltaLayer = -1; |
384 | } |
385 | else { |
386 | deltaLayer = -dz; |
387 | } |
388 | |
389 | const LayerList& layers = browsableLayers(editor); |
390 | layer_t layer = initialLayer(editor) + deltaLayer; |
391 | layer_t nlayers = layers.size(); |
392 | while (layer < 0) |
393 | layer += nlayers; |
394 | while (layer >= nlayers) |
395 | layer -= nlayers; |
396 | |
397 | editor->setLayer(layers[layer]); |
398 | break; |
399 | } |
400 | |
401 | case WheelAction::InkType: { |
402 | int ink = int(initialInkType(editor)); |
403 | int deltaInk = int(dz); |
404 | if (preciseWheel == PreciseWheel::On) { |
405 | if (dz < 0.0) |
406 | deltaInk = +1; |
407 | else if (dz > 0.0) |
408 | deltaInk = -1; |
409 | } |
410 | ink += deltaInk; |
411 | ink = std::clamp(ink, |
412 | int(tools::InkType::FIRST), |
413 | int(tools::InkType::LAST)); |
414 | |
415 | App::instance()->contextBar()->setInkType(tools::InkType(ink)); |
416 | break; |
417 | } |
418 | |
419 | case WheelAction::InkOpacity: { |
420 | int opacity = initialInkOpacity(editor); |
421 | adjust_value(preciseWheel, dz, opacity, 0, 255); |
422 | |
423 | tools::Tool* tool = getActiveTool(); |
424 | auto& toolPref = Preferences::instance().tool(tool); |
425 | toolPref.opacity(opacity); |
426 | break; |
427 | } |
428 | |
429 | case WheelAction::LayerOpacity: { |
430 | Site site = UIContext::instance()->activeSite(); |
431 | if (site.layer() && |
432 | site.layer()->isImage() && |
433 | site.layer()->isEditable()) { |
434 | Command* command = Commands::instance()->byId(CommandId::LayerOpacity()); |
435 | if (command) { |
436 | int opacity = initialLayerOpacity(editor); |
437 | adjust_value(preciseWheel, dz, opacity, 0, 255); |
438 | |
439 | Params params; |
440 | params.set("opacity" , |
441 | base::convert_to<std::string>(opacity).c_str()); |
442 | UIContext::instance()->executeCommand(command, params); |
443 | } |
444 | } |
445 | break; |
446 | } |
447 | |
448 | case WheelAction::CelOpacity: { |
449 | Site site = UIContext::instance()->activeSite(); |
450 | if (site.layer() && |
451 | site.layer()->isImage() && |
452 | site.layer()->isEditable() && |
453 | site.cel()) { |
454 | Command* command = Commands::instance()->byId(CommandId::CelOpacity()); |
455 | if (command) { |
456 | int opacity = initialCelOpacity(editor); |
457 | adjust_value(preciseWheel, dz, opacity, 0, 255); |
458 | |
459 | Params params; |
460 | params.set("opacity" , |
461 | base::convert_to<std::string>(opacity).c_str()); |
462 | UIContext::instance()->executeCommand(command, params); |
463 | } |
464 | } |
465 | break; |
466 | } |
467 | |
468 | case WheelAction::Alpha: { |
469 | disableQuickTool(); |
470 | |
471 | Color c = initialFgColor(); |
472 | int a = c.getAlpha(); |
473 | adjust_value(preciseWheel, dz, a, 0, 255); |
474 | c.setAlpha(a); |
475 | |
476 | changeFgColor(c); |
477 | break; |
478 | } |
479 | |
480 | case WheelAction::HslHue: |
481 | case WheelAction::HslSaturation: |
482 | case WheelAction::HslLightness: { |
483 | disableQuickTool(); |
484 | |
485 | Color c = initialFgColor(); |
486 | double |
487 | h = c.getHslHue(), |
488 | s = c.getHslSaturation(), |
489 | l = c.getHslLightness(); |
490 | switch (wheelAction) { |
491 | case WheelAction::HslHue: |
492 | adjust_hue(preciseWheel, dz, h, 0.0, 360.0); |
493 | break; |
494 | case WheelAction::HslSaturation: |
495 | adjust_unit(preciseWheel, dz, s); |
496 | break; |
497 | case WheelAction::HslLightness: |
498 | adjust_unit(preciseWheel, dz, l); |
499 | break; |
500 | } |
501 | |
502 | changeFgColor(Color::fromHsl(std::clamp(h, 0.0, 360.0), |
503 | std::clamp(s, 0.0, 1.0), |
504 | std::clamp(l, 0.0, 1.0))); |
505 | break; |
506 | } |
507 | |
508 | case WheelAction::HsvHue: |
509 | case WheelAction::HsvSaturation: |
510 | case WheelAction::HsvValue: { |
511 | disableQuickTool(); |
512 | |
513 | Color c = initialFgColor(); |
514 | double |
515 | h = c.getHsvHue(), |
516 | s = c.getHsvSaturation(), |
517 | v = c.getHsvValue(); |
518 | switch (wheelAction) { |
519 | case WheelAction::HsvHue: |
520 | adjust_hue(preciseWheel, dz, h, 0.0, 360.0); |
521 | break; |
522 | case WheelAction::HsvSaturation: |
523 | adjust_unit(preciseWheel, dz, s); |
524 | break; |
525 | case WheelAction::HsvValue: |
526 | adjust_unit(preciseWheel, dz, v); |
527 | break; |
528 | } |
529 | |
530 | changeFgColor(Color::fromHsv(std::clamp(h, 0.0, 360.0), |
531 | std::clamp(s, 0.0, 1.0), |
532 | std::clamp(v, 0.0, 1.0))); |
533 | break; |
534 | } |
535 | |
536 | } |
537 | } |
538 | |
539 | bool StateWithWheelBehavior::onTouchMagnify(Editor* editor, ui::TouchMessage* msg) |
540 | { |
541 | render::Zoom zoom = editor->zoom(); |
542 | zoom = render::Zoom::fromScale( |
543 | zoom.internalScale() + zoom.internalScale() * msg->magnification()); |
544 | |
545 | setZoom(editor, zoom, msg->position()); |
546 | return true; |
547 | } |
548 | |
549 | bool StateWithWheelBehavior::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) |
550 | { |
551 | tools::Ink* ink = editor->getCurrentEditorInk(); |
552 | auto theme = skin::SkinTheme::get(editor); |
553 | |
554 | if (ink) { |
555 | // If the current tool change selection (e.g. rectangular marquee, etc.) |
556 | if (ink->isSelection()) { |
557 | editor->showBrushPreview(mouseScreenPos); |
558 | return true; |
559 | } |
560 | else if (ink->isEyedropper()) { |
561 | editor->showMouseCursor( |
562 | kCustomCursor, theme->cursors.eyedropper()); |
563 | return true; |
564 | } |
565 | else if (ink->isZoom()) { |
566 | editor->showMouseCursor( |
567 | kCustomCursor, theme->cursors.magnifier()); |
568 | return true; |
569 | } |
570 | else if (ink->isScrollMovement()) { |
571 | editor->showMouseCursor(kScrollCursor); |
572 | return true; |
573 | } |
574 | else if (ink->isCelMovement()) { |
575 | editor->showMouseCursor(kMoveCursor); |
576 | return true; |
577 | } |
578 | } |
579 | |
580 | // Draw |
581 | if (editor->canDraw()) { |
582 | editor->showBrushPreview(mouseScreenPos); |
583 | } |
584 | // Forbidden |
585 | else { |
586 | editor->showMouseCursor(kForbiddenCursor); |
587 | } |
588 | |
589 | return true; |
590 | } |
591 | |
592 | void StateWithWheelBehavior::setZoom(Editor* editor, |
593 | const render::Zoom& zoom, |
594 | const gfx::Point& mousePos) |
595 | { |
596 | bool center = Preferences::instance().editor.zoomFromCenterWithWheel(); |
597 | |
598 | editor->setZoomAndCenterInMouse( |
599 | zoom, mousePos, |
600 | (center ? Editor::ZoomBehavior::CENTER: |
601 | Editor::ZoomBehavior::MOUSE)); |
602 | } |
603 | |
604 | Color StateWithWheelBehavior::initialFgColor() const |
605 | { |
606 | return ColorBar::instance()->getFgColor(); |
607 | } |
608 | |
609 | Color StateWithWheelBehavior::initialBgColor() const |
610 | { |
611 | return ColorBar::instance()->getBgColor(); |
612 | } |
613 | |
614 | int StateWithWheelBehavior::initialFgTileIndex() const |
615 | { |
616 | return ColorBar::instance()->getFgTile(); |
617 | } |
618 | |
619 | int StateWithWheelBehavior::initialBgTileIndex() const |
620 | { |
621 | return ColorBar::instance()->getBgTile(); |
622 | } |
623 | |
624 | int StateWithWheelBehavior::initialBrushSize() |
625 | { |
626 | tools::Tool* tool = getActiveTool(); |
627 | ToolPreferences::Brush& brush = |
628 | Preferences::instance().tool(tool).brush; |
629 | |
630 | return brush.size(); |
631 | } |
632 | |
633 | int StateWithWheelBehavior::initialBrushAngle() |
634 | { |
635 | tools::Tool* tool = getActiveTool(); |
636 | ToolPreferences::Brush& brush = |
637 | Preferences::instance().tool(tool).brush; |
638 | |
639 | return brush.angle(); |
640 | } |
641 | |
642 | gfx::Point StateWithWheelBehavior::initialScroll(Editor* editor) const |
643 | { |
644 | View* view = View::getView(editor); |
645 | return view->viewScroll(); |
646 | } |
647 | |
648 | render::Zoom StateWithWheelBehavior::initialZoom(Editor* editor) const |
649 | { |
650 | return editor->zoom(); |
651 | } |
652 | |
653 | doc::frame_t StateWithWheelBehavior::initialFrame(Editor* editor) const |
654 | { |
655 | return editor->frame(); |
656 | } |
657 | |
658 | doc::layer_t StateWithWheelBehavior::initialLayer(Editor* editor) const |
659 | { |
660 | return doc::find_layer_index(browsableLayers(editor), editor->layer()); |
661 | } |
662 | |
663 | tools::InkType StateWithWheelBehavior::initialInkType(Editor* editor) const |
664 | { |
665 | tools::Tool* tool = getActiveTool(); |
666 | auto& toolPref = Preferences::instance().tool(tool); |
667 | return toolPref.ink(); |
668 | } |
669 | |
670 | int StateWithWheelBehavior::initialInkOpacity(Editor* editor) const |
671 | { |
672 | tools::Tool* tool = getActiveTool(); |
673 | auto& toolPref = Preferences::instance().tool(tool); |
674 | return toolPref.opacity(); |
675 | } |
676 | |
677 | int StateWithWheelBehavior::initialCelOpacity(Editor* editor) const |
678 | { |
679 | doc::Layer* layer = editor->layer(); |
680 | if (layer && |
681 | layer->isImage() && |
682 | layer->isEditable()) { |
683 | if (Cel* cel = layer->cel(editor->frame())) |
684 | return cel->opacity(); |
685 | } |
686 | return 0; |
687 | } |
688 | |
689 | int StateWithWheelBehavior::initialLayerOpacity(Editor* editor) const |
690 | { |
691 | doc::Layer* layer = editor->layer(); |
692 | if (layer && |
693 | layer->isImage() && |
694 | layer->isEditable()) { |
695 | return static_cast<doc::LayerImage*>(layer)->opacity(); |
696 | } |
697 | else |
698 | return 0; |
699 | } |
700 | |
701 | tools::Tool* StateWithWheelBehavior::initialTool() const |
702 | { |
703 | return getActiveTool(); |
704 | } |
705 | |
706 | void StateWithWheelBehavior::changeFgColor(Color c) |
707 | { |
708 | ColorBar::instance()->setFgColor(c); |
709 | } |
710 | |
711 | tools::Tool* StateWithWheelBehavior::getActiveTool() const |
712 | { |
713 | disableQuickTool(); |
714 | return App::instance()->activeToolManager()->activeTool(); |
715 | } |
716 | |
717 | const doc::LayerList& StateWithWheelBehavior::browsableLayers(Editor* editor) const |
718 | { |
719 | if (m_browsableLayers.empty()) |
720 | m_browsableLayers = editor->sprite()->allBrowsableLayers(); |
721 | return m_browsableLayers; |
722 | } |
723 | |
724 | void StateWithWheelBehavior::disableQuickTool() const |
725 | { |
726 | auto atm = App::instance()->activeToolManager(); |
727 | if (atm->quickTool()) { |
728 | // As Ctrl key could active the Move tool, and Ctrl+mouse wheel can |
729 | // change the size of the tool, we want to remove the quick tool so |
730 | // the effect is for the selected tool. |
731 | atm->newQuickToolSelectedFromEditor(nullptr); |
732 | } |
733 | } |
734 | |
735 | tools::Tool* StateWithWheelBehavior::getInitialToolInActiveGroup() |
736 | { |
737 | if (!m_tool) |
738 | m_tool = initialTool(); |
739 | return m_tool; |
740 | } |
741 | |
742 | void StateWithWheelBehavior::onToolChange(tools::Tool* tool) |
743 | { |
744 | ToolBar::instance()->selectTool(tool); |
745 | m_tool = tool; |
746 | } |
747 | |
748 | void StateWithWheelBehavior::onToolGroupChange(Editor* editor, |
749 | tools::ToolGroup* group) |
750 | { |
751 | ToolBar::instance()->selectToolGroup(group); |
752 | |
753 | // Update initial tool in active group as the group has just |
754 | // changed. Useful when the same key modifiers are associated to |
755 | // WheelAction::ToolSameGroup and WheelAction::ToolOtherGroup at the |
756 | // same time. |
757 | m_tool = getActiveTool(); |
758 | } |
759 | |
760 | } // namespace app |
761 | |