mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-30 04:20:23 +00:00
Add the TimelineAdapter concept to abstract frames access (#3904)
We need an extra code layer (view::TimelineAdapter) to convert between old "real frames" (view::fr_t = doc::frame_t) and new "virtual frames" (view::col_t). These new types are strongly-typed ints (enum classes).
This commit is contained in:
parent
b2c6240a7e
commit
8a2df3b01d
@ -62,6 +62,7 @@
|
||||
#include "text/font.h"
|
||||
#include "ui/ui.h"
|
||||
#include "view/layers.h"
|
||||
#include "view/timeline_adapter.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
@ -170,7 +171,7 @@ namespace {
|
||||
|
||||
Timeline::Hit::Hit(int part,
|
||||
layer_t layer,
|
||||
frame_t frame,
|
||||
col_t frame,
|
||||
ObjectId tag,
|
||||
int band)
|
||||
: part(part),
|
||||
@ -239,6 +240,7 @@ bool Timeline::Row::parentEditable() const
|
||||
|
||||
Timeline::Timeline(TooltipManager* tooltipManager)
|
||||
: Widget(kGenericWidget)
|
||||
, m_adapter(nullptr)
|
||||
, m_hbar(HORIZONTAL, this)
|
||||
, m_vbar(VERTICAL, this)
|
||||
, m_zoom(1.0)
|
||||
@ -378,18 +380,11 @@ void Timeline::updateUsingEditor(Editor* editor)
|
||||
docPref.thumbnails.enabled() ?
|
||||
docPref.thumbnails.zoom(): 1.0);
|
||||
|
||||
// If we are already in the same position as the "editor", we don't
|
||||
// need to update the at all timeline.
|
||||
if (m_document == site.document() &&
|
||||
m_sprite == site.sprite() &&
|
||||
m_layer == site.layer() &&
|
||||
m_frame == site.frame())
|
||||
return;
|
||||
|
||||
m_document = site.document();
|
||||
m_sprite = site.sprite();
|
||||
m_layer = site.layer();
|
||||
m_frame = site.frame();
|
||||
m_adapter = std::make_unique<view::FullSpriteTimelineAdapter>(m_sprite);
|
||||
m_frame = m_adapter->toColFrame(fr_t(site.frame()));
|
||||
m_state = STATE_STANDBY;
|
||||
m_hot.part = PART_NOTHING;
|
||||
m_clk.part = PART_NOTHING;
|
||||
@ -437,6 +432,8 @@ void Timeline::detachDocument()
|
||||
m_editor = nullptr;
|
||||
}
|
||||
|
||||
m_adapter.reset();
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ -488,25 +485,23 @@ void Timeline::setLayer(Layer* layer)
|
||||
m_editor->setLayer(m_layer);
|
||||
}
|
||||
|
||||
void Timeline::setFrame(frame_t frame, bool byUser)
|
||||
void Timeline::setFrame(col_t frame, bool byUser)
|
||||
{
|
||||
ASSERT(m_editor != NULL);
|
||||
// ASSERT(frame >= 0 && frame < m_sprite->totalFrames());
|
||||
ASSERT(m_editor);
|
||||
ASSERT(m_adapter);
|
||||
|
||||
if (frame < 0)
|
||||
frame = firstFrame();
|
||||
else if (frame >= m_sprite->totalFrames())
|
||||
frame = frame_t(m_sprite->totalFrames()-1);
|
||||
frame = std::clamp(frame, firstFrame(), lastFrame());
|
||||
|
||||
if (m_layer) {
|
||||
Cel* oldCel = m_layer->cel(m_frame);
|
||||
Cel* newCel = m_layer->cel(frame);
|
||||
Cel* oldCel = m_layer->cel(m_adapter->toRealFrame(m_frame));
|
||||
Cel* newCel = m_layer->cel(m_adapter->toRealFrame(frame));
|
||||
std::size_t oldLinks = (oldCel ? oldCel->links(): 0);
|
||||
std::size_t newLinks = (newCel ? newCel->links(): 0);
|
||||
if ((oldLinks && !newCel) ||
|
||||
(newLinks && !oldCel) ||
|
||||
((oldLinks || newLinks) && (oldCel->data() != newCel->data())))
|
||||
((oldLinks || newLinks) && (oldCel->data() != newCel->data()))) {
|
||||
invalidateLayer(m_layer);
|
||||
}
|
||||
}
|
||||
|
||||
invalidateFrame(m_frame);
|
||||
@ -521,13 +516,14 @@ void Timeline::setFrame(frame_t frame, bool byUser)
|
||||
if (!onionRc.isEmpty())
|
||||
invalidateRect(onionRc.offset(origin()));
|
||||
|
||||
if (m_editor->frame() != frame) {
|
||||
const frame_t realFrame = m_adapter->toRealFrame(m_frame);
|
||||
if (m_editor->frame() != realFrame) {
|
||||
const bool isPlaying = m_editor->isPlaying();
|
||||
|
||||
if (isPlaying)
|
||||
m_editor->stop();
|
||||
|
||||
m_editor->setFrame(m_frame);
|
||||
m_editor->setFrame(realFrame);
|
||||
|
||||
if (isPlaying)
|
||||
m_editor->play(false,
|
||||
@ -547,11 +543,11 @@ void Timeline::prepareToMoveRange()
|
||||
++i;
|
||||
}
|
||||
|
||||
frame_t j = 0;
|
||||
col_t j = col_t(0);
|
||||
for (auto frame : m_range.selectedFrames()) {
|
||||
if (frame == m_frame)
|
||||
break;
|
||||
++j;
|
||||
j = col_t(j+1);
|
||||
}
|
||||
|
||||
m_moveRangeData.activeRelativeLayer = i;
|
||||
@ -577,13 +573,13 @@ void Timeline::moveRange(const Range& range)
|
||||
++i;
|
||||
}
|
||||
|
||||
frame_t j = 0;
|
||||
col_t j = col_t(0);
|
||||
for (auto frame : range.selectedFrames()) {
|
||||
if (j == m_moveRangeData.activeRelativeFrame) {
|
||||
setFrame(frame, true);
|
||||
setFrame(col_t(frame), true);
|
||||
break;
|
||||
}
|
||||
++j;
|
||||
j = col_t(j+1);
|
||||
}
|
||||
|
||||
// Select the range again (it might be lost between all the
|
||||
@ -794,7 +790,8 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
is_select_layer_in_canvas_key_pressed(mouseMsg));
|
||||
|
||||
if (selectLayerInCanvas) {
|
||||
select_layer_boundaries(m_rows[m_clk.layer].layer(), m_frame,
|
||||
select_layer_boundaries(m_rows[m_clk.layer].layer(),
|
||||
m_adapter->toRealFrame(m_frame),
|
||||
get_select_layer_in_canvas_op(mouseMsg));
|
||||
}
|
||||
else if (selectLayer) {
|
||||
@ -926,7 +923,7 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
(m_clk.layer != -1 &&
|
||||
mouseMsg->left() &&
|
||||
is_select_layer_in_canvas_key_pressed(mouseMsg));
|
||||
const frame_t old_frame = m_frame;
|
||||
const col_t old_frame = m_frame;
|
||||
|
||||
if (selectCelInCanvas) {
|
||||
select_layer_boundaries(m_rows[m_clk.layer].layer(),
|
||||
@ -976,25 +973,25 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
}
|
||||
|
||||
if (m_clk.frame < m_range.firstFrame())
|
||||
m_clk.frame = m_range.firstFrame();
|
||||
m_clk.frame = col_t(m_range.firstFrame());
|
||||
else if (m_clk.frame > m_range.lastFrame())
|
||||
m_clk.frame = m_range.lastFrame();
|
||||
m_clk.frame = col_t(m_range.lastFrame());
|
||||
}
|
||||
break;
|
||||
|
||||
case PART_TAG:
|
||||
m_state = STATE_MOVING_TAG;
|
||||
m_resizeTagData.reset(m_clk.tag);
|
||||
m_resizeTagData.reset(*m_adapter, m_clk.tag);
|
||||
break;
|
||||
case PART_TAG_LEFT:
|
||||
m_state = STATE_RESIZING_TAG_LEFT;
|
||||
m_resizeTagData.reset(m_clk.tag);
|
||||
m_resizeTagData.reset(*m_adapter, m_clk.tag);
|
||||
// TODO reduce the scope of the invalidation
|
||||
invalidate();
|
||||
break;
|
||||
case PART_TAG_RIGHT:
|
||||
m_state = STATE_RESIZING_TAG_RIGHT;
|
||||
m_resizeTagData.reset(m_clk.tag);
|
||||
m_resizeTagData.reset(*m_adapter, m_clk.tag);
|
||||
invalidate();
|
||||
break;
|
||||
|
||||
@ -1110,21 +1107,21 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
switch (m_state) {
|
||||
|
||||
case STATE_MOVING_RANGE: {
|
||||
frame_t newFrame;
|
||||
col_t newFrame;
|
||||
if (m_range.type() == Range::kLayers) {
|
||||
// If we are moving only layers we don't change the
|
||||
// current frame.
|
||||
newFrame = m_frame;
|
||||
}
|
||||
else {
|
||||
frame_t firstDrawableFrame;
|
||||
frame_t lastDrawableFrame;
|
||||
col_t firstDrawableFrame;
|
||||
col_t lastDrawableFrame;
|
||||
getDrawableFrames(&firstDrawableFrame, &lastDrawableFrame);
|
||||
|
||||
if (hit.frame < firstDrawableFrame)
|
||||
newFrame = firstDrawableFrame - 1;
|
||||
newFrame = col_t(firstDrawableFrame - 1);
|
||||
else if (hit.frame > lastDrawableFrame)
|
||||
newFrame = lastDrawableFrame + 1;
|
||||
newFrame = col_t(lastDrawableFrame + 1);
|
||||
else
|
||||
newFrame = hit.frame;
|
||||
}
|
||||
@ -1220,10 +1217,12 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
if (tag) {
|
||||
switch (m_state) {
|
||||
case STATE_RESIZING_TAG_LEFT:
|
||||
m_resizeTagData.from = std::clamp(hit.frame, 0, tag->toFrame());
|
||||
m_resizeTagData.from = std::clamp(
|
||||
hit.frame, col_t(0), m_adapter->toColFrame(fr_t(tag->toFrame())));
|
||||
break;
|
||||
case STATE_RESIZING_TAG_RIGHT:
|
||||
m_resizeTagData.to = std::clamp(hit.frame, tag->fromFrame(), m_sprite->lastFrame());
|
||||
m_resizeTagData.to = std::clamp(
|
||||
hit.frame, m_adapter->toColFrame(fr_t(tag->fromFrame())), lastFrame());
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
@ -1601,7 +1600,7 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
void Timeline::handleRangeMouseDown(const ui::Message* msg,
|
||||
const Range::Type rangeType,
|
||||
doc::Layer* fromLayer,
|
||||
const doc::frame_t fromFrame)
|
||||
const col_t fromFrame)
|
||||
{
|
||||
// With Ctrl+click (Win/Linux) or Shift+click (OS X) we can
|
||||
// select non-adjacents layer/frame ranges
|
||||
@ -1640,7 +1639,7 @@ void Timeline::handleRangeMouseDown(const ui::Message* msg,
|
||||
m_range.endRange(m_layer, m_frame);
|
||||
}
|
||||
// Start the range with the clicked fromLayer/Frame position.
|
||||
m_range.startRange(fromLayer, fromFrame, rangeType);
|
||||
m_range.startRange(fromLayer, frame_t(fromFrame), rangeType);
|
||||
m_startRange = m_range;
|
||||
}
|
||||
// If selectOnClick/WithKey are disabled, we start the range on
|
||||
@ -1648,7 +1647,7 @@ void Timeline::handleRangeMouseDown(const ui::Message* msg,
|
||||
// (m_startRange).
|
||||
else if (timelinePref().selectOnDrag()) {
|
||||
m_startRange.clearRange();
|
||||
m_startRange.startRange(fromLayer, fromFrame, rangeType);
|
||||
m_startRange.startRange(fromLayer, frame_t(fromFrame), rangeType);
|
||||
}
|
||||
else {
|
||||
m_startRange = m_range;
|
||||
@ -1658,7 +1657,7 @@ void Timeline::handleRangeMouseDown(const ui::Message* msg,
|
||||
}
|
||||
|
||||
void Timeline::handleRangeMouseMove(doc::Layer* fromLayer,
|
||||
const doc::frame_t fromFrame)
|
||||
const col_t fromFrame)
|
||||
{
|
||||
// Indicate the range end if it's already enabled by the mouse down
|
||||
// event, or in other case, check the if selectOnDrag=true to enable
|
||||
@ -1736,7 +1735,7 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
||||
}
|
||||
|
||||
layer_t layer, firstLayer, lastLayer;
|
||||
frame_t frame, firstFrame, lastFrame;
|
||||
col_t frame, firstFrame, lastFrame;
|
||||
|
||||
getDrawableLayers(&firstLayer, &lastLayer);
|
||||
getDrawableFrames(&firstFrame, &lastFrame);
|
||||
@ -1750,7 +1749,7 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
||||
{
|
||||
IntersectClip clip(g, getFrameHeadersBounds());
|
||||
if (clip) {
|
||||
for (frame=firstFrame; frame<=lastFrame; ++frame)
|
||||
for (frame=firstFrame; frame<=lastFrame; frame=col_t(frame+1))
|
||||
drawHeaderFrame(g, frame);
|
||||
|
||||
// Draw onionskin indicators.
|
||||
@ -1780,7 +1779,7 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
||||
Layer* layerPtr = getLayer(layer);
|
||||
if (!layerPtr || !layerPtr->isImage()) {
|
||||
// Draw empty cels
|
||||
for (frame=firstFrame; frame<=lastFrame; ++frame) {
|
||||
for (frame=firstFrame; frame<=lastFrame; frame=col_t(frame+1)) {
|
||||
drawCel(g, layer, frame, nullptr, nullptr);
|
||||
}
|
||||
continue;
|
||||
@ -1836,7 +1835,7 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
||||
data.activeIt = data.end;
|
||||
|
||||
// Draw every visible cel for each layer.
|
||||
for (frame=firstFrame; frame<=lastFrame; ++frame) {
|
||||
for (frame=firstFrame; frame<=lastFrame; frame=col_t(frame+1)) {
|
||||
Cel* cel =
|
||||
(data.it != data.end &&
|
||||
(*data.it)->frame() == frame ? *data.it: nullptr);
|
||||
@ -1901,8 +1900,10 @@ void Timeline::onAfterCommandExecution(CommandExecutionEvent& ev)
|
||||
|
||||
void Timeline::onActiveSiteChange(const Site& site)
|
||||
{
|
||||
if (hasMouse()) {
|
||||
updateStatusBarForFrame(site.frame(), nullptr, site.cel());
|
||||
if (m_adapter && hasMouse()) {
|
||||
updateStatusBarForFrame(
|
||||
m_adapter->toColFrame(fr_t(site.frame())),
|
||||
nullptr, site.cel());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1961,7 +1962,8 @@ void Timeline::onAfterRemoveLayer(DocEvent& ev)
|
||||
|
||||
void Timeline::onAddFrame(DocEvent& ev)
|
||||
{
|
||||
setFrame(ev.frame(), false);
|
||||
const col_t col = m_adapter->toColFrame(fr_t(ev.frame()));
|
||||
setFrame(col, false);
|
||||
|
||||
regenerateCols();
|
||||
showCurrentCel();
|
||||
@ -1972,16 +1974,22 @@ void Timeline::onAddFrame(DocEvent& ev)
|
||||
// TODO similar to ActiveSiteHandler::onRemoveFrame()
|
||||
void Timeline::onRemoveFrame(DocEvent& ev)
|
||||
{
|
||||
const col_t evCol = m_adapter->toColFrame(fr_t(ev.frame()));
|
||||
|
||||
// In case that the column isn't visible we can just ignore this event
|
||||
if (evCol == kNoCol) {
|
||||
// TODO Check if we can just ignore this
|
||||
}
|
||||
// Adjust current frame of all editors that are in a frame more
|
||||
// advanced that the removed one.
|
||||
if (m_frame > ev.frame()) {
|
||||
setFrame(m_frame-1, false);
|
||||
else if (m_frame > evCol) {
|
||||
setFrame(col_t(m_frame-1), false);
|
||||
}
|
||||
// If the editor was in the previous "last frame" (current value of
|
||||
// totalFrames()), we've to adjust it to the new last frame
|
||||
// (lastFrame())
|
||||
else if (m_frame >= sprite()->totalFrames()) {
|
||||
setFrame(sprite()->lastFrame(), false);
|
||||
else if (m_frame >= m_adapter->totalFrames()) {
|
||||
setFrame(lastFrame(), false);
|
||||
}
|
||||
|
||||
// Disable the selected range when we remove frames
|
||||
@ -2059,7 +2067,7 @@ void Timeline::onAfterFrameChanged(Editor* editor)
|
||||
if (m_fromTimeline)
|
||||
return;
|
||||
|
||||
setFrame(editor->frame(), false);
|
||||
setFrame(m_adapter->toColFrame(fr_t(editor->frame())), false);
|
||||
|
||||
if (!hasCapture() && !editor->keepTimelineRange())
|
||||
clearAndInvalidateRange();
|
||||
@ -2151,7 +2159,7 @@ void Timeline::getDrawableLayers(layer_t* firstDrawableLayer,
|
||||
*lastDrawableLayer = j;
|
||||
}
|
||||
|
||||
void Timeline::getDrawableFrames(frame_t* firstFrame, frame_t* lastFrame)
|
||||
void Timeline::getDrawableFrames(col_t* firstFrame, col_t* lastFrame)
|
||||
{
|
||||
const int availW = (clientBounds().w - m_separator_x);
|
||||
|
||||
@ -2257,7 +2265,7 @@ void Timeline::drawHeader(ui::Graphics* g)
|
||||
NULL, styles.timelineBox(), false, false, false);
|
||||
}
|
||||
|
||||
void Timeline::drawHeaderFrame(ui::Graphics* g, frame_t frame)
|
||||
void Timeline::drawHeaderFrame(ui::Graphics* g, col_t frame)
|
||||
{
|
||||
bool is_active = isFrameActive(frame);
|
||||
bool is_hover = (m_hot.part == PART_HEADER_FRAME && m_hot.frame == frame);
|
||||
@ -2268,7 +2276,8 @@ void Timeline::drawHeaderFrame(ui::Graphics* g, frame_t frame)
|
||||
return;
|
||||
|
||||
// Draw the header for the layers.
|
||||
const int n = (docPref().timeline.firstFrame()+frame);
|
||||
const fr_t realFrame = m_adapter->toRealFrame(frame);
|
||||
const int n = (docPref().timeline.firstFrame() + realFrame);
|
||||
std::string text = base::convert_to<std::string, int>(n % 100);
|
||||
if (n >= 100 && (n % 100) < 10)
|
||||
text.insert(0, 1, '0');
|
||||
@ -2421,7 +2430,7 @@ void Timeline::drawLayer(ui::Graphics* g, const int layerIdx)
|
||||
}
|
||||
|
||||
void Timeline::drawCel(ui::Graphics* g,
|
||||
const layer_t layerIndex, const frame_t frame,
|
||||
const layer_t layerIndex, const col_t frame,
|
||||
const Cel* cel, const DrawCelData* data)
|
||||
{
|
||||
auto& styles = skinTheme()->styles;
|
||||
@ -2608,7 +2617,7 @@ void Timeline::drawCelOverlay(ui::Graphics* g)
|
||||
}
|
||||
|
||||
void Timeline::drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds,
|
||||
const Cel* cel, const frame_t frame,
|
||||
const Cel* cel, const col_t frame,
|
||||
const bool is_active, const bool is_hover,
|
||||
const DrawCelData* data)
|
||||
{
|
||||
@ -2663,7 +2672,7 @@ void Timeline::drawTags(ui::Graphics* g)
|
||||
m_tagBands > 1 &&
|
||||
m_tagFocusBand < 0) {
|
||||
gfx::Rect bandBounds =
|
||||
getPartBounds(Hit(PART_TAG_BAND, -1, 0,
|
||||
getPartBounds(Hit(PART_TAG_BAND, -1, kNoCol,
|
||||
doc::NullId, m_hot.band));
|
||||
g->fillRect(theme->colors.timelineBandHighlight(), bandBounds);
|
||||
}
|
||||
@ -2682,8 +2691,8 @@ void Timeline::drawTags(ui::Graphics* g)
|
||||
}
|
||||
}
|
||||
|
||||
doc::frame_t fromFrame = tag->fromFrame();
|
||||
doc::frame_t toFrame = tag->toFrame();
|
||||
col_t fromFrame = m_adapter->toColFrame(fr_t(tag->fromFrame()));
|
||||
col_t toFrame = m_adapter->toColFrame(fr_t(tag->toFrame()));
|
||||
if (m_resizeTagData.tag == tag->id()) {
|
||||
fromFrame = m_resizeTagData.from;
|
||||
toFrame = m_resizeTagData.to;
|
||||
@ -2692,7 +2701,7 @@ void Timeline::drawTags(ui::Graphics* g)
|
||||
gfx::Rect bounds1 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), fromFrame));
|
||||
gfx::Rect bounds2 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), toFrame));
|
||||
gfx::Rect bounds = bounds1.createUnion(bounds2);
|
||||
gfx::Rect tagBounds = getPartBounds(Hit(PART_TAG, 0, 0, tag->id()));
|
||||
gfx::Rect tagBounds = getPartBounds(Hit(PART_TAG, 0, kNoCol, tag->id()));
|
||||
bounds.h = bounds.y2() - tagBounds.y2();
|
||||
bounds.y = tagBounds.y2();
|
||||
|
||||
@ -2780,7 +2789,7 @@ void Timeline::drawTags(ui::Graphics* g)
|
||||
// Draw button to expand/collapse the active band
|
||||
if (m_hot.band >= 0 && m_tagBands > 1) {
|
||||
gfx::Rect butBounds =
|
||||
getPartBounds(Hit(PART_TAG_SWITCH_BAND_BUTTON, -1, 0,
|
||||
getPartBounds(Hit(PART_TAG_SWITCH_BAND_BUTTON, -1, kNoCol,
|
||||
doc::NullId, m_hot.band));
|
||||
PaintWidgetPartInfo info;
|
||||
if (m_hot.part == PART_TAG_SWITCH_BAND_BUTTON) {
|
||||
@ -2942,8 +2951,8 @@ gfx::Rect Timeline::getOnionskinFramesBounds() const
|
||||
if (!docPref.onionskin.active())
|
||||
return gfx::Rect();
|
||||
|
||||
frame_t firstFrame = m_frame - docPref.onionskin.prevFrames();
|
||||
frame_t lastFrame = m_frame + docPref.onionskin.nextFrames();
|
||||
col_t firstFrame = col_t(m_frame - docPref.onionskin.prevFrames());
|
||||
col_t lastFrame = col_t(m_frame + docPref.onionskin.nextFrames());
|
||||
|
||||
if (firstFrame < this->firstFrame())
|
||||
firstFrame = this->firstFrame();
|
||||
@ -3007,7 +3016,7 @@ gfx::Rect Timeline::getPartBounds(const Hit& hit) const
|
||||
separatorX() - headerBoxWidth()*5, headerBoxHeight());
|
||||
|
||||
case PART_HEADER_FRAME: {
|
||||
frame_t frame = std::max(firstFrame(), hit.frame);
|
||||
col_t frame = std::max(firstFrame(), hit.frame);
|
||||
return gfx::Rect(
|
||||
bounds.x + separatorX() + m_separator_w - 1
|
||||
+ getFrameXPos(frame) - viewScroll().x,
|
||||
@ -3078,8 +3087,8 @@ gfx::Rect Timeline::getPartBounds(const Hit& hit) const
|
||||
case PART_TAG: {
|
||||
Tag* tag = hit.getTag();
|
||||
if (tag) {
|
||||
doc::frame_t fromFrame = tag->fromFrame();
|
||||
doc::frame_t toFrame = tag->toFrame();
|
||||
col_t fromFrame = m_adapter->toColFrame(fr_t(tag->fromFrame()));
|
||||
col_t toFrame = m_adapter->toColFrame(fr_t(tag->toFrame()));
|
||||
if (m_resizeTagData.tag == tag->id()) {
|
||||
fromFrame = m_resizeTagData.from;
|
||||
toFrame = m_resizeTagData.to;
|
||||
@ -3160,14 +3169,14 @@ gfx::Rect Timeline::getRangeBounds(const Range& range) const
|
||||
case Range::kCels:
|
||||
for (auto layer : range.selectedLayers()) {
|
||||
layer_t layerIdx = getLayerIndex(layer);
|
||||
for (auto frame : range.selectedFrames())
|
||||
rc |= getPartBounds(Hit(PART_CEL, layerIdx, frame));
|
||||
for (frame_t frame : range.selectedFrames())
|
||||
rc |= getPartBounds(Hit(PART_CEL, layerIdx, col_t(frame)));
|
||||
}
|
||||
break;
|
||||
case Range::kFrames: {
|
||||
for (auto frame : range.selectedFrames()) {
|
||||
rc |= getPartBounds(Hit(PART_HEADER_FRAME, 0, frame));
|
||||
rc |= getPartBounds(Hit(PART_CEL, 0, frame));
|
||||
for (frame_t frame : range.selectedFrames()) {
|
||||
rc |= getPartBounds(Hit(PART_HEADER_FRAME, 0, col_t(frame)));
|
||||
rc |= getPartBounds(Hit(PART_CEL, 0, col_t(frame)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -3175,7 +3184,7 @@ gfx::Rect Timeline::getRangeBounds(const Range& range) const
|
||||
for (auto layer : range.selectedLayers()) {
|
||||
layer_t layerIdx = getLayerIndex(layer);
|
||||
rc |= getPartBounds(Hit(PART_ROW_TEXT, layerIdx));
|
||||
rc |= getPartBounds(Hit(PART_CEL, layerIdx, m_sprite->lastFrame()));
|
||||
rc |= getPartBounds(Hit(PART_CEL, layerIdx, lastFrame()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -3210,22 +3219,22 @@ gfx::Rect Timeline::getRangeClipBounds(const Range& range) const
|
||||
return clipBounds;
|
||||
}
|
||||
|
||||
int Timeline::getFrameXPos(const frame_t frame) const
|
||||
int Timeline::getFrameXPos(const col_t frame) const
|
||||
{
|
||||
if (frame < 0)
|
||||
return 0;
|
||||
return frame * frameBoxWidth();
|
||||
}
|
||||
|
||||
int Timeline::getFrameWidth(const frame_t frame) const
|
||||
int Timeline::getFrameWidth(const col_t frame) const
|
||||
{
|
||||
(void)frame; // TODO receive the frame future different width per frame.
|
||||
return frameBoxWidth();
|
||||
}
|
||||
|
||||
frame_t Timeline::getFrameInXPos(const int x) const
|
||||
view::col_t Timeline::getFrameInXPos(const int x) const
|
||||
{
|
||||
return frame_t(x / frameBoxWidth());
|
||||
return col_t(x / frameBoxWidth());
|
||||
}
|
||||
|
||||
void Timeline::invalidateHit(const Hit& hit)
|
||||
@ -3258,7 +3267,7 @@ void Timeline::invalidateLayer(const Layer* layer)
|
||||
invalidateRect(rc);
|
||||
}
|
||||
|
||||
void Timeline::invalidateFrame(const frame_t frame)
|
||||
void Timeline::invalidateFrame(const col_t frame)
|
||||
{
|
||||
if (!validFrame(frame))
|
||||
return;
|
||||
@ -3278,8 +3287,8 @@ void Timeline::regenerateCols()
|
||||
ASSERT(m_document);
|
||||
ASSERT(m_sprite);
|
||||
|
||||
m_ncols = m_sprite->totalFrames();
|
||||
m_ncols = std::max(1, m_ncols);
|
||||
m_ncols = m_adapter->totalFrames();
|
||||
m_ncols = std::max(col_t(1), m_ncols);
|
||||
}
|
||||
|
||||
void Timeline::regenerateRows()
|
||||
@ -3392,7 +3401,7 @@ void Timeline::updateByMousePos(ui::Message* msg, const gfx::Point& mousePos)
|
||||
|
||||
Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos)
|
||||
{
|
||||
Hit hit(PART_NOTHING, -1, -1);
|
||||
Hit hit(PART_NOTHING);
|
||||
if (!m_document)
|
||||
return hit;
|
||||
|
||||
@ -3428,7 +3437,7 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos)
|
||||
}
|
||||
else {
|
||||
if (hit.layer > lastLayer()) hit.layer = -1;
|
||||
if (hit.frame > lastFrame()) hit.frame = -1;
|
||||
if (hit.frame > lastFrame()) hit.frame = kNoCol;
|
||||
}
|
||||
|
||||
// Is the mouse over onionskin handles?
|
||||
@ -3446,7 +3455,7 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos)
|
||||
if (m_tagFocusBand < 0) {
|
||||
for (int band=0; band<m_tagBands; ++band) {
|
||||
gfx::Rect bounds = getPartBounds(
|
||||
Hit(PART_TAG_SWITCH_BAND_BUTTON, 0, 0,
|
||||
Hit(PART_TAG_SWITCH_BAND_BUTTON, 0, kNoCol,
|
||||
doc::NullId, band));
|
||||
if (bounds.contains(mousePos)) {
|
||||
hit.part = PART_TAG_SWITCH_BAND_BUTTON;
|
||||
@ -3457,7 +3466,7 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos)
|
||||
}
|
||||
else {
|
||||
gfx::Rect bounds = getPartBounds(
|
||||
Hit(PART_TAG_SWITCH_BAND_BUTTON, 0, 0,
|
||||
Hit(PART_TAG_SWITCH_BAND_BUTTON, 0, kNoCol,
|
||||
doc::NullId, m_tagFocusBand));
|
||||
if (bounds.contains(mousePos)) {
|
||||
hit.part = PART_TAG_SWITCH_BAND_BUTTON;
|
||||
@ -3477,7 +3486,7 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos)
|
||||
continue;
|
||||
}
|
||||
|
||||
gfx::Rect tagBounds = getPartBounds(Hit(PART_TAG, 0, 0, tag->id()));
|
||||
gfx::Rect tagBounds = getPartBounds(Hit(PART_TAG, 0, kNoCol, tag->id()));
|
||||
if (tagBounds.contains(mousePos)) {
|
||||
hit.part = PART_TAG;
|
||||
hit.tag = tag->id();
|
||||
@ -3486,13 +3495,15 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos)
|
||||
}
|
||||
// Check if we are in the left/right handles to resize the tag
|
||||
else {
|
||||
gfx::Rect bounds1 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), tag->fromFrame()));
|
||||
gfx::Rect bounds2 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), tag->toFrame()));
|
||||
gfx::Rect bounds1 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), m_adapter->toColFrame(fr_t(tag->fromFrame()))));
|
||||
gfx::Rect bounds2 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), m_adapter->toColFrame(fr_t(tag->toFrame()))));
|
||||
gfx::Rect bounds = bounds1.createUnion(bounds2);
|
||||
bounds.h = bounds.y2() - tagBounds.y2();
|
||||
bounds.y = tagBounds.y2();
|
||||
|
||||
gfx::Rect bandBounds = getPartBounds(Hit(PART_TAG_BAND, 0, 0, doc::NullId, band));
|
||||
gfx::Rect bandBounds = getPartBounds(
|
||||
Hit(PART_TAG_BAND, 0, kNoCol,
|
||||
doc::NullId, band));
|
||||
|
||||
const int fw = frameBoxWidth()/2;
|
||||
if (gfx::Rect(bounds.x2()-fw, bounds.y, fw, bounds.h).contains(mousePos)) {
|
||||
@ -3521,7 +3532,7 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos)
|
||||
if (m_tagFocusBand < 0) {
|
||||
for (int band=0; band<m_tagBands; ++band) {
|
||||
gfx::Rect bounds = getPartBounds(
|
||||
Hit(PART_TAG_BAND, 0, 0,
|
||||
Hit(PART_TAG_BAND, 0, kNoCol,
|
||||
doc::NullId, band));
|
||||
if (bounds.contains(mousePos)) {
|
||||
hit.part = PART_TAG_BAND;
|
||||
@ -3532,7 +3543,7 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos)
|
||||
}
|
||||
else {
|
||||
gfx::Rect bounds = getPartBounds(
|
||||
Hit(PART_TAG_BAND, 0, 0,
|
||||
Hit(PART_TAG_BAND, 0, kNoCol,
|
||||
doc::NullId, m_tagFocusBand));
|
||||
if (bounds.contains(mousePos)) {
|
||||
hit.part = PART_TAG_BAND;
|
||||
@ -3613,7 +3624,7 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos)
|
||||
|
||||
Timeline::Hit Timeline::hitTestCel(const gfx::Point& mousePos)
|
||||
{
|
||||
Hit hit(PART_NOTHING, -1, -1);
|
||||
Hit hit(PART_NOTHING);
|
||||
if (!m_document)
|
||||
return hit;
|
||||
|
||||
@ -3795,7 +3806,7 @@ void Timeline::updateStatusBar(ui::Message* msg)
|
||||
case PART_HEADER_FRAME:
|
||||
case PART_CEL:
|
||||
case PART_TAG: {
|
||||
frame_t frame = m_frame;
|
||||
col_t frame = m_frame;
|
||||
if (validFrame(m_hot.frame))
|
||||
frame = m_hot.frame;
|
||||
|
||||
@ -3811,7 +3822,7 @@ void Timeline::updateStatusBar(ui::Message* msg)
|
||||
sb->showDefaultText();
|
||||
}
|
||||
|
||||
void Timeline::updateStatusBarForFrame(const frame_t frame,
|
||||
void Timeline::updateStatusBarForFrame(const col_t frame,
|
||||
const Tag* tag,
|
||||
const Cel* cel)
|
||||
{
|
||||
@ -3842,7 +3853,7 @@ void Timeline::updateStatusBarForFrame(const frame_t frame,
|
||||
}
|
||||
|
||||
buf += fmt::format(" :clock: {}",
|
||||
human_readable_time(m_sprite->frameDuration(frame)));
|
||||
human_readable_time(m_adapter->frameDuration(frame)));
|
||||
if (firstFrame != lastFrame) {
|
||||
buf += fmt::format(" [{}]",
|
||||
tag ?
|
||||
@ -3867,7 +3878,7 @@ void Timeline::updateStatusBarForFrame(const frame_t frame,
|
||||
StatusBar::instance()->setStatusText(0, buf);
|
||||
}
|
||||
|
||||
void Timeline::showCel(layer_t layer, frame_t frame)
|
||||
void Timeline::showCel(layer_t layer, col_t frame)
|
||||
{
|
||||
gfx::Point scroll = viewScroll();
|
||||
|
||||
@ -3936,7 +3947,7 @@ gfx::Size Timeline::getScrollableSize() const
|
||||
{
|
||||
if (m_sprite) {
|
||||
return gfx::Size(
|
||||
getFrameXPos(m_sprite->totalFrames()) + getCelsBounds().w/2,
|
||||
getFrameXPos(m_adapter->totalFrames()) + getCelsBounds().w/2,
|
||||
(m_rows.size()+1) * layerBoxHeight());
|
||||
}
|
||||
else
|
||||
@ -4043,15 +4054,15 @@ bool Timeline::isLayerActive(const layer_t layerIndex) const
|
||||
return m_range.contains(layer);
|
||||
}
|
||||
|
||||
bool Timeline::isFrameActive(const frame_t frame) const
|
||||
bool Timeline::isFrameActive(const col_t frame) const
|
||||
{
|
||||
if (frame == m_frame)
|
||||
return true;
|
||||
else
|
||||
return m_range.contains(frame);
|
||||
return m_range.contains(frame_t(frame));
|
||||
}
|
||||
|
||||
bool Timeline::isCelActive(const layer_t layerIdx, const frame_t frame) const
|
||||
bool Timeline::isCelActive(const layer_t layerIdx, const col_t frame) const
|
||||
{
|
||||
Layer* layer = getLayer(layerIdx);
|
||||
if (!layer)
|
||||
@ -4064,7 +4075,7 @@ bool Timeline::isCelActive(const layer_t layerIdx, const frame_t frame) const
|
||||
frame == m_frame);
|
||||
}
|
||||
|
||||
bool Timeline::isCelLooselyActive(const layer_t layerIdx, const frame_t frame) const
|
||||
bool Timeline::isCelLooselyActive(const layer_t layerIdx, const col_t frame) const
|
||||
{
|
||||
Layer* layer = getLayer(layerIdx);
|
||||
if (!layer)
|
||||
@ -4292,7 +4303,7 @@ void Timeline::invalidateRange()
|
||||
for (const Layer* layer : m_range.selectedLayers())
|
||||
invalidateLayer(layer);
|
||||
for (const frame_t frame : m_range.selectedFrames())
|
||||
invalidateFrame(frame);
|
||||
invalidateFrame(col_t(frame));
|
||||
|
||||
invalidateHit(Hit(PART_RANGE_OUTLINE));
|
||||
}
|
||||
@ -4366,14 +4377,19 @@ double Timeline::zoom() const
|
||||
|
||||
// Returns the last frame where the frame tag (or frame tag label)
|
||||
// is visible in the timeline.
|
||||
frame_t Timeline::calcTagVisibleToFrame(Tag* tag) const
|
||||
view::col_t Timeline::calcTagVisibleToFrame(Tag* tag) const
|
||||
{
|
||||
frame_t frame =
|
||||
col_t frame =
|
||||
getFrameInXPos(
|
||||
getFrameXPos(tag->fromFrame()) +
|
||||
getFrameXPos(m_adapter->toColFrame(fr_t(tag->fromFrame()))) +
|
||||
font()->textLength(tag->name()));
|
||||
|
||||
return std::max(frame, tag->toFrame());
|
||||
return std::max(frame, m_adapter->toColFrame(fr_t(tag->toFrame())));
|
||||
}
|
||||
|
||||
view::col_t Timeline::lastFrame() const
|
||||
{
|
||||
return std::max(col_t(0), col_t(m_adapter->totalFrames()-1));
|
||||
}
|
||||
|
||||
int Timeline::topHeight() const
|
||||
@ -4640,22 +4656,24 @@ int Timeline::tagFramesDuration(const Tag* tag) const
|
||||
ASSERT(m_sprite);
|
||||
ASSERT(tag);
|
||||
|
||||
const col_t fromFrame = m_adapter->toColFrame(fr_t(tag->fromFrame()));
|
||||
const col_t toFrame = m_adapter->toColFrame(fr_t(tag->toFrame()));
|
||||
int duration = 0;
|
||||
for (frame_t f=tag->fromFrame();
|
||||
f<=tag->toFrame(); ++f) {
|
||||
duration += m_sprite->frameDuration(f);
|
||||
for (col_t f=fromFrame; f<=toFrame; f=col_t(f+1)) {
|
||||
duration += m_adapter->frameDuration(f);
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
int Timeline::selectedFramesDuration() const
|
||||
{
|
||||
ASSERT(m_sprite);
|
||||
ASSERT(m_adapter);
|
||||
|
||||
const col_t nframes = m_adapter->totalFrames();
|
||||
int duration = 0;
|
||||
for (frame_t f=0; f<m_sprite->totalFrames(); ++f) {
|
||||
for (col_t f = col_t(0); f<nframes; f=col_t(f+1)) {
|
||||
if (isFrameActive(f))
|
||||
duration += m_sprite->frameDuration(f);
|
||||
duration += m_adapter->frameDuration(f);
|
||||
}
|
||||
return duration; // TODO cache this value
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "ui/scroll_bar.h"
|
||||
#include "ui/timer.h"
|
||||
#include "ui/widget.h"
|
||||
#include "view/timeline_adapter.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -73,7 +74,10 @@ namespace app {
|
||||
public InputChainElement,
|
||||
public TagProvider {
|
||||
public:
|
||||
typedef DocRange Range;
|
||||
using Range = DocRange;
|
||||
using fr_t = view::fr_t;
|
||||
using col_t = view::col_t;
|
||||
static constexpr const auto kNoCol = view::kNoCol;
|
||||
|
||||
enum State {
|
||||
STATE_STANDBY,
|
||||
@ -110,6 +114,9 @@ namespace app {
|
||||
|
||||
bool isMovingCel() const;
|
||||
|
||||
// The range is specified in "virtual frames" (not real sprite
|
||||
// frames, we'll have to do a conversion each time we want to use
|
||||
// this range with the sprite).
|
||||
Range range() const { return m_range; }
|
||||
const SelectedLayers& selectedLayers() const { return m_range.selectedLayers(); }
|
||||
const SelectedFrames& selectedFrames() const { return m_range.selectedFrames(); }
|
||||
@ -207,14 +214,14 @@ namespace app {
|
||||
struct Hit {
|
||||
int part;
|
||||
layer_t layer;
|
||||
frame_t frame;
|
||||
col_t frame;
|
||||
ObjectId tag;
|
||||
bool veryBottom;
|
||||
int band;
|
||||
|
||||
Hit(int part = 0,
|
||||
layer_t layer = -1,
|
||||
frame_t frame = 0,
|
||||
col_t frame = kNoCol,
|
||||
ObjectId tag = NullId,
|
||||
int band = -1);
|
||||
bool operator!=(const Hit& other) const;
|
||||
@ -267,16 +274,16 @@ namespace app {
|
||||
void handleRangeMouseDown(const ui::Message* msg,
|
||||
const Range::Type rangeType,
|
||||
doc::Layer* fromLayer,
|
||||
const doc::frame_t fromFrame);
|
||||
const col_t fromFrame);
|
||||
|
||||
void handleRangeMouseMove(doc::Layer* fromLayer,
|
||||
const doc::frame_t fromFrame);
|
||||
const col_t fromFrame);
|
||||
|
||||
bool selectedLayersBounds(const SelectedLayers& layers,
|
||||
layer_t* first, layer_t* last) const;
|
||||
|
||||
void setLayer(Layer* layer);
|
||||
void setFrame(frame_t frame, bool byUser);
|
||||
void setFrame(col_t frame, bool byUser);
|
||||
bool allLayersVisible();
|
||||
bool allLayersInvisible();
|
||||
bool allLayersLocked();
|
||||
@ -286,7 +293,7 @@ namespace app {
|
||||
void detachDocument();
|
||||
void setCursor(ui::Message* msg, const Hit& hit);
|
||||
void getDrawableLayers(layer_t* firstLayer, layer_t* lastLayer);
|
||||
void getDrawableFrames(frame_t* firstFrame, frame_t* lastFrame);
|
||||
void getDrawableFrames(col_t* firstFrame, col_t* lastFrame);
|
||||
void drawPart(ui::Graphics* g, const gfx::Rect& bounds,
|
||||
const std::string* text,
|
||||
ui::Style* style,
|
||||
@ -296,13 +303,13 @@ namespace app {
|
||||
const bool is_disabled = false);
|
||||
void drawTop(ui::Graphics* g);
|
||||
void drawHeader(ui::Graphics* g);
|
||||
void drawHeaderFrame(ui::Graphics* g, const frame_t frame);
|
||||
void drawHeaderFrame(ui::Graphics* g, const col_t frame);
|
||||
void drawLayer(ui::Graphics* g, const layer_t layerIdx);
|
||||
void drawCel(ui::Graphics* g,
|
||||
const layer_t layerIdx, const frame_t frame,
|
||||
const layer_t layerIdx, const col_t frame,
|
||||
const Cel* cel, const DrawCelData* data);
|
||||
void drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds,
|
||||
const Cel* cel, const frame_t frame,
|
||||
const Cel* cel, const col_t frame,
|
||||
const bool is_active, const bool is_hover,
|
||||
const DrawCelData* data);
|
||||
void drawTags(ui::Graphics* g);
|
||||
@ -312,7 +319,7 @@ namespace app {
|
||||
const gfx::Rect& clipBounds);
|
||||
void drawRangeOutline(ui::Graphics* g);
|
||||
void drawPaddings(ui::Graphics* g);
|
||||
bool drawPart(ui::Graphics* g, int part, layer_t layer, frame_t frame);
|
||||
bool drawPart(ui::Graphics* g, int part, layer_t layer, col_t frame);
|
||||
void drawClipboardRange(ui::Graphics* g);
|
||||
gfx::Rect getLayerHeadersBounds() const;
|
||||
gfx::Rect getFrameHeadersBounds() const;
|
||||
@ -321,12 +328,12 @@ namespace app {
|
||||
gfx::Rect getPartBounds(const Hit& hit) const;
|
||||
gfx::Rect getRangeBounds(const Range& range) const;
|
||||
gfx::Rect getRangeClipBounds(const Range& range) const;
|
||||
int getFrameXPos(const frame_t frame) const;
|
||||
int getFrameWidth(const frame_t frame) const;
|
||||
frame_t getFrameInXPos(const int x) const;
|
||||
int getFrameXPos(const col_t frame) const;
|
||||
int getFrameWidth(const col_t frame) const;
|
||||
col_t getFrameInXPos(const int x) const;
|
||||
void invalidateHit(const Hit& hit);
|
||||
void invalidateLayer(const Layer* layer);
|
||||
void invalidateFrame(const frame_t frame);
|
||||
void invalidateFrame(const col_t frame);
|
||||
void invalidateRange();
|
||||
void regenerateCols();
|
||||
void regenerateRows();
|
||||
@ -337,7 +344,7 @@ namespace app {
|
||||
Hit hitTest(ui::Message* msg, const gfx::Point& mousePos);
|
||||
Hit hitTestCel(const gfx::Point& mousePos);
|
||||
void setHot(const Hit& hit);
|
||||
void showCel(layer_t layer, frame_t frame);
|
||||
void showCel(layer_t layer, col_t frame);
|
||||
void showCurrentCel();
|
||||
void focusTagBand(int band);
|
||||
void cleanClk();
|
||||
@ -346,26 +353,27 @@ namespace app {
|
||||
doc::Layer* getLayer(int layerIndex) const;
|
||||
layer_t getLayerIndex(const Layer* layer) const;
|
||||
bool isLayerActive(const layer_t layerIdx) const;
|
||||
bool isFrameActive(const frame_t frame) const;
|
||||
bool isCelActive(const layer_t layerIdx, const frame_t frame) const;
|
||||
bool isCelLooselyActive(const layer_t layerIdx, const frame_t frame) const;
|
||||
bool isFrameActive(const col_t frame) const;
|
||||
bool isCelActive(const layer_t layerIdx, const col_t frame) const;
|
||||
bool isCelLooselyActive(const layer_t layerIdx, const col_t frame) const;
|
||||
|
||||
void updateStatusBar(ui::Message* msg);
|
||||
void updateStatusBarForFrame(const frame_t frame,
|
||||
void updateStatusBarForFrame(const col_t frame,
|
||||
const Tag* tag,
|
||||
const Cel* cel);
|
||||
void updateDropRange(const gfx::Point& pt);
|
||||
void clearClipboardRange();
|
||||
|
||||
// The layer of the bottom (e.g. Background layer)
|
||||
layer_t firstLayer() const { return 0; }
|
||||
constexpr layer_t firstLayer() const { return 0; }
|
||||
// The layer of the top.
|
||||
layer_t lastLayer() const { return m_rows.size()-1; }
|
||||
|
||||
frame_t firstFrame() const { return frame_t(0); }
|
||||
frame_t lastFrame() const { return m_sprite->lastFrame(); }
|
||||
constexpr col_t firstFrame() const { return col_t(0); }
|
||||
col_t lastFrame() const;
|
||||
|
||||
bool validLayer(layer_t layer) const { return layer >= firstLayer() && layer <= lastLayer(); }
|
||||
bool validFrame(frame_t frame) const { return frame >= firstFrame() && frame <= lastFrame(); }
|
||||
bool validFrame(col_t frame) const { return frame >= firstFrame() && frame <= lastFrame(); }
|
||||
|
||||
int topHeight() const;
|
||||
|
||||
@ -380,7 +388,7 @@ namespace app {
|
||||
int frameBoxWidth() const;
|
||||
int outlineWidth() const;
|
||||
int oneTagHeight() const;
|
||||
frame_t calcTagVisibleToFrame(Tag* tag) const;
|
||||
col_t calcTagVisibleToFrame(Tag* tag) const;
|
||||
|
||||
void updateCelOverlayBounds(const Hit& hit);
|
||||
void drawCelOverlay(ui::Graphics* g);
|
||||
@ -404,6 +412,7 @@ namespace app {
|
||||
|
||||
static gfx::Color highlightColor(const gfx::Color color);
|
||||
|
||||
std::unique_ptr<view::TimelineAdapter> m_adapter;
|
||||
ui::ScrollBar m_hbar;
|
||||
ui::ScrollBar m_vbar;
|
||||
gfx::Rect m_viewportArea;
|
||||
@ -413,7 +422,7 @@ namespace app {
|
||||
Doc* m_document;
|
||||
Sprite* m_sprite;
|
||||
Layer* m_layer;
|
||||
frame_t m_frame;
|
||||
col_t m_frame;
|
||||
int m_rangeLocks;
|
||||
Range m_range;
|
||||
Range m_startRange;
|
||||
@ -426,7 +435,7 @@ namespace app {
|
||||
doc::ObjectVersion m_savedVersion;
|
||||
|
||||
// Data used to display columns in the timeline
|
||||
frame_t m_ncols;
|
||||
col_t m_ncols;
|
||||
|
||||
// Data used to display each row in the timeline
|
||||
std::vector<Row> m_rows;
|
||||
@ -472,22 +481,22 @@ namespace app {
|
||||
// Temporal data used to move the range.
|
||||
struct MoveRange {
|
||||
layer_t activeRelativeLayer;
|
||||
frame_t activeRelativeFrame;
|
||||
col_t activeRelativeFrame;
|
||||
} m_moveRangeData;
|
||||
|
||||
// Temporal data used to move tags.
|
||||
struct ResizeTag {
|
||||
doc::ObjectId tag = doc::NullId;
|
||||
doc::frame_t from, to;
|
||||
col_t from, to;
|
||||
void reset() {
|
||||
tag = doc::NullId;
|
||||
}
|
||||
void reset(const doc::ObjectId tagId) {
|
||||
auto tag = doc::get<doc::Tag>(tagId);
|
||||
if (tag) {
|
||||
void reset(const view::TimelineAdapter& adapter,
|
||||
const doc::ObjectId tagId) {
|
||||
if (auto tag = doc::get<doc::Tag>(tagId)) {
|
||||
this->tag = tagId;
|
||||
this->from = tag->fromFrame();
|
||||
this->to = tag->toFrame();
|
||||
this->from = adapter.toColFrame(fr_t(tag->fromFrame()));
|
||||
this->to = adapter.toColFrame(fr_t(tag->toFrame()));
|
||||
}
|
||||
else {
|
||||
this->tag = doc::NullId;
|
||||
|
22
src/view/frames.h
Normal file
22
src/view/frames.h
Normal file
@ -0,0 +1,22 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2023 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef VIEW_FRAMES_H_INCLUDED
|
||||
#define VIEW_FRAMES_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace view {
|
||||
|
||||
// We have two kind of frames: one real (matches the sprite frames),
|
||||
// and one virtual (matches the columns of the timeline)
|
||||
enum fr_t : int { }; // real frame (to reference real sprite frames)
|
||||
enum col_t : int { }; // timeline column / virtual frame
|
||||
|
||||
static constexpr const col_t kNoCol = col_t(-1);
|
||||
|
||||
} // namespace view
|
||||
|
||||
#endif // VIEW_FRAMES_H_INCLUDED
|
55
src/view/timeline_adapter.h
Normal file
55
src/view/timeline_adapter.h
Normal file
@ -0,0 +1,55 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2023 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef VIEW_TIMELINE_ADAPTER_H_INCLUDED
|
||||
#define VIEW_TIMELINE_ADAPTER_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/sprite.h"
|
||||
#include "view/frames.h"
|
||||
|
||||
namespace view {
|
||||
|
||||
// Tries to create an abstract way to access timeline frames (or
|
||||
// layers) so then we can change the kind of timeline view depending
|
||||
// on some kind of configuration (e.g. collapsing a tag/hiding frames
|
||||
// needs some kind of adapter between columns <-> frames, or "virtual
|
||||
// frames/columns" <-> "real frames").
|
||||
class TimelineAdapter {
|
||||
public:
|
||||
virtual ~TimelineAdapter() { }
|
||||
|
||||
// Total number of columns in the timeline.
|
||||
virtual col_t totalFrames() const = 0;
|
||||
|
||||
// Frame duration of the specific column.
|
||||
virtual int frameDuration(col_t frame) const = 0;
|
||||
|
||||
// Returns the real frame that the given column is showing.
|
||||
virtual fr_t toRealFrame(col_t frame) const = 0;
|
||||
|
||||
// Returns the column in the timeline that represents the given real
|
||||
// frame of the sprite. Returns -1 if the frame is not visible given
|
||||
// the current timeline filters/hidden frames.
|
||||
virtual col_t toColFrame(fr_t frame) const = 0;
|
||||
};
|
||||
|
||||
// Represents the default timeline view where the whole sprite is
|
||||
// visible (all frames/all layers/open groups).
|
||||
class FullSpriteTimelineAdapter : public TimelineAdapter {
|
||||
public:
|
||||
FullSpriteTimelineAdapter(doc::Sprite* sprite) : m_sprite(sprite) { }
|
||||
col_t totalFrames() const override { return col_t(m_sprite->totalFrames()); }
|
||||
int frameDuration(col_t frame) const override { return m_sprite->frameDuration(frame); }
|
||||
fr_t toRealFrame(col_t frame) const override { return fr_t(frame); }
|
||||
col_t toColFrame(fr_t frame) const override { return col_t(frame); }
|
||||
private:
|
||||
doc::Sprite* m_sprite = nullptr;
|
||||
};
|
||||
|
||||
} // namespace view
|
||||
|
||||
#endif // VIEW_TIMELINE_ADAPTER_H_INCLUDED
|
Loading…
x
Reference in New Issue
Block a user