Improve handling of overlapping frame tags in the Timeline (fix #768)

This commit is contained in:
David Capello 2017-03-26 21:52:50 -03:00
parent 2c2f29b87a
commit 27bc151a37
5 changed files with 93 additions and 9 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -493,7 +493,7 @@
<text color="button_hot_text" state="mouse" />
<text color="button_selected_text" state="selected" />
<text color="background" x="1" y="1" state="disabled" />
<newlayer />
<newlayer />
<text color="disabled" state="disabled" />
</style>
<style id="check_box" border="2">

View File

@ -1136,9 +1136,11 @@ void Timeline::onResize(ui::ResizeEvent& ev)
gfx::Size sz = m_aniControls.sizeHint();
m_aniControls.setBounds(
gfx::Rect(rc.x, rc.y, MIN(sz.w, m_separator_x),
font()->height() +
skinTheme()->dimensions.timelineTagsAreaHeight()));
gfx::Rect(
rc.x,
rc.y+MAX(0, m_tagBands-1)*oneTagHeight(),
MIN(sz.w, m_separator_x),
oneTagHeight()));
updateScrollBars();
}
@ -2010,11 +2012,15 @@ void Timeline::drawFrameTags(ui::Graphics* g)
clientBounds().w,
theme->dimensions.timelineTagsAreaHeight()));
std::vector<unsigned char> tagsPerFrame(m_sprite->totalFrames(), 0);
for (FrameTag* frameTag : m_sprite->frameTags()) {
gfx::Rect bounds1 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), frameTag->fromFrame()));
gfx::Rect bounds2 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), frameTag->toFrame()));
gfx::Rect bounds = bounds1.createUnion(bounds2);
bounds.y -= theme->dimensions.timelineTagsAreaHeight();
gfx::Rect frameTagBounds = getPartBounds(Hit(PART_FRAME_TAG, 0, 0, frameTag->id()));
bounds.h = bounds.y2() - frameTagBounds.y2();
bounds.y = frameTagBounds.y2();
{
IntersectClip clip(g, bounds);
@ -2023,7 +2029,7 @@ void Timeline::drawFrameTags(ui::Graphics* g)
}
{
bounds = getPartBounds(Hit(PART_FRAME_TAG, 0, 0, frameTag->id()));
bounds = frameTagBounds;
gfx::Color bg = frameTag->color();
if (m_clk.part == PART_FRAME_TAG && m_clk.frameTag == frameTag->id()) {
@ -2049,6 +2055,11 @@ void Timeline::drawFrameTags(ui::Graphics* g)
gfx::ColorNone,
bounds.origin());
}
for (frame_t f=frameTag->fromFrame(); f<=frameTag->toFrame(); ++f) {
if (tagsPerFrame[f] < 255)
++tagsPerFrame[f];
}
}
}
@ -2335,6 +2346,13 @@ gfx::Rect Timeline::getPartBounds(const Hit& hit) const
bounds.x += 3*ui::guiscale();
bounds.w = font()->textLength(frameTag->name().c_str()) + 4*ui::guiscale();
bounds.h = font()->height() + 2*ui::guiscale();
auto it = m_tagBand.find(frameTag);
if (it != m_tagBand.end()) {
int dy = (m_tagBands-it->second-1)*oneTagHeight();
bounds.y -= dy;
}
return bounds;
}
break;
@ -2405,9 +2423,46 @@ void Timeline::regenerateLayers()
m_layers[i++] = LayerInfo(layer, level, flags);
});
regenerateTagBands();
updateScrollBars();
}
void Timeline::regenerateTagBands()
{
// TODO improve this implementation
std::vector<unsigned char> tagsPerFrame(m_sprite->totalFrames(), 0);
std::vector<FrameTag*> bands(4, nullptr);
m_tagBand.clear();
for (FrameTag* frameTag : m_sprite->frameTags()) {
frame_t f = frameTag->fromFrame();
int b=0;
for (; b<int(bands.size()); ++b) {
if (!bands[b] ||
frameTag->fromFrame() > calcTagVisibleToFrame(bands[b])) {
bands[b] = frameTag;
m_tagBand[frameTag] = b;
break;
}
}
if (b == int(bands.size()))
m_tagBand[frameTag] = tagsPerFrame[f];
frame_t toFrame = calcTagVisibleToFrame(frameTag);
for (; f<=toFrame; ++f) {
if (tagsPerFrame[f] < 255)
++tagsPerFrame[f];
}
}
int oldBands = m_tagBands;
m_tagBands = 0;
for (int i : tagsPerFrame)
m_tagBands = MAX(m_tagBands, i);
if (oldBands != m_tagBands)
layout();
}
void Timeline::updateScrollBars()
{
gfx::Rect rc = bounds();
@ -3108,13 +3163,30 @@ int Timeline::outlineWidth() const
return skinTheme()->dimensions.timelineOutlineWidth();
}
int Timeline::oneTagHeight() const
{
return
font()->height() +
2*ui::guiscale() +
skinTheme()->dimensions.timelineTagsAreaHeight();
}
// Returns the last frame where the frame tag (or frame tag label)
// is visible in the timeline.
int Timeline::calcTagVisibleToFrame(FrameTag* frameTag) const
{
return
MAX(frameTag->toFrame(),
frameTag->fromFrame() +
font()->textLength(frameTag->name())/frameBoxWidth());
}
int Timeline::topHeight() const
{
int h = 0;
if (m_document && m_sprite) {
h += skinTheme()->dimensions.timelineTopBorder();
h += font()->height();
h += skinTheme()->dimensions.timelineTagsAreaHeight();
h += oneTagHeight() * MAX(1, m_tagBands);
}
return h;
}

View File

@ -266,6 +266,7 @@ namespace app {
gfx::Rect getRangeBounds(const Range& range) const;
void invalidateHit(const Hit& hit);
void regenerateLayers();
void regenerateTagBands();
void updateScrollBars();
void updateByMousePos(ui::Message* msg, const gfx::Point& mousePos);
Hit hitTest(ui::Message* msg, const gfx::Point& mousePos);
@ -308,6 +309,8 @@ namespace app {
int layerBoxHeight() const;
int frameBoxWidth() const;
int outlineWidth() const;
int oneTagHeight() const;
int calcTagVisibleToFrame(FrameTag* frameTag) const;
void updateCelOverlayBounds(const Hit& hit);
void drawCelOverlay(ui::Graphics* g);
@ -329,7 +332,14 @@ namespace app {
Range m_startRange;
Range m_dropRange;
State m_state;
// Data used to display each layer
std::vector<LayerInfo> m_layers;
// Data used to display frame tags
int m_tagBands = 0;
std::map<FrameTag*, int> m_tagBand;
int m_separator_x;
int m_separator_w;
int m_origFrames;

View File

@ -34,7 +34,9 @@ void FrameTags::add(FrameTag* tag)
{
auto it = begin(), end = this->end();
for (; it != end; ++it) {
if ((*it)->fromFrame() > tag->fromFrame())
if ((*it)->fromFrame() > tag->fromFrame() ||
((*it)->fromFrame() == tag->fromFrame() &&
(*it)->toFrame() < tag->toFrame()))
break;
}
m_tags.insert(it, tag);