From dc9dd3fbd7ee98b89f6939c59b72f23c8cb5c011 Mon Sep 17 00:00:00 2001 From: Joel Holdsworth Date: Sat, 3 Jan 2009 16:06:30 +0000 Subject: [PATCH] WIP: Initial track tree expand animation --- .../timeline/timeline-header-container.cpp | 3 +- .../timeline/timeline-layout-helper.cpp | 106 ++++++++++++++++-- .../timeline/timeline-layout-helper.hpp | 16 ++- src/gui/widgets/timeline/timeline-track.cpp | 55 ++++++++- src/gui/widgets/timeline/timeline-track.hpp | 24 +++- 5 files changed, 190 insertions(+), 14 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-header-container.cpp b/src/gui/widgets/timeline/timeline-header-container.cpp index 88914e779..d2bcef5ba 100644 --- a/src/gui/widgets/timeline/timeline-header-container.cpp +++ b/src/gui/widgets/timeline/timeline-header-container.cpp @@ -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(); diff --git a/src/gui/widgets/timeline/timeline-layout-helper.cpp b/src/gui/widgets/timeline/timeline-layout-helper.cpp index d57baddc5..d6a4e5560 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.cpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.cpp @@ -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 = - 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 &first_descendant_track = + lookup_timeline_track( + *(++TrackTree::pre_order_iterator(iterator))); + const shared_ptr &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 &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 diff --git a/src/gui/widgets/timeline/timeline-layout-helper.hpp b/src/gui/widgets/timeline/timeline-layout-helper.hpp index f6fade115..36e01953a 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.hpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.hpp @@ -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 lookup_timeline_track( boost::shared_ptr 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 diff --git a/src/gui/widgets/timeline/timeline-track.cpp b/src/gui/widgets/timeline/timeline-track.cpp index d6605941c..45921e006 100644 --- a/src/gui/widgets/timeline/timeline-track.cpp +++ b/src/gui/widgets/timeline/timeline-track.cpp @@ -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 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 diff --git a/src/gui/widgets/timeline/timeline-track.hpp b/src/gui/widgets/timeline/timeline-track.hpp index 652543afc..ad3c814d5 100644 --- a/src/gui/widgets/timeline/timeline-track.hpp +++ b/src/gui/widgets/timeline/timeline-track.hpp @@ -39,6 +39,14 @@ class TimelineViewWindow; class Track : public sigc::trackable { +public: + + enum ExpandDirection + { + Expand, + Collapse + }; + public: Track(TimelineWidget &timeline_widget, boost::shared_ptr 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, 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 ------//