Tidied and documented TimelineLayoutHelper
This commit is contained in:
parent
1908ff08a0
commit
9879aef3dd
2 changed files with 304 additions and 187 deletions
|
|
@ -154,7 +154,7 @@ TimelineLayoutHelper::begin_dragging_track(
|
|||
draggingTrackIter = iterator_from_track(model_track);
|
||||
dragBranchHeight = measure_branch_height(draggingTrackIter);
|
||||
|
||||
draggingDrop.relation = None;
|
||||
dropPoint.relation = None;
|
||||
|
||||
return dragging_track;
|
||||
}
|
||||
|
|
@ -163,7 +163,7 @@ void
|
|||
TimelineLayoutHelper::end_dragging_track(bool apply)
|
||||
{
|
||||
if(apply)
|
||||
apply_drop_to_model_tree(draggingDrop);
|
||||
apply_drop_to_model_tree(dropPoint);
|
||||
|
||||
draggingTrackIter.node = NULL;
|
||||
clone_tree_from_sequence();
|
||||
|
|
@ -183,14 +183,17 @@ TimelineLayoutHelper::get_dragging_track_iter() const
|
|||
}
|
||||
|
||||
void
|
||||
TimelineLayoutHelper::drag_to_point(const Gdk::Point &point)
|
||||
TimelineLayoutHelper::drag_to_point(const Gdk::Point &mouse_point)
|
||||
{
|
||||
Drop drop;
|
||||
DropPoint drop;
|
||||
|
||||
// begin_dragging_track must have been called before
|
||||
REQUIRE(is_dragging_track());
|
||||
|
||||
// Apply the scroll offset
|
||||
const Gdk::Point last_point(dragPoint);
|
||||
dragPoint = Gdk::Point(point.get_x(),
|
||||
point.get_y() + timelineWidget.get_y_scroll_offset());
|
||||
dragPoint = Gdk::Point(mouse_point.get_x(),
|
||||
mouse_point.get_y() + timelineWidget.get_y_scroll_offset());
|
||||
|
||||
// Get a test-point
|
||||
// We probe on the bottom edge of the dragging branch if the track is
|
||||
|
|
@ -226,182 +229,12 @@ TimelineLayoutHelper::drag_to_point(const Gdk::Point &point)
|
|||
if(drop.relation != None)
|
||||
{
|
||||
apply_drop_to_layout_tree(drop);
|
||||
draggingDrop = drop;
|
||||
dropPoint = drop;
|
||||
}
|
||||
|
||||
update_layout();
|
||||
}
|
||||
|
||||
TimelineLayoutHelper::Drop
|
||||
TimelineLayoutHelper::attempt_drop(TrackTree::pre_order_iterator target,
|
||||
const Gdk::Point &point)
|
||||
{
|
||||
// Lookup the tracks
|
||||
const shared_ptr<model::Track> model_track(*target);
|
||||
REQUIRE(model_track);
|
||||
const weak_ptr<timeline::Track> timeline_track =
|
||||
lookup_timeline_track(model_track);
|
||||
|
||||
// Calculate coordinates
|
||||
const Gdk::Rectangle &rect = headerBoxes[timeline_track];
|
||||
const int half_height = rect.get_height() / 2;
|
||||
const int y = rect.get_y();
|
||||
const int y_mid = y + half_height;
|
||||
const int full_width = rect.get_x() + rect.get_width();
|
||||
const int x_mid = rect.get_x() + rect.get_width() / 2;
|
||||
|
||||
// Initialize the drop
|
||||
// By specifying relation = None, the default return value will signal
|
||||
// no drop-point was foind at point
|
||||
Drop drop = {target, None};
|
||||
|
||||
if(pt_in_rect(point, Gdk::Rectangle(0, y, full_width, half_height)))
|
||||
{
|
||||
// We're hovering over the upper half of the header
|
||||
drop.relation = Before;
|
||||
}
|
||||
else if(pt_in_rect(point, Gdk::Rectangle(0, y_mid,
|
||||
full_width, half_height)))
|
||||
{
|
||||
// We're hovering over the lower half of the header
|
||||
if(model_track->can_host_children())
|
||||
{
|
||||
if(model_track->get_child_tracks().empty())
|
||||
{
|
||||
// Is our track being dragged after this header?
|
||||
if(dragPoint.get_x() < x_mid)
|
||||
drop.relation = After;
|
||||
else
|
||||
drop.relation = FirstChild;
|
||||
}
|
||||
else
|
||||
drop.relation = LastChild;
|
||||
}
|
||||
else
|
||||
{
|
||||
// When this track cannot be a parent, the dragging track is
|
||||
// simply dropped after
|
||||
drop.relation = After;
|
||||
}
|
||||
}
|
||||
|
||||
return drop;
|
||||
}
|
||||
|
||||
void
|
||||
TimelineLayoutHelper::apply_drop_to_layout_tree(
|
||||
const TimelineLayoutHelper::Drop &drop)
|
||||
{
|
||||
switch(drop.relation)
|
||||
{
|
||||
case None:
|
||||
break;
|
||||
|
||||
case Before:
|
||||
draggingTrackIter = layoutTree.move_before(
|
||||
drop.target, draggingTrackIter);
|
||||
break;
|
||||
|
||||
case After:
|
||||
draggingTrackIter = layoutTree.move_after(
|
||||
drop.target, draggingTrackIter);
|
||||
break;
|
||||
|
||||
case FirstChild:
|
||||
if(draggingTrackIter.node->parent != drop.target.node)
|
||||
{
|
||||
draggingTrackIter = layoutTree.move_ontop(
|
||||
layoutTree.prepend_child(drop.target), draggingTrackIter);
|
||||
}
|
||||
break;
|
||||
|
||||
case LastChild:
|
||||
if(draggingTrackIter.node->parent != drop.target.node)
|
||||
{
|
||||
draggingTrackIter = layoutTree.move_ontop(
|
||||
layoutTree.append_child(drop.target), draggingTrackIter);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT(0); // Unexpected value of relation
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TimelineLayoutHelper::apply_drop_to_model_tree(
|
||||
const TimelineLayoutHelper::Drop &drop)
|
||||
{
|
||||
if(drop.relation == None)
|
||||
return;
|
||||
|
||||
// Freeze the timeline widget - it must be done manually later
|
||||
timelineWidget.freeze_update_tracks();
|
||||
|
||||
// Get the tracks
|
||||
shared_ptr<model::Track> &dragging_track = *draggingTrackIter;
|
||||
REQUIRE(dragging_track);
|
||||
REQUIRE(dragging_track != timelineWidget.sequence);
|
||||
|
||||
shared_ptr<model::Track> &target_track = *drop.target;
|
||||
REQUIRE(target_track);
|
||||
REQUIRE(target_track != timelineWidget.sequence);
|
||||
|
||||
// Detach the track from the old parent
|
||||
shared_ptr<model::ParentTrack> old_parent =
|
||||
dynamic_pointer_cast<model::ParentTrack, model::Track>(
|
||||
model::Track::find_parent(
|
||||
timelineWidget.sequence, dragging_track));
|
||||
REQUIRE(old_parent); // The track must have a parent
|
||||
old_parent->get_child_track_list().remove(dragging_track);
|
||||
|
||||
if(drop.relation == Before || drop.relation == After)
|
||||
{
|
||||
// Find the new parent track
|
||||
shared_ptr<model::ParentTrack> new_parent =
|
||||
dynamic_pointer_cast<model::ParentTrack, model::Track>(
|
||||
model::Track::find_parent(
|
||||
timelineWidget.sequence, target_track));
|
||||
REQUIRE(new_parent); // The track must have a parent
|
||||
|
||||
// Find the destination point
|
||||
observable_list< shared_ptr<model::Track> > &dest =
|
||||
new_parent->get_child_track_list();
|
||||
list< shared_ptr<model::Track> >::iterator iter;
|
||||
for(iter = dest.begin(); iter != dest.end(); iter++)
|
||||
{
|
||||
if(*iter == target_track)
|
||||
break;
|
||||
}
|
||||
REQUIRE(iter != dest.end()); // The target must be
|
||||
// in the destination
|
||||
|
||||
// We have to jump on 1 if we want to insert after
|
||||
if(drop.relation == After)
|
||||
iter++;
|
||||
|
||||
// Insert at this point
|
||||
dest.insert(iter, dragging_track);
|
||||
}
|
||||
else if(drop.relation == FirstChild || drop.relation == LastChild)
|
||||
{
|
||||
shared_ptr<model::ParentTrack> new_parent =
|
||||
dynamic_pointer_cast<model::ParentTrack, model::Track>(
|
||||
target_track);
|
||||
REQUIRE(new_parent); // The track must have a parent
|
||||
|
||||
if(drop.relation == FirstChild)
|
||||
new_parent->get_child_track_list().push_front(dragging_track);
|
||||
else if(drop.relation == LastChild)
|
||||
new_parent->get_child_track_list().push_back(dragging_track);
|
||||
}
|
||||
else ASSERT(0); // Unexpected value of relation
|
||||
|
||||
// Freeze the timeline widget - we will do it manually
|
||||
timelineWidget.freeze_update_tracks();
|
||||
}
|
||||
|
||||
int
|
||||
TimelineLayoutHelper::get_total_height() const
|
||||
{
|
||||
|
|
@ -612,6 +445,176 @@ TimelineLayoutHelper::on_animation_tick()
|
|||
return animating;
|
||||
}
|
||||
|
||||
TimelineLayoutHelper::DropPoint
|
||||
TimelineLayoutHelper::attempt_drop(TrackTree::pre_order_iterator target,
|
||||
const Gdk::Point &point)
|
||||
{
|
||||
// Lookup the tracks
|
||||
const shared_ptr<model::Track> model_track(*target);
|
||||
REQUIRE(model_track);
|
||||
const weak_ptr<timeline::Track> timeline_track =
|
||||
lookup_timeline_track(model_track);
|
||||
|
||||
// Calculate coordinates
|
||||
const Gdk::Rectangle &rect = headerBoxes[timeline_track];
|
||||
const int half_height = rect.get_height() / 2;
|
||||
const int y = rect.get_y();
|
||||
const int y_mid = y + half_height;
|
||||
const int full_width = rect.get_x() + rect.get_width();
|
||||
const int x_mid = rect.get_x() + rect.get_width() / 2;
|
||||
|
||||
// Initialize the drop
|
||||
// By specifying relation = None, the default return value will signal
|
||||
// no drop-point was foind at point
|
||||
DropPoint drop = {target, None};
|
||||
|
||||
if(pt_in_rect(point, Gdk::Rectangle(0, y, full_width, half_height)))
|
||||
{
|
||||
// We're hovering over the upper half of the header
|
||||
drop.relation = Before;
|
||||
}
|
||||
else if(pt_in_rect(point, Gdk::Rectangle(0, y_mid,
|
||||
full_width, half_height)))
|
||||
{
|
||||
// We're hovering over the lower half of the header
|
||||
if(model_track->can_host_children())
|
||||
{
|
||||
if(model_track->get_child_tracks().empty())
|
||||
{
|
||||
// Is our track being dragged after this header?
|
||||
if(dragPoint.get_x() < x_mid)
|
||||
drop.relation = After;
|
||||
else
|
||||
drop.relation = FirstChild;
|
||||
}
|
||||
else
|
||||
drop.relation = LastChild;
|
||||
}
|
||||
else
|
||||
{
|
||||
// When this track cannot be a parent, the dragging track is
|
||||
// simply dropped after
|
||||
drop.relation = After;
|
||||
}
|
||||
}
|
||||
|
||||
return drop;
|
||||
}
|
||||
|
||||
void
|
||||
TimelineLayoutHelper::apply_drop_to_layout_tree(
|
||||
const TimelineLayoutHelper::DropPoint &drop)
|
||||
{
|
||||
switch(drop.relation)
|
||||
{
|
||||
case None:
|
||||
break;
|
||||
|
||||
case Before:
|
||||
draggingTrackIter = layoutTree.move_before(
|
||||
drop.target, draggingTrackIter);
|
||||
break;
|
||||
|
||||
case After:
|
||||
draggingTrackIter = layoutTree.move_after(
|
||||
drop.target, draggingTrackIter);
|
||||
break;
|
||||
|
||||
case FirstChild:
|
||||
if(draggingTrackIter.node->parent != drop.target.node)
|
||||
{
|
||||
draggingTrackIter = layoutTree.move_ontop(
|
||||
layoutTree.prepend_child(drop.target), draggingTrackIter);
|
||||
}
|
||||
break;
|
||||
|
||||
case LastChild:
|
||||
if(draggingTrackIter.node->parent != drop.target.node)
|
||||
{
|
||||
draggingTrackIter = layoutTree.move_ontop(
|
||||
layoutTree.append_child(drop.target), draggingTrackIter);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT(0); // Unexpected value of relation
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TimelineLayoutHelper::apply_drop_to_model_tree(
|
||||
const TimelineLayoutHelper::DropPoint &drop)
|
||||
{
|
||||
if(drop.relation == None)
|
||||
return;
|
||||
|
||||
// Freeze the timeline widget - it must be done manually later
|
||||
timelineWidget.freeze_update_tracks();
|
||||
|
||||
// Get the tracks
|
||||
shared_ptr<model::Track> &dragging_track = *draggingTrackIter;
|
||||
REQUIRE(dragging_track);
|
||||
REQUIRE(dragging_track != timelineWidget.sequence);
|
||||
|
||||
shared_ptr<model::Track> &target_track = *drop.target;
|
||||
REQUIRE(target_track);
|
||||
REQUIRE(target_track != timelineWidget.sequence);
|
||||
|
||||
// Detach the track from the old parent
|
||||
shared_ptr<model::ParentTrack> old_parent =
|
||||
dynamic_pointer_cast<model::ParentTrack, model::Track>(
|
||||
model::Track::find_parent(
|
||||
timelineWidget.sequence, dragging_track));
|
||||
REQUIRE(old_parent); // The track must have a parent
|
||||
old_parent->get_child_track_list().remove(dragging_track);
|
||||
|
||||
if(drop.relation == Before || drop.relation == After)
|
||||
{
|
||||
// Find the new parent track
|
||||
shared_ptr<model::ParentTrack> new_parent =
|
||||
dynamic_pointer_cast<model::ParentTrack, model::Track>(
|
||||
model::Track::find_parent(
|
||||
timelineWidget.sequence, target_track));
|
||||
REQUIRE(new_parent); // The track must have a parent
|
||||
|
||||
// Find the destination point
|
||||
observable_list< shared_ptr<model::Track> > &dest =
|
||||
new_parent->get_child_track_list();
|
||||
list< shared_ptr<model::Track> >::iterator iter;
|
||||
for(iter = dest.begin(); iter != dest.end(); iter++)
|
||||
{
|
||||
if(*iter == target_track)
|
||||
break;
|
||||
}
|
||||
REQUIRE(iter != dest.end()); // The target must be
|
||||
// in the destination
|
||||
|
||||
// We have to jump on 1 if we want to insert after
|
||||
if(drop.relation == After)
|
||||
iter++;
|
||||
|
||||
// Insert at this point
|
||||
dest.insert(iter, dragging_track);
|
||||
}
|
||||
else if(drop.relation == FirstChild || drop.relation == LastChild)
|
||||
{
|
||||
shared_ptr<model::ParentTrack> new_parent =
|
||||
dynamic_pointer_cast<model::ParentTrack, model::Track>(
|
||||
target_track);
|
||||
REQUIRE(new_parent); // The track must have a parent
|
||||
|
||||
if(drop.relation == FirstChild)
|
||||
new_parent->get_child_track_list().push_front(dragging_track);
|
||||
else if(drop.relation == LastChild)
|
||||
new_parent->get_child_track_list().push_back(dragging_track);
|
||||
}
|
||||
else ASSERT(0); // Unexpected value of relation
|
||||
|
||||
// Freeze the timeline widget - we will do it manually
|
||||
timelineWidget.freeze_update_tracks();
|
||||
}
|
||||
|
||||
} // namespace timeline
|
||||
} // namespace widgets
|
||||
} // namespace gui
|
||||
|
|
|
|||
|
|
@ -127,16 +127,40 @@ public:
|
|||
**/
|
||||
boost::shared_ptr<timeline::Track> track_from_y(int y);
|
||||
|
||||
/**
|
||||
* Begins to drag the track under mouse_point, if there is one.
|
||||
* @param mouse_point The mouse point to begin dragging from, measured
|
||||
* in pixels from the top left of the header container widget.
|
||||
**/
|
||||
boost::shared_ptr<timeline::Track>
|
||||
begin_dragging_track(const Gdk::Point &mouse_point);
|
||||
|
||||
/**
|
||||
* Drops the dragging track.
|
||||
* @param apply true if the model tree should be modified.
|
||||
**/
|
||||
void end_dragging_track(bool apply);
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if a track is being dragged.
|
||||
**/
|
||||
bool is_dragging_track() const;
|
||||
|
||||
/**
|
||||
* Gets the iterator of the layout tree node that is being dragged.
|
||||
**/
|
||||
TrackTree::pre_order_iterator get_dragging_track_iter() const;
|
||||
|
||||
void drag_to_point(const Gdk::Point &point);
|
||||
/**
|
||||
* Drags the dragging branch to a new mouse point.
|
||||
* @param mouse_point The mouse point to drag the dragging track to.
|
||||
* This point is in pixels relative to the top left of the header
|
||||
* container.
|
||||
* @remarks drag_to_point may only be called after
|
||||
* begin_dragging_track and before end_dragging_point have been
|
||||
* called.
|
||||
**/
|
||||
void drag_to_point(const Gdk::Point &mouse_point);
|
||||
|
||||
/**
|
||||
* Returns the total height in pixels of the layout tree.
|
||||
|
|
@ -146,6 +170,9 @@ public:
|
|||
**/
|
||||
int get_total_height() const;
|
||||
|
||||
/**
|
||||
* Returns true if the layout is currently animating.
|
||||
**/
|
||||
bool is_animating() const;
|
||||
|
||||
/**
|
||||
|
|
@ -157,22 +184,65 @@ public:
|
|||
**/
|
||||
TrackTree::pre_order_iterator iterator_from_track(
|
||||
boost::shared_ptr<model::Track> model_track);
|
||||
|
||||
|
||||
/**
|
||||
* A function that recursively calculates the visible height of a
|
||||
* branch taking into account branches that are expanded or collapsed.
|
||||
* @param parent_iterator The parent of the branch to measure. This
|
||||
* node and all the child nodes will be included in the measurement.
|
||||
* @return Returns the height of the branch in pixels.
|
||||
**/
|
||||
int measure_branch_height(TrackTree::iterator_base parent_iterator);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* An enum to specify the relationship between a tree node, and
|
||||
* another node which is going to be inserted adjacent.
|
||||
**/
|
||||
enum TreeRelation
|
||||
{
|
||||
/**
|
||||
* No relation
|
||||
**/
|
||||
None,
|
||||
|
||||
/**
|
||||
* The node will be inserted immediately before this one.
|
||||
**/
|
||||
Before,
|
||||
|
||||
/**
|
||||
* The node will be inserted immediately after this one.
|
||||
**/
|
||||
After,
|
||||
|
||||
/**
|
||||
* The node will be inserted as the first child of this one.
|
||||
**/
|
||||
FirstChild,
|
||||
|
||||
/**
|
||||
* The node will be inserted as the last child of this one.
|
||||
**/
|
||||
LastChild
|
||||
};
|
||||
|
||||
struct Drop
|
||||
/**
|
||||
* A structure used to specify where a track will be dropped when
|
||||
* dragging ends.
|
||||
**/
|
||||
struct DropPoint
|
||||
{
|
||||
/**
|
||||
* Specifies the target node onto which the dragging track will be
|
||||
* dropped.
|
||||
**/
|
||||
TrackTree::pre_order_iterator target;
|
||||
|
||||
/**
|
||||
* The where to drop the dragging track in relation to target.
|
||||
**/
|
||||
TreeRelation relation;
|
||||
};
|
||||
|
||||
|
|
@ -237,14 +307,34 @@ protected:
|
|||
* The animation timer tick callback.
|
||||
**/
|
||||
bool on_animation_tick();
|
||||
|
||||
TimelineLayoutHelper::Drop
|
||||
|
||||
/**
|
||||
* Attempts to find a drop point on the target node at point.
|
||||
* @param[in] target The iterator of the target node on which to make
|
||||
* the attempt.
|
||||
* @param[in] point The point on which do to the test.
|
||||
* @remarks This function hit-tests a header looking to see if the
|
||||
* point is hovering over it, and if it is, it works out what part of
|
||||
* the header, and therefore what drop location the user us gesturally
|
||||
* pointing to.
|
||||
**/
|
||||
TimelineLayoutHelper::DropPoint
|
||||
attempt_drop(TrackTree::pre_order_iterator target,
|
||||
const Gdk::Point &point);
|
||||
|
||||
void apply_drop_to_layout_tree(const Drop &drop);
|
||||
/**
|
||||
* Drops the dragging track to a new location in the layout tree as
|
||||
* specified by drop.
|
||||
* @param[in] drop The point in the tree to drop onto.
|
||||
**/
|
||||
void apply_drop_to_layout_tree(const DropPoint &drop);
|
||||
|
||||
void apply_drop_to_model_tree(const Drop &drop);
|
||||
/**
|
||||
* Drops the dragging track to a new location in the model tree as
|
||||
* specified by drop.
|
||||
* @param[in] drop The point in the tree to drop onto.
|
||||
**/
|
||||
void apply_drop_to_model_tree(const DropPoint &drop);
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
|
@ -274,15 +364,39 @@ protected:
|
|||
**/
|
||||
int totalHeight;
|
||||
|
||||
/**
|
||||
* The iterator of the layoutTree node that is presently being
|
||||
* dragged.
|
||||
* @remarks draggingTrackIter.node is set to NULL when no drag is
|
||||
* taking place.
|
||||
**/
|
||||
TrackTree::pre_order_iterator draggingTrackIter;
|
||||
|
||||
|
||||
/**
|
||||
* The offset of the mouse relative to the top-left corner of the
|
||||
* dragging track.
|
||||
**/
|
||||
Gdk::Point dragStartOffset;
|
||||
|
||||
/**
|
||||
* The coordinates of the dragging mouse in pixels, measured from the
|
||||
* top left of the whole layout.
|
||||
* @remarks This value is changed by begin_dragging_track and
|
||||
* drag_to_point
|
||||
**/
|
||||
Gdk::Point dragPoint;
|
||||
|
||||
/**
|
||||
* The total height of the dragging branch in pixels.
|
||||
* @remarks This value is updated by begin_dragging_track
|
||||
**/
|
||||
int dragBranchHeight;
|
||||
|
||||
Drop draggingDrop;
|
||||
/**
|
||||
* The tree point the the user is currently hovering on.
|
||||
* @remarks This value is updated by drag_to_point.
|
||||
**/
|
||||
DropPoint dropPoint;
|
||||
|
||||
/**
|
||||
* The connection to the animation timer.
|
||||
|
|
|
|||
Loading…
Reference in a new issue