WIP: Initial track tree expand animation
This commit is contained in:
parent
2d4a341123
commit
dc9dd3fbd7
5 changed files with 190 additions and 14 deletions
|
|
@ -183,7 +183,8 @@ bool TimelineHeaderContainer::on_button_release_event (
|
||||||
if(clickedExpander != NULL)
|
if(clickedExpander != NULL)
|
||||||
{
|
{
|
||||||
// Yes? The toggle the expanding
|
// Yes? The toggle the expanding
|
||||||
clickedExpander->set_expanded(!clickedExpander->get_expanded());
|
clickedExpander->expand_collapse(
|
||||||
|
clickedExpander->get_expanded() ? Track::Collapse : Track::Expand);
|
||||||
clickedExpander.reset();
|
clickedExpander.reset();
|
||||||
|
|
||||||
timelineWidget.layoutHelper.update_layout();
|
timelineWidget.layoutHelper.update_layout();
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,12 @@ namespace gui {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
namespace timeline {
|
namespace timeline {
|
||||||
|
|
||||||
|
const int TimelineLayoutHelper::AnimationTimeout = 20; // 20ms
|
||||||
|
|
||||||
TimelineLayoutHelper::TimelineLayoutHelper(TimelineWidget &owner) :
|
TimelineLayoutHelper::TimelineLayoutHelper(TimelineWidget &owner) :
|
||||||
timelineWidget(owner),
|
timelineWidget(owner),
|
||||||
totalHeight(0)
|
totalHeight(0),
|
||||||
|
animation_state(Track::NoAnimationState)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,6 +146,9 @@ TimelineLayoutHelper::update_layout()
|
||||||
{
|
{
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
|
// Reset the animation state value, before it gets recalculated
|
||||||
|
animation_state = Track::NoAnimationState;
|
||||||
|
|
||||||
// Clear previously cached layout
|
// Clear previously cached layout
|
||||||
headerBoxes.clear();
|
headerBoxes.clear();
|
||||||
|
|
||||||
|
|
@ -150,18 +156,23 @@ TimelineLayoutHelper::update_layout()
|
||||||
const int header_width = TimelineWidget::HeaderWidth;
|
const int header_width = TimelineWidget::HeaderWidth;
|
||||||
const int indent_width = TimelineWidget::HeaderIndentWidth;
|
const int indent_width = TimelineWidget::HeaderIndentWidth;
|
||||||
layout_headers_recursive(layoutTree.begin(),
|
layout_headers_recursive(layoutTree.begin(),
|
||||||
offset, header_width, indent_width, 0, true);
|
offset, animation_state, header_width, indent_width, 0, true);
|
||||||
|
|
||||||
totalHeight = offset;
|
totalHeight = offset;
|
||||||
|
|
||||||
// Signal that the layout has changed
|
// Signal that the layout has changed
|
||||||
timelineWidget.on_layout_changed();
|
timelineWidget.on_layout_changed();
|
||||||
|
|
||||||
|
// Begin animating as necessary
|
||||||
|
if(animation_state != Track::NoAnimationState && !animationTimer)
|
||||||
|
begin_animation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineLayoutHelper::layout_headers_recursive(
|
TimelineLayoutHelper::layout_headers_recursive(
|
||||||
TrackTree::iterator_base parent_iterator,
|
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)
|
const int depth, const bool parent_expanded)
|
||||||
{
|
{
|
||||||
REQUIRE(depth >= 0);
|
REQUIRE(depth >= 0);
|
||||||
|
|
@ -177,6 +188,11 @@ TimelineLayoutHelper::layout_headers_recursive(
|
||||||
shared_ptr<timeline::Track> timeline_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)
|
if(parent_expanded)
|
||||||
{
|
{
|
||||||
// Calculate and store the box of the header
|
// Calculate and store the box of the header
|
||||||
|
|
@ -193,9 +209,66 @@ TimelineLayoutHelper::layout_headers_recursive(
|
||||||
offset += track_height + TimelineWidget::TrackPadding;
|
offset += track_height + TimelineWidget::TrackPadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
layout_headers_recursive(iterator, offset, header_width,
|
// Recurse to children
|
||||||
indent_width, depth + 1,
|
const bool expand_child =
|
||||||
timeline_track->get_expanded() && parent_expanded);
|
((animation_state != Track::NoAnimationState) ||
|
||||||
|
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;
|
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 timeline
|
||||||
} // namespace widgets
|
} // namespace widgets
|
||||||
} // namespace gui
|
} // namespace gui
|
||||||
|
|
|
||||||
|
|
@ -167,9 +167,11 @@ protected:
|
||||||
* false if any of them are collapsed.
|
* false if any of them are collapsed.
|
||||||
* @see update_layout()
|
* @see update_layout()
|
||||||
**/
|
**/
|
||||||
|
#warning is_animating not documented
|
||||||
void layout_headers_recursive(
|
void layout_headers_recursive(
|
||||||
TrackTree::iterator_base parent_iterator,
|
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);
|
const int depth, const bool parent_expanded);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -185,6 +187,10 @@ protected:
|
||||||
boost::shared_ptr<timeline::Track> lookup_timeline_track(
|
boost::shared_ptr<timeline::Track> lookup_timeline_track(
|
||||||
boost::shared_ptr<model::Track> model_track);
|
boost::shared_ptr<model::Track> model_track);
|
||||||
|
|
||||||
|
void begin_animation();
|
||||||
|
|
||||||
|
bool on_animation_tick();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -213,6 +219,14 @@ protected:
|
||||||
* @see update_layout()
|
* @see update_layout()
|
||||||
**/
|
**/
|
||||||
int totalHeight;
|
int totalHeight;
|
||||||
|
|
||||||
|
sigc::connection animationTimer;
|
||||||
|
|
||||||
|
int animation_state;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
static const int AnimationTimeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace timeline
|
} // namespace timeline
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,15 @@ namespace gui {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
namespace timeline {
|
namespace timeline {
|
||||||
|
|
||||||
|
const int Track::NoAnimationState = -1;
|
||||||
|
const int Track::MaxExpandAnimation = 10;
|
||||||
|
|
||||||
Track::Track(TimelineWidget &timeline_widget,
|
Track::Track(TimelineWidget &timeline_widget,
|
||||||
shared_ptr<model::Track> track) :
|
shared_ptr<model::Track> track) :
|
||||||
timelineWidget(timeline_widget),
|
timelineWidget(timeline_widget),
|
||||||
model_track(track),
|
model_track(track),
|
||||||
expanded(true),
|
expanded(true),
|
||||||
|
expandAnimationState(Track::NoAnimationState),
|
||||||
enableButton(Gtk::StockID("track_enabled")),
|
enableButton(Gtk::StockID("track_enabled")),
|
||||||
lockButton(Gtk::StockID("track_unlocked"))
|
lockButton(Gtk::StockID("track_unlocked"))
|
||||||
{
|
{
|
||||||
|
|
@ -109,9 +113,56 @@ Track::get_expanded() const
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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
|
void
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,14 @@ class TimelineViewWindow;
|
||||||
|
|
||||||
class Track : public sigc::trackable
|
class Track : public sigc::trackable
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum ExpandDirection
|
||||||
|
{
|
||||||
|
Expand,
|
||||||
|
Collapse
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Track(TimelineWidget &timeline_widget,
|
Track(TimelineWidget &timeline_widget,
|
||||||
boost::shared_ptr<model::Track> track);
|
boost::shared_ptr<model::Track> track);
|
||||||
|
|
@ -51,7 +59,12 @@ public:
|
||||||
|
|
||||||
bool get_expanded() const;
|
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);
|
void show_header_context_menu(guint button, guint32 time);
|
||||||
|
|
||||||
|
|
@ -59,6 +72,10 @@ public:
|
||||||
TimelineViewWindow* const window)
|
TimelineViewWindow* const window)
|
||||||
const = 0;
|
const = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const int NoAnimationState;
|
||||||
|
static const int MaxExpandAnimation;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//----- Internals -----//
|
//----- Internals -----//
|
||||||
void update_name();
|
void update_name();
|
||||||
|
|
@ -78,6 +95,11 @@ protected:
|
||||||
private:
|
private:
|
||||||
bool expanded;
|
bool expanded;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ExpandDirection expandDirection;
|
||||||
|
int expandAnimationState;
|
||||||
|
|
||||||
//----- Header Widgets ------//
|
//----- Header Widgets ------//
|
||||||
|
|
||||||
Gtk::VBox headerWidget;
|
Gtk::VBox headerWidget;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue