more profound changes to canvas scrolling, in particular find appropriate ScrollGroup for Canvas::{window,canvas}_to_{canvas,window}()

This commit is contained in:
Paul Davis 2014-06-03 15:57:56 -04:00
parent d4989ed9ce
commit e0533e9dd7
14 changed files with 167 additions and 91 deletions

View File

@ -2421,21 +2421,31 @@ Editor::get_state ()
return *node;
}
/** @param y y offset from the top of all trackviews.
/** @param y y is in canvas coordinate space, in pixel units
*
* @return pair: TimeAxisView that y is over, layer index.
*
* TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
* in stacked or expanded region display mode, otherwise 0.
*/
std::pair<TimeAxisView *, double>
Editor::trackview_by_y_position (double y)
{
for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
/* convert y into an offset within the trackview group */
std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
if (r.first) {
return r;
ArdourCanvas::Duple top_of_trackviews_canvas = _trackview_group->item_to_canvas (ArdourCanvas::Duple (0, 0));
if (y >= top_of_trackviews_canvas.y) {
y -= top_of_trackviews_canvas.y;
for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
if (r.first) {
return r;
}
}
}

View File

@ -744,9 +744,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
/* The group containing all trackviews. */
ArdourCanvas::Group* _trackview_group;
/* The group used for region motion. Sits on top of _trackview_group */
ArdourCanvas::Group* _region_motion_group;
/* a rect that sits at the bottom of all tracks to act as a drag-no-drop/clickable
* target area.
*/

View File

@ -116,12 +116,8 @@ Editor::initialize_canvas ()
CANVAS_DEBUG_NAME (time_line_group, "time line group");
_trackview_group = new ArdourCanvas::Group (hv_scroll_group);
//_trackview_group->set_scroll_sensitivity (ArdourCanvas::Group::ScrollSensitivity (ArdourCanvas::Group::ScrollsVertically|ArdourCanvas::Group::ScrollsHorizontally));
CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
_region_motion_group = new ArdourCanvas::Group (_trackview_group);
CANVAS_DEBUG_NAME (_region_motion_group, "Canvas Region Motion");
/* TIME BAR CANVAS */
_time_markers_group = new ArdourCanvas::Group (h_scroll_group);
@ -453,7 +449,7 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
/* D-n-D coordinates are window-relative, so convert to "world" coordinates
/* D-n-D coordinates are window-relative, so convert to canvas coordinates
*/
ev.type = GDK_BUTTON_RELEASE;

View File

@ -1,4 +1,4 @@
/*
*
Copyright (C) 2009 Paul Davis
This program is free software; you can redistribute it and/or modify
@ -40,6 +40,8 @@
#include "ardour/region_factory.h"
#include "ardour/session.h"
#include "canvas/scroll_group.h"
#include "editor.h"
#include "i18n.h"
#include "keyboard.h"
@ -654,7 +656,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (_drags->current_pointer_y ());
TimeAxisView* tv = r.first;
if (tv) {
if (tv && tv->view()) {
double layer = r.second;
if (first_move && tv->view()->layer_display() == Stacked) {
@ -700,22 +702,9 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
}
if (first_move) {
rv->drag_start ();
/* Reparent to a non scrolling group so that we can keep the
region selection above all time axis views.
Reparenting means that we will have to move the region view
within its new parent, as the two parent groups have different coordinates.
*/
ArdourCanvas::Group* rvg = rv->get_canvas_group();
Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
rv->get_canvas_group()->reparent (_editor->_region_motion_group);
rv->fake_set_opaque (true);
rvg->set_position (rv_canvas_offset);
rv->raise_to_top ();
}
/* If we have moved tracks, we'll fudge the layer delta so that the
@ -730,7 +719,13 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
if (tv) {
/* The TimeAxisView that this region is now on */
int track_index = i->time_axis_view + delta_time_axis_view;
if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
continue;
}
/* The TimeAxisView that this region is now over */
TimeAxisView* current_tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
/* Ensure it is moved from stacked -> expanded if appropriate */
@ -762,39 +757,50 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
if (_brushing) {
_editor->mouse_brush_insert_region (rv, pending_region_position);
} else {
double x = 0;
double y = 0;
/* Get the y coordinate of the top of the track that this region is now on */
current_tv->canvas_display()->item_to_canvas (x, y);
Duple track_origin;
/* Get the y coordinate of the top of the track that this region is now over */
track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
/* And adjust for the layer that it should be on */
StreamView* cv = current_tv->view ();
switch (cv->layer_display ()) {
case Overlaid:
break;
case Stacked:
y += (cv->layers() - i->layer - 1) * cv->child_height ();
track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
break;
case Expanded:
y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
break;
}
/* need to get the parent of the regionview
* canvas group and get its position in
* equivalent coordinate space as the trackview
* we are now dragging over.
*/
/* Now move the region view */
rv->move (x_delta, y - rv->get_canvas_group()->position().y);
rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0, 0)).y);
}
} else {
double y = 0;
double x = 0;
TimeAxisView* last = _time_axis_views.back();
last->canvas_display()->item_to_canvas (x, y);
y += last->effective_height();
rv->move (x_delta, y - rv->get_canvas_group()->position().y);
i->time_axis_view = -1;
}
/* Only move the region into the empty dropzone at the bottom if the pointer
* is down there.
*/
if (_drags->current_pointer_y() >= _editor->get_trackview_group()->item_to_canvas (Duple (0,0)).y) {
Duple track_origin;
TimeAxisView* last = _time_axis_views.back();
track_origin = last->canvas_display()->item_to_canvas (track_origin);
track_origin.y += last->effective_height();
rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0,0)).y);
i->time_axis_view = -1;
}
}
} /* foreach region */
_total_x_delta += x_delta;

View File

@ -140,12 +140,6 @@ Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) c
return 0;
}
/* adjust for scrolling (the canvas used by Ardour has global scroll
* disabled, so we have to do the adjustment explicitly).
*/
d.translate (ArdourCanvas::Duple (horizontal_adjustment.get_value(), vertical_adjustment.get_value()));
/* event coordinates are in window units, so convert to canvas
*/
@ -2710,8 +2704,6 @@ Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region
return;
}
_region_motion_group->raise_to_top ();
if (Config->get_edit_mode() == Splice) {
_drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
} else {
@ -2728,8 +2720,6 @@ Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* r
return;
}
_region_motion_group->raise_to_top ();
_drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
}

View File

@ -1333,8 +1333,6 @@ Editor::scroll_tracks_up_line ()
bool
Editor::scroll_down_one_track ()
{
double vertical_pos = vertical_adjustment.get_value () + vertical_adjustment.get_page_size() - 1.0;
TrackViewList::reverse_iterator next = track_views.rend();
std::pair<TimeAxisView*,double> res;
@ -1343,7 +1341,8 @@ Editor::scroll_down_one_track ()
continue;
}
res = (*t)->covers_y_position (vertical_pos);
/* find the trackview at the bottom of the trackview group */
res = (*t)->covers_y_position (_visible_canvas_height);
if (res.first) {
break;
@ -1376,7 +1375,8 @@ Editor::scroll_up_one_track ()
continue;
}
res = (*t)->covers_y_position(vertical_pos);
/* find the trackview at the top of the trackview group */
res = (*t)->covers_y_position (0);
if (res.first) {
break;

View File

@ -694,7 +694,18 @@ RegionView::move (double x_delta, double y_delta)
return;
}
get_canvas_group()->move (ArdourCanvas::Duple (x_delta, y_delta));
/* items will not prevent Item::move() moving
* them to a negative x-axis coordinate, which
* is legal, but we don't want that here.
*/
ArdourCanvas::Item *item = get_canvas_group ();
if (item->position().x + x_delta < 0) {
x_delta = -item->position().x; /* move it to zero */
}
item->move (ArdourCanvas::Duple (x_delta, y_delta));
/* note: ghosts never leave their tracks so y_delta for them is always zero */

View File

@ -1151,9 +1151,12 @@ TimeAxisView::color_handler ()
}
/** @return Pair: TimeAxisView, layer index.
* TimeAxisView is non-0 if this object covers y, or one of its children does.
* TimeAxisView is non-0 if this object covers @param y, or one of its children
* does. @param y is an offset from the top of the trackview area.
*
* If the covering object is a child axis, then the child is returned.
* TimeAxisView is 0 otherwise.
*
* Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
* and is in stacked or expanded * region display mode, otherwise 0.
*/

View File

@ -207,13 +207,53 @@ Canvas::item_changed (Item* item, boost::optional<Rect> pre_change_bounding_box)
Duple
Canvas::window_to_canvas (Duple const & d) const
{
/* Find the scroll group that covers d (a window coordinate). Scroll groups are only allowed
* as children of the root group, so we just scan its first level
* children and see what we can find.
*/
std::list<Item*> const& root_children (_root.items());
ScrollGroup* sg = 0;
for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_window (d)) {
break;
}
}
if (sg) {
return d.translate (sg->scroll_offset());
}
/* fallback to global canvas offset ... it would be nice to remove this */
return d.translate (_scroll_offset);
}
Duple
Canvas::canvas_to_window (Duple const & d, bool rounded) const
{
Duple wd = d.translate (-_scroll_offset);
/* Find the scroll group that covers d (a canvas coordinate). Scroll groups are only allowed
* as children of the root group, so we just scan its first level
* children and see what we can find.
*/
std::list<Item*> const& root_children (_root.items());
ScrollGroup* sg = 0;
Duple wd;
for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_canvas (d)) {
break;
}
}
if (sg) {
wd = d.translate (-sg->scroll_offset());
} else {
wd = d.translate (-_scroll_offset);
}
/* Note that this intentionally almost always returns integer coordinates */
@ -225,29 +265,6 @@ Canvas::canvas_to_window (Duple const & d, bool rounded) const
return wd;
}
Rect
Canvas::window_to_canvas (Rect const & r) const
{
return r.translate (Duple (_scroll_offset.x, _scroll_offset.y));
}
Rect
Canvas::canvas_to_window (Rect const & r, bool rounded) const
{
Rect wr = r.translate (Duple (-_scroll_offset.x, -_scroll_offset.y));
/* Note that this intentionally almost always returns integer coordinates */
if (rounded) {
wr.x0 = round (wr.x0);
wr.x1 = round (wr.x1);
wr.y0 = round (wr.y0);
wr.y1 = round (wr.y1);
}
return wr;
}
/** Called when an item has moved.
* @param item Item that has moved.
* @param pre_change_parent_bounding_box The bounding box of the item before

View File

@ -90,8 +90,7 @@ public:
void item_moved (Item *, boost::optional<Rect>);
virtual Cairo::RefPtr<Cairo::Context> context () = 0;
Rect canvas_to_window (Rect const&, bool rounded = true) const;
Rect window_to_canvas (Rect const&) const;
Duple canvas_to_window (Duple const&, bool rounded = true) const;
Duple window_to_canvas (Duple const&) const;

View File

@ -129,6 +129,10 @@ public:
return _position;
}
Duple window_origin() const;
ScrollGroup* scroll_parent() const { return _scroll_parent; }
boost::optional<Rect> bounding_box () const;
Coord height() const;
Coord width() const;

View File

@ -37,6 +37,9 @@ class LIBCANVAS_API ScrollGroup : public Group
void scroll_to (Duple const& d);
Duple scroll_offset() const { return _scroll_offset; }
bool covers_canvas (Duple const& d) const;
bool covers_window (Duple const& d) const;
private:
ScrollSensitivity _scroll_sensitivity;
Duple _scroll_offset;

View File

@ -82,6 +82,20 @@ Item::~Item ()
}
}
Duple
Item::window_origin () const
{
/* This is slightly subtle. Our _position is in the coordinate space of
our parent. So to find out where that is in window coordinates, we
have to ask our parent.
*/
if (_parent) {
return _parent->item_to_window (_position);
} else {
return _parent->item_to_window (Duple (0,0));
}
}
ArdourCanvas::Rect
Item::item_to_parent (ArdourCanvas::Rect const & r) const
{

View File

@ -49,3 +49,29 @@ ScrollGroup::scroll_to (Duple const& d)
_scroll_offset.y = d.y;
}
}
bool
ScrollGroup::covers_canvas (Duple const& d) const
{
boost::optional<Rect> r = bounding_box ();
if (!r) {
return false;
}
return r->contains (d);
}
bool
ScrollGroup::covers_window (Duple const& d) const
{
boost::optional<Rect> r = bounding_box ();
if (!r) {
return false;
}
Rect w = r->translate (-_scroll_offset);
return w.contains (d);
}