1// Aseprite
2// Copyright (C) 2018-2022 Igara Studio S.A.
3// Copyright (C) 2001-2018 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifndef APP_UI_EDITOR_H_INCLUDED
9#define APP_UI_EDITOR_H_INCLUDED
10#pragma once
11
12#include "app/color.h"
13#include "app/doc.h"
14#include "app/doc_observer.h"
15#include "app/pref/preferences.h"
16#include "app/tools/active_tool_observer.h"
17#include "app/tools/tool_loop_modifiers.h"
18#include "app/ui/color_source.h"
19#include "app/ui/editor/brush_preview.h"
20#include "app/ui/editor/editor_hit.h"
21#include "app/ui/editor/editor_observers.h"
22#include "app/ui/editor/editor_state.h"
23#include "app/ui/editor/editor_states_history.h"
24#include "app/ui/tile_source.h"
25#include "app/util/tiled_mode.h"
26#include "doc/algorithm/flip_type.h"
27#include "doc/frame.h"
28#include "doc/image_buffer.h"
29#include "doc/selected_objects.h"
30#include "filters/tiled_mode.h"
31#include "gfx/fwd.h"
32#include "obs/connection.h"
33#include "os/color_space.h"
34#include "render/projection.h"
35#include "render/zoom.h"
36#include "ui/base.h"
37#include "ui/cursor_type.h"
38#include "ui/pointer_type.h"
39#include "ui/timer.h"
40#include "ui/widget.h"
41
42#include <memory>
43#include <set>
44
45namespace doc {
46 class Layer;
47 class Sprite;
48}
49namespace gfx {
50 class Region;
51}
52namespace ui {
53 class Cursor;
54 class Graphics;
55 class View;
56}
57
58namespace app {
59 class Context;
60 class DocView;
61 class EditorCustomizationDelegate;
62 class EditorRender;
63 class PixelsMovement;
64 class Site;
65 class Transformation;
66
67 namespace tools {
68 class Ink;
69 class Pointer;
70 class Tool;
71 }
72
73 enum class AutoScroll {
74 MouseDir,
75 ScrollDir,
76 };
77
78 class Editor : public ui::Widget,
79 public app::DocObserver,
80 public IColorSource,
81 public ITileSource,
82 public tools::ActiveToolObserver {
83 public:
84 enum EditorFlags {
85 kNoneFlag = 0,
86 kShowGrid = 1,
87 kShowMask = 2,
88 kShowOnionskin = 4,
89 kShowOutside = 8,
90 kShowDecorators = 16,
91 kShowSymmetryLine = 32,
92 kShowSlices = 64,
93 kUseNonactiveLayersOpacityWhenEnabled = 128,
94 kDefaultEditorFlags = (kShowGrid |
95 kShowMask |
96 kShowOnionskin |
97 kShowOutside |
98 kShowDecorators |
99 kShowSymmetryLine |
100 kShowSlices |
101 kUseNonactiveLayersOpacityWhenEnabled)
102 };
103
104 enum class ZoomBehavior {
105 CENTER, // Zoom from center (don't change center of the editor)
106 MOUSE, // Zoom from cursor
107 };
108
109 static ui::WidgetType Type();
110
111 Editor(Doc* document,
112 EditorFlags flags = kDefaultEditorFlags,
113 EditorStatePtr state = nullptr);
114 ~Editor();
115
116 static void destroyEditorSharedInternals();
117
118 bool isActive() const;
119 bool isUsingNewRenderEngine() const;
120
121 DocView* getDocView() { return m_docView; }
122 void setDocView(DocView* docView) { m_docView = docView; }
123
124 // Returns the current state.
125 EditorStatePtr getState() { return m_state; }
126
127 bool isMovingPixels() const;
128 void dropMovingPixels();
129
130 // Changes the state of the editor.
131 void setState(const EditorStatePtr& newState);
132
133 // Backs to previous state.
134 void backToPreviousState();
135
136 // Gets/sets the current decorator. The decorator is not owned by
137 // the Editor, so it must be deleted by the caller.
138 EditorDecorator* decorator() { return m_decorator; }
139 void setDecorator(EditorDecorator* decorator) { m_decorator = decorator; }
140 void getInvalidDecoratoredRegion(gfx::Region& region);
141
142 EditorFlags editorFlags() const { return m_flags; }
143 void setEditorFlags(EditorFlags flags) { m_flags = flags; }
144
145 bool isExtraCelLocked() const {
146 return m_flashing != Flashing::None;
147 }
148
149 Doc* document() { return m_document; }
150 Sprite* sprite() { return m_sprite; }
151 Layer* layer() { return m_layer; }
152 frame_t frame() { return m_frame; }
153 DocumentPreferences& docPref() { return m_docPref; }
154
155 void getSite(Site* site) const;
156 Site getSite() const;
157
158 void setLayer(const Layer* layer);
159 void setFrame(frame_t frame);
160
161 const render::Projection& projection() const { return m_proj; }
162 const render::Zoom& zoom() const { return m_proj.zoom(); }
163 const gfx::Point& padding() const { return m_padding; }
164
165 void setZoom(const render::Zoom& zoom);
166 void setDefaultScroll();
167 void setScrollToCenter();
168 void setScrollAndZoomToFitScreen();
169 void setEditorScroll(const gfx::Point& scroll);
170 void setEditorZoom(const render::Zoom& zoom);
171
172 // Updates the Editor's view.
173 void updateEditor(const bool restoreScrollPos);
174
175 // Draws the sprite taking care of the whole clipping region.
176 void drawSpriteClipped(const gfx::Region& updateRegion);
177
178 void flashCurrentLayer();
179
180 // Convert ui::Display coordinates (pixel relative to the top-left
181 // corner of the in the display content bounds) from/to
182 // editor/sprite coordinates (pixel in the canvas).
183 //
184 // TODO we should rename these functions to displayToEditor() and editorToDisplay()
185 gfx::Point screenToEditor(const gfx::Point& pt);
186 gfx::Point screenToEditorCeiling(const gfx::Point& pt);
187 gfx::PointF screenToEditorF(const gfx::Point& pt);
188 gfx::Point editorToScreen(const gfx::Point& pt);
189 gfx::PointF editorToScreenF(const gfx::PointF& pt);
190 gfx::Rect screenToEditor(const gfx::Rect& rc);
191 gfx::Rect editorToScreen(const gfx::Rect& rc);
192 gfx::RectF editorToScreenF(const gfx::RectF& rc);
193
194 void add_observer(EditorObserver* observer);
195 void remove_observer(EditorObserver* observer);
196
197 void setCustomizationDelegate(EditorCustomizationDelegate* delegate);
198
199 EditorCustomizationDelegate* getCustomizationDelegate() {
200 return m_customizationDelegate;
201 }
202
203 // Returns the visible area of the viewport in sprite coordinates.
204 gfx::Rect getViewportBounds();
205
206 // Returns the visible area of the active sprite.
207 gfx::Rect getVisibleSpriteBounds();
208
209 gfx::Size canvasSize() const;
210 gfx::Point mainTilePosition() const;
211 void expandRegionByTiledMode(gfx::Region& rgn,
212 const bool withProj) const;
213 void collapseRegionByTiledMode(gfx::Region& rgn) const;
214
215 // Changes the scroll to see the given point as the center of the editor.
216 void centerInSpritePoint(const gfx::Point& spritePos);
217
218 void updateStatusBar();
219
220 // Control scroll when cursor goes out of the editor viewport.
221 gfx::Point autoScroll(const ui::MouseMessage* msg,
222 const AutoScroll dir);
223
224 tools::Tool* getCurrentEditorTool() const;
225 tools::Ink* getCurrentEditorInk() const;
226
227 tools::ToolLoopModifiers getToolLoopModifiers() const { return m_toolLoopModifiers; }
228 bool isAutoSelectLayer();
229
230 // Returns true if we are able to draw in the current doc/sprite/layer/cel.
231 bool canDraw();
232
233 // Returns true if the cursor is inside the active mask/selection.
234 bool isInsideSelection();
235
236 // Returns true if the cursor is inside the selection and the
237 // selection mode is the default one which prioritizes and easy
238 // way to move the selection.
239 bool canStartMovingSelectionPixels();
240
241 // Returns true if the range selected in the timeline should be
242 // kept. E.g. When we are moving/transforming pixels on multiple
243 // cels, the MovingPixelsState can handle previous/next frame
244 // commands, so it's nice to keep the timeline range intact while
245 // we are in the MovingPixelsState.
246 bool keepTimelineRange();
247
248 // Returns the element that will be modified if the mouse is used
249 // in the given position.
250 EditorHit calcHit(const gfx::Point& mouseScreenPos);
251
252 void setZoomAndCenterInMouse(const render::Zoom& zoom,
253 const gfx::Point& mousePos, ZoomBehavior zoomBehavior);
254
255 void pasteImage(const Image* image, const Mask* mask = nullptr);
256
257 void startSelectionTransformation(const gfx::Point& move, double angle);
258 void startFlipTransformation(doc::algorithm::FlipType flipType);
259 void updateTransformation(const Transformation& transform);
260
261 // Used by EditorView to notify changes in the view's scroll
262 // position.
263 void notifyScrollChanged();
264 void notifyZoomChanged();
265
266 // Returns true and changes to ScrollingState when "msg" says "the
267 // user wants to scroll". Same for zoom.
268 bool checkForScroll(ui::MouseMessage* msg);
269 bool checkForZoom(ui::MouseMessage* msg);
270
271 // Start Scrolling/ZoomingState
272 void startScrollingState(ui::MouseMessage* msg);
273 void startZoomingState(ui::MouseMessage* msg);
274
275 // Animation control
276 void play(const bool playOnce,
277 const bool playAll);
278 void stop();
279 bool isPlaying() const;
280
281 // Shows a popup menu to change the editor animation speed.
282 void showAnimationSpeedMultiplierPopup(Option<bool>& playOnce,
283 Option<bool>& playAll,
284 const bool withStopBehaviorOptions);
285 double getAnimationSpeedMultiplier() const;
286 void setAnimationSpeedMultiplier(double speed);
287
288 // Functions to be used in EditorState::onSetCursor()
289 void showMouseCursor(ui::CursorType cursorType,
290 const ui::Cursor* cursor = nullptr);
291 void showBrushPreview(const gfx::Point& pos);
292
293 // Gets the brush preview controller.
294 BrushPreview& brushPreview() { return m_brushPreview; }
295
296 static EditorRender& renderEngine() { return *m_renderEngine; }
297
298 // IColorSource
299 app::Color getColorByPosition(const gfx::Point& pos) override;
300
301 // ITileSource
302 doc::tile_t getTileByPosition(const gfx::Point& pos) override;
303
304 void setTagFocusBand(int value) { m_tagFocusBand = value; }
305 int tagFocusBand() const { return m_tagFocusBand; }
306
307 // Returns true if the Shift key to draw straight lines with a
308 // freehand tool is pressed.
309 bool startStraightLineWithFreehandTool(const tools::Pointer* pointer);
310
311 // Functions to handle the set of selected slices.
312 bool isSliceSelected(const doc::Slice* slice) const;
313 void clearSlicesSelection();
314 void selectSlice(const doc::Slice* slice);
315 bool selectSliceBox(const gfx::Rect& box);
316 void selectAllSlices();
317 bool hasSelectedSlices() const { return !m_selectedSlices.empty(); }
318
319 // Called by DocView's InputChainElement::onCancel() impl when Esc
320 // key is pressed to cancel the active selection.
321 void cancelSelections();
322
323 // Properties to show information in the status bar
324 bool showAutoCelGuides() const { return m_showAutoCelGuides; }
325
326 static void registerCommands();
327
328 protected:
329 bool onProcessMessage(ui::Message* msg) override;
330 void onSizeHint(ui::SizeHintEvent& ev) override;
331 void onResize(ui::ResizeEvent& ev) override;
332 void onPaint(ui::PaintEvent& ev) override;
333 void onInvalidateRegion(const gfx::Region& region) override;
334 void onSamplingChange();
335 void onFgColorChange();
336 void onContextBarBrushChange();
337 void onTiledModeBeforeChange();
338 void onTiledModeChange();
339 void onShowExtrasChange();
340
341 // DocObserver impl
342 void onColorSpaceChanged(DocEvent& ev) override;
343 void onExposeSpritePixels(DocEvent& ev) override;
344 void onSpritePixelRatioChanged(DocEvent& ev) override;
345 void onBeforeRemoveLayer(DocEvent& ev) override;
346 void onBeforeRemoveCel(DocEvent& ev) override;
347 void onAddTag(DocEvent& ev) override;
348 void onRemoveTag(DocEvent& ev) override;
349 void onRemoveSlice(DocEvent& ev) override;
350
351 // ActiveToolObserver impl
352 void onActiveToolChange(tools::Tool* tool) override;
353
354 private:
355 enum class Flashing { None, WithFlashExtraCel, WaitingDeferedPaint };
356
357 void setStateInternal(const EditorStatePtr& newState);
358 void updateQuicktool();
359 void updateToolByTipProximity(ui::PointerType pointerType);
360
361 // firstFromMouseDown=true when we call this function from the
362 // first MouseDown message (instead of KeyDown).
363 void updateToolLoopModifiersIndicators(const bool firstFromMouseDown = false);
364
365 void drawBackground(ui::Graphics* g);
366 void drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& rc);
367 void drawMaskSafe();
368 void drawMask(ui::Graphics* g);
369 void drawGrid(ui::Graphics* g, const gfx::Rect& spriteBounds, const gfx::Rect& gridBounds,
370 const app::Color& color, int alpha);
371 void drawSlices(ui::Graphics* g);
372 void drawTileNumbers(ui::Graphics* g, const Cel* cel);
373 void drawCelBounds(ui::Graphics* g, const Cel* cel, const gfx::Color color);
374 void drawCelGuides(ui::Graphics* g, const Cel* cel, const Cel* mouseCel);
375 void drawCelHGuide(ui::Graphics* g,
376 const int sprX1, const int sprX2,
377 const int scrX1, const int scrX2, const int scrY,
378 const gfx::Rect& scrCelBounds, const gfx::Rect& scrCmpBounds,
379 const int dottedX);
380 void drawCelVGuide(ui::Graphics* g,
381 const int sprY1, const int sprY2,
382 const int scrY1, const int scrY2, const int scrX,
383 const gfx::Rect& scrCelBounds, const gfx::Rect& scrCmpBounds,
384 const int dottedY);
385 gfx::Rect getCelScreenBounds(const Cel* cel);
386
387 void setCursor(const gfx::Point& mouseDisplayPos);
388
389 // Draws the specified portion of sprite in the editor. Warning:
390 // You should setup the clip of the screen before calling this
391 // routine.
392 void drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& rc, int dx, int dy);
393
394 gfx::Point calcExtraPadding(const render::Projection& proj);
395
396 void invalidateCanvas();
397 void invalidateIfActive();
398 void updateAutoCelGuides(ui::Message* msg);
399
400 // Stack of states. The top element in the stack is the current state (m_state).
401 EditorStatesHistory m_statesHistory;
402 EditorStatesHistory m_deletedStates;
403
404 // Current editor state (it can be shared between several editors to
405 // the same document). This member cannot be NULL.
406 EditorStatePtr m_state;
407
408 // Current decorator (to draw extra UI elements).
409 EditorDecorator* m_decorator;
410
411 Doc* m_document; // Active document in the editor
412 Sprite* m_sprite; // Active sprite in the editor
413 Layer* m_layer; // Active layer in the editor
414 frame_t m_frame; // Active frame in the editor
415 render::Projection m_proj; // Zoom/pixel ratio in the editor
416 DocumentPreferences& m_docPref;
417 // Helper functions affected by the current Tiled Mode.
418 app::TiledModeHelper m_tiledModeHelper;
419
420 // Brush preview
421 BrushPreview m_brushPreview;
422
423 tools::ToolLoopModifiers m_toolLoopModifiers;
424
425 // Extra space around the sprite.
426 gfx::Point m_padding;
427
428 // Marching ants stuff
429 ui::Timer m_antsTimer;
430 int m_antsOffset;
431
432 obs::scoped_connection m_samplingChangeConn;
433 obs::scoped_connection m_fgColorChangeConn;
434 obs::scoped_connection m_contextBarBrushChangeConn;
435 obs::scoped_connection m_showExtrasConn;
436
437 // Slots listeing document preferences.
438 obs::scoped_connection m_tiledConnBefore;
439 obs::scoped_connection m_tiledConn;
440 obs::scoped_connection m_gridConn;
441 obs::scoped_connection m_pixelGridConn;
442 obs::scoped_connection m_bgConn;
443 obs::scoped_connection m_onionskinConn;
444 obs::scoped_connection m_symmetryModeConn;
445
446 EditorObservers m_observers;
447
448 EditorCustomizationDelegate* m_customizationDelegate;
449
450 // TODO This field shouldn't be here. It should be removed when
451 // editors.cpp are finally replaced with a fully funtional Workspace
452 // widget.
453 DocView* m_docView;
454
455 gfx::Point m_oldPos;
456
457 EditorFlags m_flags;
458
459 bool m_secondaryButton;
460 Flashing m_flashing;
461
462 // Animation speed multiplier.
463 double m_aniSpeed;
464 bool m_isPlaying;
465
466 // The Cel that is above the mouse if the Ctrl (or Cmd) key is
467 // pressed (move key).
468 Cel* m_showGuidesThisCel;
469 bool m_showAutoCelGuides;
470
471 // Focused tag band. Used by the Timeline to save/restore the
472 // focused tag band for each sprite/editor.
473 int m_tagFocusBand;
474
475 // Used to restore scroll when the tiled mode is changed.
476 // TODO could we avoid one extra field just to do this?
477 gfx::Point m_oldMainTilePos;
478
479#if ENABLE_DEVMODE
480 gfx::Rect m_perfInfoBounds;
481#endif
482
483 // For slices
484 doc::SelectedObjects m_selectedSlices;
485
486 // The render engine must be shared between all editors so when a
487 // DrawingState is being used in one editor, other editors for the
488 // same document can show the same preview image/stroke being drawn
489 // (search for Render::setPreviewImage()).
490 static std::unique_ptr<EditorRender> m_renderEngine;
491 };
492
493} // namespace app
494
495#endif
496