WIP: Initial track tree expand animation

This commit is contained in:
Joel Holdsworth 2009-01-03 16:06:30 +00:00
parent 2d4a341123
commit dc9dd3fbd7
5 changed files with 190 additions and 14 deletions

View file

@ -183,7 +183,8 @@ bool TimelineHeaderContainer::on_button_release_event (
if(clickedExpander != NULL)
{
// Yes? The toggle the expanding
clickedExpander->set_expanded(!clickedExpander->get_expanded());
clickedExpander->expand_collapse(
clickedExpander->get_expanded() ? Track::Collapse : Track::Expand);
clickedExpander.reset();
timelineWidget.layoutHelper.update_layout();

View file

@ -37,9 +37,12 @@ namespace gui {
namespace widgets {
namespace timeline {
const int TimelineLayoutHelper::AnimationTimeout = 20; // 20ms
TimelineLayoutHelper::TimelineLayoutHelper(TimelineWidget &owner) :
timelineWidget(owner),
totalHeight(0)
totalHeight(0),
animation_state(Track::NoAnimationState)
{
}
@ -140,8 +143,11 @@ TimelineLayoutHelper::get_total_height() const
void
TimelineLayoutHelper::update_layout()
{
{
int offset = 0;
// Reset the animation state value, before it gets recalculated
animation_state = Track::NoAnimationState;
// Clear previously cached layout
headerBoxes.clear();
@ -150,18 +156,23 @@ TimelineLayoutHelper::update_layout()
const int header_width = TimelineWidget::HeaderWidth;
const int indent_width = TimelineWidget::HeaderIndentWidth;
layout_headers_recursive(layoutTree.begin(),
offset, header_width, indent_width, 0, true);
offset, animation_state, header_width, indent_width, 0, true);
totalHeight = offset;
// Signal that the layout has changed
timelineWidget.on_layout_changed();
// Begin animating as necessary
if(animation_state != Track::NoAnimationState && !animationTimer)
begin_animation();
}
void
TimelineLayoutHelper::layout_headers_recursive(
TrackTree::iterator_base parent_iterator,
int &offset, const int header_width, const int indent_width,
int &offset, int &common_animation_state,
const int header_width, const int indent_width,
const int depth, const bool parent_expanded)
{
REQUIRE(depth >= 0);
@ -175,8 +186,13 @@ TimelineLayoutHelper::layout_headers_recursive(
REQUIRE(model_track);
shared_ptr<timeline::Track> timeline_track =
lookup_timeline_track(model_track);
lookup_timeline_track(model_track);
// Is the track animating?
const int animation_state =
timeline_track->get_expand_animation_state();
// Is the track going to be shown?
if(parent_expanded)
{
// Calculate and store the box of the header
@ -192,10 +208,67 @@ TimelineLayoutHelper::layout_headers_recursive(
// Offset for the next header
offset += track_height + TimelineWidget::TrackPadding;
}
// Recurse to children
const bool expand_child =
((animation_state != Track::NoAnimationState) ||
timeline_track->get_expanded())
&& parent_expanded;
layout_headers_recursive(iterator, offset, header_width,
indent_width, depth + 1,
timeline_track->get_expanded() && parent_expanded);
layout_headers_recursive(iterator, offset, common_animation_state,
header_width, indent_width, depth + 1, expand_child);
// Do collapse animation as necessary
if(animation_state != Track::NoAnimationState)
{
timeline_track->tick_expand_animation();
// Calculate the total height of the branch
// Get the top and bottom descendants, and use them to get the
// total height
const shared_ptr<timeline::Track> &first_descendant_track =
lookup_timeline_track(
*(++TrackTree::pre_order_iterator(iterator)));
const shared_ptr<timeline::Track> &last_descendant_track =
lookup_timeline_track(
*(--TrackTree::pre_order_iterator(
++TrackTree::sibling_iterator(iterator))));
const Gdk::Rectangle &first_rect =
headerBoxes[first_descendant_track];
const Gdk::Rectangle &last_rect =
headerBoxes[last_descendant_track];
const int branch_height =
(last_rect.get_y() + last_rect.get_height()) -
first_rect.get_y();
// Now we have the branch_height, obscure tracks according to
// the animation state
offset = offset - branch_height + branch_height * animation_state / Track::MaxExpandAnimation;
TrackTree::pre_order_iterator descendant_iterator(iterator);
descendant_iterator++;
TrackTree::sibling_iterator end_iterator(iterator);
end_iterator++;
for(descendant_iterator = layoutTree.begin(parent_iterator);
descendant_iterator != end_iterator;
descendant_iterator++)
{
const weak_ptr<timeline::Track> &track =
lookup_timeline_track(*descendant_iterator);
const Gdk::Rectangle &rect = headerBoxes[track];
if(rect.get_y() + rect.get_height() > offset)
headerBoxes.erase(track);
}
// Make sure the global animation state includes this branch's
// animation state
common_animation_state = max(
common_animation_state, animation_state);
}
}
}
@ -211,6 +284,21 @@ TimelineLayoutHelper::lookup_timeline_track(
return timeline_track;
}
void
TimelineLayoutHelper::begin_animation()
{
animationTimer = Glib::signal_timeout().connect(
sigc::mem_fun(this, &TimelineLayoutHelper::on_animation_tick),
AnimationTimeout);
}
bool
TimelineLayoutHelper::on_animation_tick()
{
update_layout();
return animation_state != Track::NoAnimationState;
}
} // namespace timeline
} // namespace widgets
} // namespace gui

View file

@ -167,9 +167,11 @@ protected:
* false if any of them are collapsed.
* @see update_layout()
**/
#warning is_animating not documented
void layout_headers_recursive(
TrackTree::iterator_base parent_iterator,
int &offset, const int header_width, const int indent_width,
int &offset, int &common_animation_state,
const int header_width, const int indent_width,
const int depth, const bool parent_expanded);
/**
@ -184,6 +186,10 @@ protected:
**/
boost::shared_ptr<timeline::Track> lookup_timeline_track(
boost::shared_ptr<model::Track> model_track);
void begin_animation();
bool on_animation_tick();
protected:
@ -213,6 +219,14 @@ protected:
* @see update_layout()
**/
int totalHeight;
sigc::connection animationTimer;
int animation_state;
protected:
static const int AnimationTimeout;
};
} // namespace timeline

View file

@ -36,11 +36,15 @@ namespace gui {
namespace widgets {
namespace timeline {
const int Track::NoAnimationState = -1;
const int Track::MaxExpandAnimation = 10;
Track::Track(TimelineWidget &timeline_widget,
shared_ptr<model::Track> track) :
timelineWidget(timeline_widget),
model_track(track),
expanded(true),
expandAnimationState(Track::NoAnimationState),
enableButton(Gtk::StockID("track_enabled")),
lockButton(Gtk::StockID("track_unlocked"))
{
@ -109,9 +113,56 @@ Track::get_expanded() const
}
void
Track::set_expanded(bool expanded)
Track::expand_collapse(ExpandDirection direction)
{
this->expanded = expanded;
expandDirection = direction;
if(direction == Expand)
{
expanded = true;
expandAnimationState = 0;
}
else
{
expanded = false;
expandAnimationState = MaxExpandAnimation;
}
}
int
Track::get_expand_animation_state() const
{
ENSURE((expandAnimationState >= 0 &&
expandAnimationState <= MaxExpandAnimation) ||
expandAnimationState == NoAnimationState);
return expandAnimationState;
}
void
Track::tick_expand_animation()
{
if(expandAnimationState <= NoAnimationState)
{
WARN(gui, "tick_expand_animation() was called when"
" expandAnimationState was set to NoAnimationState");
return;
}
if(expandDirection == Expand)
{
expandAnimationState++;
if(expandAnimationState >= MaxExpandAnimation)
expandAnimationState = NoAnimationState;
}
else
{
expandAnimationState--;
if(expandAnimationState <= 0)
expandAnimationState = NoAnimationState;
}
ENSURE((expandAnimationState >= 0 &&
expandAnimationState <= MaxExpandAnimation) ||
expandAnimationState == NoAnimationState);
}
void

View file

@ -39,6 +39,14 @@ class TimelineViewWindow;
class Track : public sigc::trackable
{
public:
enum ExpandDirection
{
Expand,
Collapse
};
public:
Track(TimelineWidget &timeline_widget,
boost::shared_ptr<model::Track> track);
@ -51,13 +59,22 @@ public:
bool get_expanded() const;
void set_expanded(bool expanded);
void expand_collapse(ExpandDirection direction);
// -1 for no animation
int get_expand_animation_state() const;
void tick_expand_animation();
void show_header_context_menu(guint button, guint32 time);
virtual void draw_track(Cairo::RefPtr<Cairo::Context> cairo,
TimelineViewWindow* const window)
const = 0;
public:
static const int NoAnimationState;
static const int MaxExpandAnimation;
private:
//----- Internals -----//
@ -77,6 +94,11 @@ protected:
private:
bool expanded;
ExpandDirection expandDirection;
int expandAnimationState;
//----- Header Widgets ------//