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
39namespace doc {
40 class Cel;
41 class Layer;
42 class LayerImage;
43 class Sprite;
44}
45
46namespace ui {
47 class Graphics;
48 class TooltipManager;
49}
50
51namespace app {
52
53 namespace skin {
54 class SkinTheme;
55 }
56
57 using namespace doc;
58
59 class CommandExecutionEvent;
60 class ConfigureTimelinePopup;
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 drawHeader(ui::Graphics* g);
285 void drawHeaderFrame(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 getLayerHeadersBounds() const;
301 gfx::Rect getFrameHeadersBounds() 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 headerBoxWidth() const;
356 int headerBoxHeight() 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> m_confPopup;
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