Timeline: improve look & feel of range selections

This commit is contained in:
David Capello 2013-12-15 09:42:05 -03:00
parent 6b7625267d
commit bcab165700
3 changed files with 166 additions and 44 deletions

View File

@ -362,8 +362,14 @@
<text align="left" valign="middle" padding-left="4" /> <text align="left" valign="middle" padding-left="4" />
</style> </style>
<!-- timeline_selected_cel -->
<style id="timeline_selected_cel">
<background color="timeline_clicked" part="timeline_clicked" />
<text color="timeline_clicked_text" />
</style>
<!-- timeline_empty_frame --> <!-- timeline_empty_frame -->
<style id="timeline_empty_frame" base="timeline_box"> <style id="timeline_empty_frame">
<icon part="timeline_empty_frame_normal" /> <icon part="timeline_empty_frame_normal" />
</style> </style>
<style id="timeline_empty_frame:active"> <style id="timeline_empty_frame:active">
@ -371,14 +377,14 @@
</style> </style>
<!--timeline_keyframe--> <!--timeline_keyframe-->
<style id="timeline_keyframe" base="timeline_box"> <style id="timeline_keyframe">
<icon part="timeline_keyframe_normal" /> <icon part="timeline_keyframe_normal" />
</style> </style>
<style id="timeline_keyframe:active"> <style id="timeline_keyframe:active">
<icon part="timeline_keyframe_active" /> <icon part="timeline_keyframe_active" />
</style> </style>
<!-- timeline_cel --> <!-- timeline_gear -->
<style id="timeline_gear" base="timeline_box"> <style id="timeline_gear" base="timeline_box">
<icon part="timeline_gear" /> <icon part="timeline_gear" />
</style> </style>

View File

@ -92,6 +92,7 @@ static const char* kTimelinePadding = "timeline_padding";
static const char* kTimelinePaddingTr = "timeline_padding_tr"; static const char* kTimelinePaddingTr = "timeline_padding_tr";
static const char* kTimelinePaddingBl = "timeline_padding_bl"; static const char* kTimelinePaddingBl = "timeline_padding_bl";
static const char* kTimelinePaddingBr = "timeline_padding_br"; static const char* kTimelinePaddingBr = "timeline_padding_br";
static const char* kTimelineSelectedCelStyle = "timeline_selected_cel";
static const char* kTimelineActiveColor = "timeline_active"; static const char* kTimelineActiveColor = "timeline_active";
@ -124,11 +125,10 @@ Timeline::Timeline()
, m_timelinePaddingTrStyle(get_style(kTimelinePaddingTr)) , m_timelinePaddingTrStyle(get_style(kTimelinePaddingTr))
, m_timelinePaddingBlStyle(get_style(kTimelinePaddingBl)) , m_timelinePaddingBlStyle(get_style(kTimelinePaddingBl))
, m_timelinePaddingBrStyle(get_style(kTimelinePaddingBr)) , m_timelinePaddingBrStyle(get_style(kTimelinePaddingBr))
, m_timelineSelectedCelStyle(get_style(kTimelineSelectedCelStyle))
, m_context(UIContext::instance()) , m_context(UIContext::instance())
, m_editor(NULL) , m_editor(NULL)
, m_document(NULL) , m_document(NULL)
, m_frameBegin(-1)
, m_frameEnd(-1)
{ {
m_context->addObserver(this); m_context->addObserver(this);
@ -209,6 +209,7 @@ void Timeline::setLayer(Layer* layer)
ASSERT(m_editor != NULL); ASSERT(m_editor != NULL);
m_layer = layer; m_layer = layer;
invalidate();
if (m_editor->getLayer() != layer) if (m_editor->getLayer() != layer)
m_editor->setLayer(m_layer); m_editor->setLayer(m_layer);
@ -278,12 +279,11 @@ bool Timeline::onProcessMessage(Message* msg)
case A_PART_HEADER_FRAME: case A_PART_HEADER_FRAME:
setFrame(m_clk_frame); setFrame(m_clk_frame);
captureMouse(); captureMouse();
if (msg->shiftPressed()) if (msg->ctrlPressed())
m_state = STATE_MOVING_FRAME; m_state = STATE_MOVING_FRAME;
else { else {
m_state = STATE_SELECTING_FRAME; m_state = STATE_SELECTING_FRAMES;
m_frameBegin = m_frame; startRange(getLayerIndex(m_layer), m_clk_frame);
m_frameEnd = m_frame;
} }
break; break;
case A_PART_LAYER_TEXT: { case A_PART_LAYER_TEXT: {
@ -298,10 +298,17 @@ bool Timeline::onProcessMessage(Message* msg)
invalidate(); invalidate();
} }
// Change the scroll to show the new selected cel. // Change the scroll to show the new selected layer/cel.
showCel(m_clk_layer, m_frame); showCel(m_clk_layer, m_frame);
if (msg->ctrlPressed()) {
m_state = STATE_MOVING_LAYER;
}
else {
m_state = STATE_SELECTING_LAYERS;
startRange(m_clk_layer, m_frame);
}
captureMouse(); captureMouse();
m_state = STATE_MOVING_LAYER;
break; break;
} }
case A_PART_LAYER_EYE_ICON: case A_PART_LAYER_EYE_ICON:
@ -321,6 +328,7 @@ bool Timeline::onProcessMessage(Message* msg)
old_frame != m_clk_frame) { old_frame != m_clk_frame) {
setLayer(m_layers[m_clk_layer]); setLayer(m_layers[m_clk_layer]);
setFrame(m_clk_frame); setFrame(m_clk_frame);
invalidate();
} }
// Change the scroll to show the new selected cel. // Change the scroll to show the new selected cel.
@ -328,16 +336,20 @@ bool Timeline::onProcessMessage(Message* msg)
// Capture the mouse (to move the cel). // Capture the mouse (to move the cel).
captureMouse(); captureMouse();
m_state = STATE_MOVING_CEL;
if (msg->ctrlPressed())
m_state = STATE_MOVING_CEL;
else {
m_state = STATE_SELECTING_CELS;
startRange(m_clk_layer, m_clk_frame);
invalidate();
}
break; break;
} }
} }
// Redraw the new clicked part (header, layer or cel). // Redraw the new clicked part (header, layer or cel).
invalidatePart(m_clk_part, invalidatePart(m_clk_part, m_clk_layer, m_clk_frame);
m_clk_layer,
m_clk_frame);
invalidate();
break; break;
case kMouseMoveMessage: { case kMouseMoveMessage: {
@ -394,6 +406,15 @@ bool Timeline::onProcessMessage(Message* msg)
- HDRSIZE - HDRSIZE
+ m_scroll_y) / LAYSIZE; + m_scroll_y) / LAYSIZE;
if (hot_layer >= (int)m_layers.size()) {
if (hasCapture())
hot_layer = m_layers.size()-1;
else
hot_layer = -1;
}
else if (hasCapture() && hot_layer < 0)
hot_layer = 0;
// Is the mouse on a layer's label? // Is the mouse on a layer's label?
if (mousePos.x < m_separator_x) { if (mousePos.x < m_separator_x) {
if (getPartBounds(A_PART_LAYER_EYE_ICON, hot_layer).contains(mousePos)) if (getPartBounds(A_PART_LAYER_EYE_ICON, hot_layer).contains(mousePos))
@ -414,17 +435,38 @@ bool Timeline::onProcessMessage(Message* msg)
} }
} }
if (hasCapture()) {
if (m_state == STATE_SELECTING_FRAME) {
if (m_frame != m_hot_frame) {
m_frameEnd = m_hot_frame;
setFrame(m_hot_frame);
}
}
}
// Set the new 'hot' thing. // Set the new 'hot' thing.
hotThis(hot_part, hot_layer, hot_frame); hotThis(hot_part, hot_layer, hot_frame);
if (hasCapture()) {
hot_layer = MID(0, hot_layer, (int)m_layers.size()-1);
hot_frame = MID(FrameNumber(0), hot_frame, m_sprite->getLastFrame());
switch (m_state) {
case STATE_SELECTING_LAYERS: {
if (m_layer != m_layers[hot_layer]) {
updateRange(hot_layer, m_frame);
setLayer(m_layers[m_clk_layer = hot_layer]);
}
break;
}
case STATE_SELECTING_FRAMES: {
updateRange(getLayerIndex(m_layer), hot_frame);
setFrame(m_clk_frame = hot_frame);
break;
}
case STATE_SELECTING_CELS:
if ((m_layer != m_layers[hot_layer])
|| (m_frame != hot_frame)) {
updateRange(hot_layer, hot_frame);
setLayer(m_layers[m_clk_layer = hot_layer]);
setFrame(m_clk_frame = hot_frame);
}
break;
}
}
return true; return true;
} }
@ -434,10 +476,10 @@ bool Timeline::onProcessMessage(Message* msg)
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg); MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
releaseMouse();
if (m_state == STATE_SCROLLING) { if (m_state == STATE_SCROLLING) {
m_state = STATE_STANDBY; m_state = STATE_STANDBY;
releaseMouse();
return true; return true;
} }
@ -511,6 +553,7 @@ bool Timeline::onProcessMessage(Message* msg)
} }
} }
} }
#if 0
// Move a layer. // Move a layer.
else if (mouseMsg->left()) { else if (mouseMsg->left()) {
if (m_hot_layer >= 0 && if (m_hot_layer >= 0 &&
@ -543,6 +586,7 @@ bool Timeline::onProcessMessage(Message* msg)
} }
} }
} }
#endif
break; break;
case A_PART_LAYER_EYE_ICON: case A_PART_LAYER_EYE_ICON:
// Hide/show layer. // Hide/show layer.
@ -612,14 +656,18 @@ bool Timeline::onProcessMessage(Message* msg)
// Clean the clicked-part & redraw the hot-part. // Clean the clicked-part & redraw the hot-part.
cleanClk(); cleanClk();
invalidatePart(m_hot_part,
m_hot_layer, if (hasCapture())
m_hot_frame); invalidate();
else
invalidatePart(m_hot_part, m_hot_layer, m_hot_frame);
// Restore the cursor. // Restore the cursor.
m_state = STATE_STANDBY; m_state = STATE_STANDBY;
setCursor(mouseMsg->position().x, setCursor(mouseMsg->position().x,
mouseMsg->position().y); mouseMsg->position().y);
releaseMouse();
return true; return true;
} }
break; break;
@ -814,7 +862,7 @@ void Timeline::onFrameChanged(Editor* editor)
setFrame(editor->getFrame()); setFrame(editor->getFrame());
if (!hasCapture()) if (!hasCapture())
m_frameBegin = m_frameEnd = FrameNumber(-1); disableRange();
showCurrentCel(); showCurrentCel();
} }
@ -822,6 +870,10 @@ void Timeline::onFrameChanged(Editor* editor)
void Timeline::onLayerChanged(Editor* editor) void Timeline::onLayerChanged(Editor* editor)
{ {
setLayer(editor->getLayer()); setLayer(editor->getLayer());
if (!hasCapture())
disableRange();
showCurrentCel(); showCurrentCel();
} }
@ -978,6 +1030,13 @@ void Timeline::drawCel(ui::Graphics* g, int layer_index, FrameNumber frame, Cel*
m_sprite->getStock()->getImage(cel->getImage()) == NULL); m_sprite->getStock()->getImage(cel->getImage()) == NULL);
gfx::Rect bounds = getPartBounds(A_PART_CEL, layer_index, frame); gfx::Rect bounds = getPartBounds(A_PART_CEL, layer_index, frame);
if (m_range.inRange(layer_index, frame)
|| (isLayerActive(layer) && isFrameActive(frame))) {
drawPart(g, bounds, NULL, m_timelineSelectedCelStyle, false, false, true);
}
else
drawPart(g, bounds, NULL, m_timelineBoxStyle, is_active, is_hover);
drawPart(g, bounds, NULL, drawPart(g, bounds, NULL,
is_empty ? m_timelineEmptyFrameStyle: m_timelineKeyframeStyle, is_empty ? m_timelineEmptyFrameStyle: m_timelineKeyframeStyle,
is_active, is_hover); is_active, is_hover);
@ -1115,6 +1174,25 @@ void Timeline::regenerateLayers()
m_layers[c] = m_sprite->indexToLayer(LayerIndex(nlayers-c-1)); m_layers[c] = m_sprite->indexToLayer(LayerIndex(nlayers-c-1));
} }
void Timeline::startRange(int layer, FrameNumber frame)
{
m_range.enabled = true;
m_range.layerBegin = m_range.layerEnd = layer;
m_range.frameBegin = m_range.frameEnd = frame;
}
void Timeline::updateRange(int layer, FrameNumber frame)
{
ASSERT(m_range.enabled);
m_range.layerEnd = layer;
m_range.frameEnd = frame;
}
void Timeline::disableRange()
{
m_range.enabled = false;
}
void Timeline::hotThis(int hot_part, int hot_layer, FrameNumber hot_frame) void Timeline::hotThis(int hot_part, int hot_layer, FrameNumber hot_frame)
{ {
// If the part, layer or frame change. // If the part, layer or frame change.
@ -1274,22 +1352,41 @@ int Timeline::getLayerIndex(const Layer* layer) const
bool Timeline::isLayerActive(const Layer* layer) const bool Timeline::isLayerActive(const Layer* layer) const
{ {
return layer == m_layer; if (layer == m_layer)
return true;
else
return m_range.inRange(getLayerIndex(layer));
} }
bool Timeline::isFrameActive(FrameNumber frame) const bool Timeline::isFrameActive(FrameNumber frame) const
{ {
if (frame == m_frame) if (frame == m_frame)
return true; return true;
else { else
if (m_frameBegin < m_frameEnd) { return m_range.inRange(frame);
return (frame >= m_frameBegin && frame <= m_frameEnd); }
}
else { bool Timeline::Range::inRange(int layer) const
return (frame >= m_frameEnd && frame <= m_frameBegin); {
} if (enabled)
} return ((layer >= layerBegin && layer <= layerEnd)
return false; || (layer >= layerEnd && layer <= layerBegin));
else
return false;
}
bool Timeline::Range::inRange(FrameNumber frame) const
{
if (enabled)
return ((frame >= frameBegin && frame <= frameEnd)
|| (frame >= frameEnd && frame <= frameBegin));
else
return false;
}
bool Timeline::Range::inRange(int layer, FrameNumber frame) const
{
return inRange(layer) && inRange(frame);
} }
} // namespace app } // namespace app

View File

@ -54,7 +54,9 @@ namespace app {
enum State { enum State {
STATE_STANDBY, STATE_STANDBY,
STATE_SCROLLING, STATE_SCROLLING,
STATE_SELECTING_FRAME, STATE_SELECTING_LAYERS,
STATE_SELECTING_FRAMES,
STATE_SELECTING_CELS,
STATE_MOVING_SEPARATOR, STATE_MOVING_SEPARATOR,
STATE_MOVING_LAYER, STATE_MOVING_LAYER,
STATE_MOVING_CEL, STATE_MOVING_CEL,
@ -116,6 +118,9 @@ namespace app {
gfx::Rect getPartBounds(int part, int layer = 0, FrameNumber frame = FrameNumber(0)) const; gfx::Rect getPartBounds(int part, int layer = 0, FrameNumber frame = FrameNumber(0)) const;
void invalidatePart(int part, int layer, FrameNumber frame); void invalidatePart(int part, int layer, FrameNumber frame);
void regenerateLayers(); void regenerateLayers();
void startRange(int layer, FrameNumber frame);
void updateRange(int layer, FrameNumber frame);
void disableRange();
void hotThis(int hot_part, int hot_layer, FrameNumber hotFrame); void hotThis(int hot_part, int hot_layer, FrameNumber hotFrame);
void centerCel(int layer, FrameNumber frame); void centerCel(int layer, FrameNumber frame);
void showCel(int layer, FrameNumber frame); void showCel(int layer, FrameNumber frame);
@ -126,6 +131,20 @@ namespace app {
bool isLayerActive(const Layer* layer) const; bool isLayerActive(const Layer* layer) const;
bool isFrameActive(FrameNumber frame) const; bool isFrameActive(FrameNumber frame) const;
struct Range {
bool enabled;
int layerBegin;
int layerEnd;
FrameNumber frameBegin;
FrameNumber frameEnd;
Range() : enabled(false) { }
bool inRange(int layer) const;
bool inRange(FrameNumber frame) const;
bool inRange(int layer, FrameNumber frame) const;
};
skin::Style* m_timelineStyle; skin::Style* m_timelineStyle;
skin::Style* m_timelineBoxStyle; skin::Style* m_timelineBoxStyle;
skin::Style* m_timelineOpenEyeStyle; skin::Style* m_timelineOpenEyeStyle;
@ -140,14 +159,14 @@ namespace app {
skin::Style* m_timelinePaddingTrStyle; skin::Style* m_timelinePaddingTrStyle;
skin::Style* m_timelinePaddingBlStyle; skin::Style* m_timelinePaddingBlStyle;
skin::Style* m_timelinePaddingBrStyle; skin::Style* m_timelinePaddingBrStyle;
skin::Style* m_timelineSelectedCelStyle;
Context* m_context; Context* m_context;
Editor* m_editor; Editor* m_editor;
Document* m_document; Document* m_document;
Sprite* m_sprite; Sprite* m_sprite;
Layer* m_layer; Layer* m_layer;
FrameNumber m_frame; FrameNumber m_frame;
FrameNumber m_frameBegin; Range m_range;
FrameNumber m_frameEnd;
State m_state; State m_state;
std::vector<Layer*> m_layers; std::vector<Layer*> m_layers;
int m_scroll_x; int m_scroll_x;