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_TIMELINE_TIMELINE_H_INCLUDED |
9 | #define APP_UI_TIMELINE_TIMELINE_H_INCLUDED |
10 | #pragma once |
11 | |
12 | #include "app/doc_observer.h" |
13 | #include "app/doc_range.h" |
14 | #include "app/docs_observer.h" |
15 | #include "app/loop_tag.h" |
16 | #include "app/pref/preferences.h" |
17 | #include "app/ui/editor/editor_observer.h" |
18 | #include "app/ui/input_chain_element.h" |
19 | #include "app/ui/timeline/ani_controls.h" |
20 | #include "app/ui/timeline/timeline_observer.h" |
21 | #include "base/debug.h" |
22 | #include "doc/frame.h" |
23 | #include "doc/layer.h" |
24 | #include "doc/object_version.h" |
25 | #include "doc/selected_frames.h" |
26 | #include "doc/selected_layers.h" |
27 | #include "doc/sprite.h" |
28 | #include "doc/tag.h" |
29 | #include "gfx/color.h" |
30 | #include "obs/connection.h" |
31 | #include "obs/observable.h" |
32 | #include "ui/scroll_bar.h" |
33 | #include "ui/timer.h" |
34 | #include "ui/widget.h" |
35 | |
36 | #include <memory> |
37 | #include <vector> |
38 | |
39 | namespace doc { |
40 | class Cel; |
41 | class Layer; |
42 | class LayerImage; |
43 | class Sprite; |
44 | } |
45 | |
46 | namespace ui { |
47 | class Graphics; |
48 | class TooltipManager; |
49 | } |
50 | |
51 | namespace app { |
52 | |
53 | namespace skin { |
54 | class SkinTheme; |
55 | } |
56 | |
57 | using namespace doc; |
58 | |
59 | class CommandExecutionEvent; |
60 | class ; |
61 | class Context; |
62 | class Doc; |
63 | class Editor; |
64 | |
65 | class Timeline : public ui::Widget, |
66 | public ui::ScrollableViewDelegate, |
67 | public obs::observable<TimelineObserver>, |
68 | public ContextObserver, |
69 | public DocsObserver, |
70 | public DocObserver, |
71 | public EditorObserver, |
72 | public InputChainElement, |
73 | public TagProvider { |
74 | public: |
75 | typedef DocRange Range; |
76 | |
77 | enum State { |
78 | STATE_STANDBY, |
79 | STATE_SCROLLING, |
80 | STATE_SELECTING_LAYERS, |
81 | STATE_SELECTING_FRAMES, |
82 | STATE_SELECTING_CELS, |
83 | STATE_MOVING_SEPARATOR, |
84 | STATE_MOVING_RANGE, |
85 | STATE_MOVING_ONIONSKIN_RANGE_LEFT, |
86 | STATE_MOVING_ONIONSKIN_RANGE_RIGHT, |
87 | STATE_MOVING_TAG, |
88 | STATE_RESIZING_TAG_LEFT, |
89 | STATE_RESIZING_TAG_RIGHT, |
90 | // Changing layers flags states |
91 | STATE_SHOWING_LAYERS, |
92 | STATE_HIDING_LAYERS, |
93 | STATE_LOCKING_LAYERS, |
94 | STATE_UNLOCKING_LAYERS, |
95 | STATE_ENABLING_CONTINUOUS_LAYERS, |
96 | STATE_DISABLING_CONTINUOUS_LAYERS, |
97 | STATE_EXPANDING_LAYERS, |
98 | STATE_COLLAPSING_LAYERS, |
99 | }; |
100 | |
101 | enum DropOp { kMove, kCopy }; |
102 | |
103 | Timeline(ui::TooltipManager* tooltipManager); |
104 | ~Timeline(); |
105 | |
106 | void updateUsingEditor(Editor* editor); |
107 | |
108 | Sprite* sprite() { return m_sprite; } |
109 | Layer* getLayer() { return m_layer; } |
110 | frame_t getFrame() { return m_frame; } |
111 | |
112 | State getState() const { return m_state; } |
113 | bool isMovingCel() const; |
114 | |
115 | Range range() const { return m_range; } |
116 | const SelectedLayers& selectedLayers() const { return m_range.selectedLayers(); } |
117 | const SelectedFrames& selectedFrames() const { return m_range.selectedFrames(); } |
118 | |
119 | void prepareToMoveRange(); |
120 | void moveRange(const Range& range); |
121 | void setRange(const Range& range); |
122 | |
123 | void activateClipboardRange(); |
124 | |
125 | // Drag-and-drop operations. These actions are used by commands |
126 | // called from popup menus. |
127 | void dropRange(DropOp op); |
128 | |
129 | // TagProvider impl |
130 | // Returns the active frame tag depending on the timeline status |
131 | // E.g. if other frame tags are collapsed, the focused band has |
132 | // priority and tags in other bands are ignored. |
133 | Tag* getTagByFrame(const frame_t frame, |
134 | const bool getLoopTagIfNone) override; |
135 | |
136 | // ScrollableViewDelegate impl |
137 | gfx::Size visibleSize() const override; |
138 | gfx::Point viewScroll() const override; |
139 | void setViewScroll(const gfx::Point& pt) override; |
140 | |
141 | void lockRange(); |
142 | void unlockRange(); |
143 | |
144 | void clearAndInvalidateRange(); |
145 | |
146 | protected: |
147 | bool onProcessMessage(ui::Message* msg) override; |
148 | void onInitTheme(ui::InitThemeEvent& ev) override; |
149 | void onInvalidateRegion(const gfx::Region& region) override; |
150 | void onSizeHint(ui::SizeHintEvent& ev) override; |
151 | void onResize(ui::ResizeEvent& ev) override; |
152 | void onPaint(ui::PaintEvent& ev) override; |
153 | |
154 | // DocObserver impl. |
155 | void onGeneralUpdate(DocEvent& ev) override; |
156 | void onAddLayer(DocEvent& ev) override; |
157 | void onBeforeRemoveLayer(DocEvent& ev) override; |
158 | void onAfterRemoveLayer(DocEvent& ev) override; |
159 | void onAddFrame(DocEvent& ev) override; |
160 | void onRemoveFrame(DocEvent& ev) override; |
161 | void onAddCel(DocEvent& ev) override; |
162 | void onAfterRemoveCel(DocEvent& ev) override; |
163 | void onLayerNameChange(DocEvent& ev) override; |
164 | void onAddTag(DocEvent& ev) override; |
165 | void onRemoveTag(DocEvent& ev) override; |
166 | void onTagChange(DocEvent& ev) override; |
167 | void onTagRename(DocEvent& ev) override; |
168 | |
169 | // app::Context slots. |
170 | void onBeforeCommandExecution(CommandExecutionEvent& ev); |
171 | void onAfterCommandExecution(CommandExecutionEvent& ev); |
172 | |
173 | // ContextObserver impl |
174 | void onActiveSiteChange(const Site& site) override; |
175 | |
176 | // DocsObserver impl. |
177 | void onRemoveDocument(Doc* document) override; |
178 | |
179 | // EditorObserver impl. |
180 | void onStateChanged(Editor* editor) override; |
181 | void onAfterFrameChanged(Editor* editor) override; |
182 | void onAfterLayerChanged(Editor* editor) override; |
183 | void onDestroyEditor(Editor* editor) override; |
184 | |
185 | // InputChainElement impl |
186 | void onNewInputPriority(InputChainElement* element, |
187 | const ui::Message* msg) override; |
188 | bool onCanCut(Context* ctx) override; |
189 | bool onCanCopy(Context* ctx) override; |
190 | bool onCanPaste(Context* ctx) override; |
191 | bool onCanClear(Context* ctx) override; |
192 | bool onCut(Context* ctx) override; |
193 | bool onCopy(Context* ctx) override; |
194 | bool onPaste(Context* ctx) override; |
195 | bool onClear(Context* ctx) override; |
196 | void onCancel(Context* ctx) override; |
197 | |
198 | private: |
199 | struct DrawCelData; |
200 | |
201 | struct Hit { |
202 | int part; |
203 | layer_t layer; |
204 | frame_t frame; |
205 | ObjectId tag; |
206 | bool veryBottom; |
207 | int band; |
208 | |
209 | Hit(int part = 0, |
210 | layer_t layer = -1, |
211 | frame_t frame = 0, |
212 | ObjectId tag = NullId, |
213 | int band = -1); |
214 | bool operator!=(const Hit& other) const; |
215 | Tag* getTag() const; |
216 | }; |
217 | |
218 | struct DropTarget { |
219 | enum HHit { |
220 | HNone, |
221 | Before, |
222 | After |
223 | }; |
224 | enum VHit { |
225 | VNone, |
226 | Bottom, |
227 | Top, |
228 | FirstChild, |
229 | VeryBottom |
230 | }; |
231 | DropTarget(); |
232 | DropTarget(const DropTarget& o); |
233 | bool operator!=(const DropTarget& o) const { |
234 | return (hhit != o.hhit || |
235 | vhit != o.vhit || |
236 | outside != o.outside); |
237 | } |
238 | HHit hhit; |
239 | VHit vhit; |
240 | bool outside; |
241 | }; |
242 | |
243 | struct Row { |
244 | Row(); |
245 | Row(Layer* layer, |
246 | const int level, |
247 | const LayerFlags inheritedFlags); |
248 | |
249 | Layer* layer() const { return m_layer; } |
250 | int level() const { return m_level; } |
251 | |
252 | bool parentVisible() const; |
253 | bool parentEditable() const; |
254 | |
255 | private: |
256 | Layer* m_layer; |
257 | int m_level; |
258 | LayerFlags m_inheritedFlags; |
259 | }; |
260 | |
261 | bool selectedLayersBounds(const SelectedLayers& layers, |
262 | layer_t* first, layer_t* last) const; |
263 | |
264 | void setLayer(Layer* layer); |
265 | void setFrame(frame_t frame, bool byUser); |
266 | bool allLayersVisible(); |
267 | bool allLayersInvisible(); |
268 | bool allLayersLocked(); |
269 | bool allLayersUnlocked(); |
270 | bool allLayersContinuous(); |
271 | bool allLayersDiscontinuous(); |
272 | void detachDocument(); |
273 | void setCursor(ui::Message* msg, const Hit& hit); |
274 | void getDrawableLayers(layer_t* firstLayer, layer_t* lastLayer); |
275 | void getDrawableFrames(frame_t* firstFrame, frame_t* lastFrame); |
276 | void drawPart(ui::Graphics* g, const gfx::Rect& bounds, |
277 | const std::string* text, |
278 | ui::Style* style, |
279 | const bool is_active = false, |
280 | const bool is_hover = false, |
281 | const bool is_clicked = false, |
282 | const bool is_disabled = false); |
283 | void drawTop(ui::Graphics* g); |
284 | void (ui::Graphics* g); |
285 | void (ui::Graphics* g, frame_t frame); |
286 | void drawLayer(ui::Graphics* g, layer_t layerIdx); |
287 | void drawCel(ui::Graphics* g, layer_t layerIdx, frame_t frame, Cel* cel, DrawCelData* data); |
288 | void drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds, |
289 | Cel* cel, frame_t frame, bool is_active, bool is_hover, |
290 | DrawCelData* data); |
291 | void drawTags(ui::Graphics* g); |
292 | void drawTagBraces(ui::Graphics* g, |
293 | gfx::Color tagColor, |
294 | const gfx::Rect& bounds, |
295 | const gfx::Rect& clipBounds); |
296 | void drawRangeOutline(ui::Graphics* g); |
297 | void drawPaddings(ui::Graphics* g); |
298 | bool drawPart(ui::Graphics* g, int part, layer_t layer, frame_t frame); |
299 | void drawClipboardRange(ui::Graphics* g); |
300 | gfx::Rect () const; |
301 | gfx::Rect () const; |
302 | gfx::Rect getOnionskinFramesBounds() const; |
303 | gfx::Rect getCelsBounds() const; |
304 | gfx::Rect getPartBounds(const Hit& hit) const; |
305 | gfx::Rect getRangeBounds(const Range& range) const; |
306 | gfx::Rect getRangeClipBounds(const Range& range) const; |
307 | void invalidateHit(const Hit& hit); |
308 | void invalidateLayer(const Layer* layer); |
309 | void invalidateFrame(const frame_t frame); |
310 | void invalidateRange(); |
311 | void regenerateRows(); |
312 | void regenerateTagBands(); |
313 | int visibleTagBands() const; |
314 | void updateScrollBars(); |
315 | void updateByMousePos(ui::Message* msg, const gfx::Point& mousePos); |
316 | Hit hitTest(ui::Message* msg, const gfx::Point& mousePos); |
317 | Hit hitTestCel(const gfx::Point& mousePos); |
318 | void setHot(const Hit& hit); |
319 | void showCel(layer_t layer, frame_t frame); |
320 | void showCurrentCel(); |
321 | void focusTagBand(int band); |
322 | void cleanClk(); |
323 | gfx::Size getScrollableSize() const; |
324 | gfx::Point getMaxScrollablePos() const; |
325 | layer_t getLayerIndex(const Layer* layer) const; |
326 | bool isLayerActive(const layer_t layerIdx) const; |
327 | bool isFrameActive(const frame_t frame) const; |
328 | bool isCelActive(const layer_t layerIdx, const frame_t frame) const; |
329 | bool isCelLooselyActive(const layer_t layerIdx, const frame_t frame) const; |
330 | void updateStatusBar(ui::Message* msg); |
331 | void updateStatusBarForFrame(const frame_t frame, |
332 | const Tag* tag, |
333 | const Cel* cel); |
334 | void updateDropRange(const gfx::Point& pt); |
335 | void clearClipboardRange(); |
336 | |
337 | // The layer of the bottom (e.g. Background layer) |
338 | layer_t firstLayer() const { return 0; } |
339 | // The layer of the top. |
340 | layer_t lastLayer() const { return m_rows.size()-1; } |
341 | |
342 | frame_t firstFrame() const { return frame_t(0); } |
343 | frame_t lastFrame() const { return m_sprite->lastFrame(); } |
344 | |
345 | bool validLayer(layer_t layer) const { return layer >= firstLayer() && layer <= lastLayer(); } |
346 | bool validFrame(frame_t frame) const { return frame >= firstFrame() && frame <= lastFrame(); } |
347 | |
348 | int topHeight() const; |
349 | |
350 | DocumentPreferences& docPref() const; |
351 | |
352 | // Theme/dimensions |
353 | skin::SkinTheme* skinTheme() const; |
354 | gfx::Size celBoxSize() const; |
355 | int () const; |
356 | int () const; |
357 | int layerBoxHeight() const; |
358 | int frameBoxWidth() const; |
359 | int outlineWidth() const; |
360 | int oneTagHeight() const; |
361 | int calcTagVisibleToFrame(Tag* tag) const; |
362 | |
363 | void updateCelOverlayBounds(const Hit& hit); |
364 | void drawCelOverlay(ui::Graphics* g); |
365 | void onThumbnailsPrefChange(); |
366 | void setZoom(const double zoom); |
367 | void setZoomAndUpdate(const double zoom, |
368 | const bool updatePref); |
369 | |
370 | double zoom() const; |
371 | int tagFramesDuration(const Tag* tag) const; |
372 | // Calculate the duration of the selected range of frames |
373 | int selectedFramesDuration() const; |
374 | |
375 | void setLayerVisibleFlag(const layer_t layer, const bool state); |
376 | void setLayerEditableFlag(const layer_t layer, const bool state); |
377 | void setLayerContinuousFlag(const layer_t layer, const bool state); |
378 | void setLayerCollapsedFlag(const layer_t layer, const bool state); |
379 | |
380 | int separatorX() const; |
381 | void setSeparatorX(int newValue); |
382 | |
383 | static gfx::Color highlightColor(const gfx::Color color); |
384 | |
385 | ui::ScrollBar m_hbar; |
386 | ui::ScrollBar m_vbar; |
387 | gfx::Rect m_viewportArea; |
388 | double m_zoom; |
389 | Context* m_context; |
390 | Editor* m_editor; |
391 | Doc* m_document; |
392 | Sprite* m_sprite; |
393 | Layer* m_layer; |
394 | frame_t m_frame; |
395 | int m_rangeLocks; |
396 | Range m_range; |
397 | Range m_startRange; |
398 | Range m_dropRange; |
399 | State m_state; |
400 | |
401 | // Version of the sprite before executing a command. Used to check |
402 | // if the sprite was modified after executing a command to avoid |
403 | // regenerating all rows if it's not necessary. |
404 | doc::ObjectVersion m_savedVersion; |
405 | |
406 | // Data used to display each row in the timeline |
407 | std::vector<Row> m_rows; |
408 | |
409 | // Data used to display frame tags |
410 | int m_tagBands; |
411 | int m_tagFocusBand; |
412 | std::map<Tag*, int> m_tagBand; |
413 | |
414 | int m_separator_x; |
415 | int m_separator_w; |
416 | int m_origFrames; |
417 | Hit m_hot; // The 'hot' part is where the mouse is on top of |
418 | DropTarget m_dropTarget; |
419 | Hit m_clk; // The 'clk' part is where the mouse's button was pressed (maybe for a drag & drop operation) |
420 | // Absolute mouse positions for scrolling. |
421 | gfx::Point m_oldPos; |
422 | // Configure timeline |
423 | std::unique_ptr<ConfigureTimelinePopup> ; |
424 | obs::scoped_connection m_ctxConn1, m_ctxConn2; |
425 | obs::connection m_firstFrameConn; |
426 | |
427 | // Marching ants stuff to show the range in the clipboard. |
428 | // TODO merge this with the marching ants of the sprite editor (ui::Editor) |
429 | ui::Timer m_clipboard_timer; |
430 | int m_offset_count; |
431 | bool m_redrawMarchingAntsOnly; |
432 | |
433 | bool m_scroll; // True if the drag-and-drop operation is a scroll operation. |
434 | bool m_copy; // True if the drag-and-drop operation is a copy. |
435 | bool m_fromTimeline; |
436 | |
437 | AniControls m_aniControls; |
438 | |
439 | // Data used for thumbnails. |
440 | bool m_thumbnailsOverlayVisible; |
441 | gfx::Rect m_thumbnailsOverlayBounds; |
442 | Hit m_thumbnailsOverlayHit; |
443 | gfx::Point m_thumbnailsOverlayDirection; |
444 | obs::connection m_thumbnailsPrefConn; |
445 | |
446 | // Temporal data used to move the range. |
447 | struct MoveRange { |
448 | layer_t activeRelativeLayer; |
449 | frame_t activeRelativeFrame; |
450 | } m_moveRangeData; |
451 | |
452 | // Temporal data used to move tags. |
453 | struct ResizeTag { |
454 | doc::ObjectId tag = doc::NullId; |
455 | doc::frame_t from, to; |
456 | void reset() { |
457 | tag = doc::NullId; |
458 | } |
459 | void reset(const doc::ObjectId tagId) { |
460 | auto tag = doc::get<doc::Tag>(tagId); |
461 | if (tag) { |
462 | this->tag = tagId; |
463 | this->from = tag->fromFrame(); |
464 | this->to = tag->toFrame(); |
465 | } |
466 | else { |
467 | this->tag = doc::NullId; |
468 | } |
469 | } |
470 | } m_resizeTagData; |
471 | }; |
472 | |
473 | #ifdef ENABLE_UI |
474 | class LockTimelineRange { |
475 | public: |
476 | LockTimelineRange(Timeline* timeline) |
477 | : m_timeline(timeline) { |
478 | if (m_timeline) |
479 | m_timeline->lockRange(); |
480 | } |
481 | ~LockTimelineRange() { |
482 | if (m_timeline) |
483 | m_timeline->unlockRange(); |
484 | } |
485 | private: |
486 | Timeline* m_timeline; |
487 | }; |
488 | #else // !ENABLE_UI |
489 | class LockTimelineRange { |
490 | public: |
491 | LockTimelineRange(Timeline* timeline) { } |
492 | }; |
493 | #endif |
494 | |
495 | } // namespace app |
496 | |
497 | #endif |
498 | |