many changes to get the cairo-canvas version much, much more functional. still problems with a lot of subtle and not-so-subtle issues

This commit is contained in:
Paul Davis 2013-04-11 20:19:22 -04:00
parent 1fda7b293a
commit ee1f0520a8
10 changed files with 175 additions and 123 deletions

View file

@ -24,6 +24,7 @@
#include <cassert>
#include <gtkmm/adjustment.h>
#include <gtkmm/label.h>
#include "pbd/xml++.h"
#include "pbd/compose.h"
@ -39,6 +40,8 @@ using namespace ArdourCanvas;
Canvas::Canvas ()
: _root (this)
, _log_renders (true)
, _scroll_offset_x (0)
, _scroll_offset_y (0)
{
set_epoch ();
}
@ -49,6 +52,8 @@ Canvas::Canvas ()
Canvas::Canvas (XMLTree const * tree)
: _root (this)
, _log_renders (true)
, _scroll_offset_x (0)
, _scroll_offset_y (0)
{
set_epoch ();
@ -70,6 +75,13 @@ Canvas::Canvas (XMLTree const * tree)
}
}
void
Canvas::scroll_to (Coord x, Coord y)
{
_scroll_offset_x = x;
_scroll_offset_y = y;
}
/** Render an area of the canvas.
* @param area Area in canvas coordinates.
* @param context Cairo context to render to.
@ -85,9 +97,21 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context
}
#endif
checkpoint ("render", "-> render");
// checkpoint ("render", "-> render");
render_count = 0;
#ifdef CANVAS_DEBUG
if (getenv ("ARDOUR_HARLEQUIN_CANVAS")) {
/* light up the canvas to show redraws */
context->set_source_rgba (random()%255 / 255.0,
random()%255 / 255.0,
random()%255 / 255.0,
255);
context->rectangle (area.x0, area.y0, area.width(), area.height());
context->fill ();
}
#endif
context->save ();
/* clip to the requested area */
@ -97,7 +121,7 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context
boost::optional<Rect> root_bbox = _root.bounding_box();
if (!root_bbox) {
/* the root has no bounding box, so there's nothing to render */
checkpoint ("render", "no root bbox");
// checkpoint ("render", "no root bbox");
context->restore ();
return;
}
@ -107,7 +131,9 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context
/* there's a common area between the root and the requested
area, so render it.
*/
checkpoint ("render", "... root");
// checkpoint ("render", "... root");
context->stroke ();
_root.render (*draw, context);
}
@ -117,7 +143,7 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context
context->restore ();
checkpoint ("render", "<- render");
// checkpoint ("render", "<- render");
}
ostream&
@ -412,52 +438,6 @@ GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
}
}
/** Construct an ImageCanvas.
* @param size Size in pixels.
*/
ImageCanvas::ImageCanvas (Duple size)
: _surface (Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, size.x, size.y))
{
_context = Cairo::Context::create (_surface);
}
/** Construct an ImageCanvas from an XML tree.
* @param tree XML Tree.
* @param size Size in pixels.
*/
ImageCanvas::ImageCanvas (XMLTree const * tree, Duple size)
: Canvas (tree)
, _surface (Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, size.x, size.y))
{
_context = Cairo::Context::create (_surface);
}
/** Render the canvas to our pixbuf.
* @param area Area to render, in canvas coordinates.
*/
void
ImageCanvas::render_to_image (Rect const & area) const
{
render (area, _context);
}
/** Write our pixbuf to a PNG file.
* @param f PNG file name.
*/
void
ImageCanvas::write_to_png (string const & f)
{
assert (_surface);
_surface->write_to_png (f);
}
/** @return Our Cairo context */
Cairo::RefPtr<Cairo::Context>
ImageCanvas::context ()
{
return _context;
}
/** Handler for GDK expose events.
* @param ev Event.
* @return true if the event was handled.
@ -465,8 +445,30 @@ ImageCanvas::context ()
bool
GtkCanvas::on_expose_event (GdkEventExpose* ev)
{
Cairo::RefPtr<Cairo::Context> c = get_window()->create_cairo_context ();
render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), c);
/* WINDOW CANVAS
* 0,0 _scroll_offset_x, _scroll_offset_y
*/
/* render using canvas coordinates */
Rect canvas_area (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height);
canvas_area = canvas_area.translate (Duple (_scroll_offset_x, _scroll_offset_y));
/* things are going to render to the cairo surface with canvas
* coordinates:
*
* an item at window/cairo 0,0 will have canvas_coords _scroll_offset_x,_scroll_offset_y
*
* let them render at their natural coordinates by using cairo_translate()
*/
c->translate (-_scroll_offset_x, -_scroll_offset_y);
render (canvas_area, c);
return true;
}
@ -489,10 +491,18 @@ GtkCanvas::context ()
bool
GtkCanvas::on_button_press_event (GdkEventButton* ev)
{
/* translate event coordinates from window to canvas */
GdkEvent copy = *((GdkEvent*)ev);
Duple where = window_to_canvas (Duple (ev->x, ev->y));
copy.button.x = where.x;
copy.button.y = where.y;
/* Coordinates in the event will be canvas coordinates, correctly adjusted
for scroll if this GtkCanvas is in a GtkCanvasViewport.
*/
return button_handler (ev);
return button_handler ((GdkEventButton*) &copy);
}
/** Handler for GDK button release events.
@ -501,11 +511,19 @@ GtkCanvas::on_button_press_event (GdkEventButton* ev)
*/
bool
GtkCanvas::on_button_release_event (GdkEventButton* ev)
{
{
/* translate event coordinates from window to canvas */
GdkEvent copy = *((GdkEvent*)ev);
Duple where = window_to_canvas (Duple (ev->x, ev->y));
copy.button.x = where.x;
copy.button.y = where.y;
/* Coordinates in the event will be canvas coordinates, correctly adjusted
for scroll if this GtkCanvas is in a GtkCanvasViewport.
*/
return button_handler (ev);
return button_handler ((GdkEventButton*) &copy);
}
/** Handler for GDK motion events.
@ -515,18 +533,27 @@ GtkCanvas::on_button_release_event (GdkEventButton* ev)
bool
GtkCanvas::on_motion_notify_event (GdkEventMotion* ev)
{
/* translate event coordinates from window to canvas */
GdkEvent copy = *((GdkEvent*)ev);
Duple where = window_to_canvas (Duple (ev->x, ev->y));
copy.motion.x = where.x;
copy.motion.y = where.y;
/* Coordinates in the event will be canvas coordinates, correctly adjusted
for scroll if this GtkCanvas is in a GtkCanvasViewport.
*/
return motion_notify_handler (ev);
return motion_notify_handler ((GdkEventMotion*) &copy);
}
/** Called to request a redraw of our canvas.
* @param area Area to redraw, in canvas coordinates.
*/
void
GtkCanvas::request_redraw (Rect const & area)
GtkCanvas::request_redraw (Rect const & request)
{
Rect area = canvas_to_window (request);
queue_draw_area (floor (area.x0), floor (area.y0), ceil (area.x1) - floor (area.x0), ceil (area.y1) - floor (area.y0));
}
@ -574,17 +601,56 @@ GtkCanvas::ungrab ()
* @param vadj Adjustment to use for vertica scrolling.
*/
GtkCanvasViewport::GtkCanvasViewport (Gtk::Adjustment& hadj, Gtk::Adjustment& vadj)
: Viewport (hadj, vadj)
: Alignment (0, 0, 1.0, 1.0)
, hadjustment (hadj)
, vadjustment (vadj)
{
add (_canvas);
hadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
vadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
}
void
GtkCanvasViewport::scrolled ()
{
_canvas.scroll_to (hadjustment.get_value(), vadjustment.get_value());
queue_draw ();
}
Duple
GtkCanvas::window_to_canvas (Duple const & d) const
{
return d.translate (Duple (_scroll_offset_x, _scroll_offset_y));
}
Duple
GtkCanvas::canvas_to_window (Duple const & d) const
{
return d.translate (Duple (-_scroll_offset_x, -_scroll_offset_y));
}
Rect
GtkCanvas::window_to_canvas (Rect const & r) const
{
return r.translate (Duple (_scroll_offset_x, _scroll_offset_y));
}
Rect
GtkCanvas::canvas_to_window (Rect const & r) const
{
return r.translate (Duple (-_scroll_offset_x, -_scroll_offset_y));
}
/** Handler for when GTK asks us what minimum size we want.
* @param req Requsition to fill in.
*/
void
GtkCanvasViewport::on_size_request (Gtk::Requisition* req)
{
/* force the canvas to size itself */
// _canvas.root()->bounding_box();
req->width = 16;
req->height = 16;
}
@ -599,15 +665,16 @@ GtkCanvasViewport::on_size_request (Gtk::Requisition* req)
void
GtkCanvasViewport::window_to_canvas (int wx, int wy, Coord& cx, Coord& cy) const
{
cx = wx + get_hadjustment()->get_value ();
cy = wy + get_vadjustment()->get_value ();
Duple d = _canvas.window_to_canvas (Duple (wx, wy));
cx = d.x;
cy = d.y;
}
/** @return The visible area of the canvas, in canvas coordinates */
Rect
GtkCanvasViewport::visible_area () const
{
Distance const xo = get_hadjustment()->get_value ();
Distance const yo = get_vadjustment()->get_value ();
Distance const xo = hadjustment.get_value ();
Distance const yo = vadjustment.get_value ();
return Rect (xo, yo, xo + get_allocation().get_width (), yo + get_allocation().get_height ());
}

View file

@ -26,7 +26,7 @@
#include <gdkmm/window.h>
#include <gtkmm/eventbox.h>
#include <gtkmm/viewport.h>
#include <gtkmm/alignment.h>
#include <cairomm/surface.h>
#include <cairomm/context.h>
#include "pbd/signals.h"
@ -91,6 +91,8 @@ public:
_log_renders = log;
}
void scroll_to (Coord x, Coord y);
std::string indent() const;
std::string render_indent() const;
void dump (std::ostream&) const;
@ -103,39 +105,9 @@ protected:
mutable std::list<Rect> _renders;
bool _log_renders;
};
/** A Canvas which renders onto an in-memory pixbuf. In Ardour's context,
* this is most useful for testing.
*/
class ImageCanvas : public Canvas
{
public:
ImageCanvas (Duple size = Duple (1024, 1024));
ImageCanvas (XMLTree const *, Duple size = Duple (1024, 1024));
void request_redraw (Rect const &) {
/* XXX */
}
void request_size (Duple) {
/* XXX */
}
void grab (Item *) {}
void ungrab () {}
void render_to_image (Rect const &) const;
void clear ();
void write_to_png (std::string const &);
Cairo::RefPtr<Cairo::Context> context ();
private:
/** our Cairo surface */
Cairo::RefPtr<Cairo::Surface> _surface;
/** our Cairo context */
Cairo::RefPtr<Cairo::Context> _context;
Coord _scroll_offset_x;
Coord _scroll_offset_y;
};
/** A canvas which renders onto a GTK EventBox */
@ -152,6 +124,12 @@ public:
Cairo::RefPtr<Cairo::Context> context ();
Rect canvas_to_window (Rect const&) const;
Rect window_to_canvas (Rect const&) const;
Duple canvas_to_window (Duple const&) const;
Duple window_to_canvas (Duple const&) const;
protected:
bool on_expose_event (GdkEventExpose *);
bool on_button_press_event (GdkEventButton *);
@ -166,16 +144,20 @@ private:
void item_going_away (Item *, boost::optional<Rect>);
bool send_leave_event (Item const *, double, double) const;
/** the item that the mouse is currently over, or 0 */
Item const * _current_item;
/** the item that is currently grabbed, or 0 */
Item const * _grabbed_item;
};
/** A GTK::Viewport with a GtkCanvas inside it. This provides a GtkCanvas
* that can be scrolled.
/** A GTK::Alignment with a GtkCanvas inside it plus some Gtk::Adjustments for
* scrolling.
*
* This provides a GtkCanvas that can be scrolled. It does NOT implement the
* Gtk::Scrollable interface.
*/
class GtkCanvasViewport : public Gtk::Viewport
class GtkCanvasViewport : public Gtk::Alignment
{
public:
GtkCanvasViewport (Gtk::Adjustment &, Gtk::Adjustment &);
@ -194,6 +176,10 @@ protected:
private:
/** our GtkCanvas */
GtkCanvas _canvas;
Gtk::Adjustment& hadjustment;
Gtk::Adjustment& vadjustment;
void scrolled ();
};
}

View file

@ -277,7 +277,7 @@ void
Group::dump (ostream& o) const
{
o << _canvas->indent();
o << "Group " << this;
o << "Group " << this << " [" << name << ']';
o << " Items: " << _items.size();
o << " Visible ? " << _visible;

View file

@ -73,6 +73,7 @@ Item::set_position (Duple p)
{
boost::optional<Rect> bbox = bounding_box ();
boost::optional<Rect> pre_change_parent_bounding_box;
if (bbox) {
pre_change_parent_bounding_box = item_to_parent (bbox.get());
}

View file

@ -52,7 +52,7 @@ Rect
Rect::translate (Duple t) const
{
Rect r;
r.x0 = safe_add (x0, t.x);
r.y0 = safe_add (y0, t.y);
r.x1 = safe_add (x1, t.x);