X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fcanvas%2Fcanvas.cc;h=be0f25d7a24f1afed057e85dbc10ba947e4a35fd;hb=c4c7598adbc9e5eca5fe04a23bb7e88fc0989f34;hp=c671ce253efc80e01dfa63216f940ff728eef6e3;hpb=d0dafc171c75146e16081c263e92190570b88a0f;p=ardour.git diff --git a/libs/canvas/canvas.cc b/libs/canvas/canvas.cc index c671ce253e..be0f25d7a2 100644 --- a/libs/canvas/canvas.cc +++ b/libs/canvas/canvas.cc @@ -32,16 +32,21 @@ #include "pbd/stacktrace.h" #include "canvas/canvas.h" +#include "canvas/colors.h" #include "canvas/debug.h" #include "canvas/line.h" #include "canvas/scroll_group.h" +#include "canvas/utils.h" using namespace std; using namespace ArdourCanvas; +uint32_t Canvas::tooltip_timeout_msecs = 750; + /** Construct a new Canvas */ Canvas::Canvas () : _root (this) + , _bg_color (rgba_to_color (0, 1.0, 0.0, 1.0)) { set_epoch (); } @@ -111,7 +116,7 @@ Canvas::render (Rect const & area, Cairo::RefPtr const & context _root.render (*draw, context); -#ifdef CANVAS_DEBUG +#if defined CANVAS_DEBUG && !PLATFORM_WINDOWS if (getenv ("CANVAS_HARLEQUIN_DEBUGGING")) { // This transparently colors the rect being rendered, after it has been drawn. double r = (random() % 65536) /65536.0; @@ -332,6 +337,24 @@ Canvas::queue_draw_item_area (Item* item, Rect area) request_redraw (item->item_to_window (area)); } +void +Canvas::set_tooltip_timeout (uint32_t msecs) +{ + tooltip_timeout_msecs = msecs; +} + +void +Canvas::set_background_color (Color c) +{ + _bg_color = c; + + boost::optional r = _root.bounding_box(); + + if (r) { + request_redraw (_root.item_to_window (r.get())); + } +} + void GtkCanvas::re_enter () { @@ -351,7 +374,8 @@ GtkCanvas::GtkCanvas () { /* these are the events we want to know about */ add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK | - Gdk::SCROLL_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); + Gdk::SCROLL_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK | + Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK); } void @@ -607,12 +631,11 @@ GtkCanvas::deliver_enter_leave (Duple const & point, int state) if (_new_current_item && !_new_current_item->ignore_events()) { enter_event.detail = enter_detail; DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("ENTER %1/%2\n", _new_current_item->whatami(), _new_current_item->name)); + start_tooltip_timeout (_new_current_item); _new_current_item->Event ((GdkEvent*)&enter_event); } - start_tooltip_timeout (_new_current_item); _current_item = _new_current_item; - } @@ -713,6 +736,18 @@ GtkCanvas::item_going_away (Item* item, boost::optional bounding_box) } +void +GtkCanvas::on_size_allocate (Gtk::Allocation& a) +{ + EventBox::on_size_allocate (a); +#ifdef USE_CAIRO_IMAGE_SURFACE + /* allocate an image surface as large as the canvas itself */ + + canvas_image.clear (); + canvas_image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, a.get_width(), a.get_height()); +#endif +} + /** Handler for GDK expose events. * @param ev Event. * @return true if the event was handled. @@ -720,21 +755,38 @@ GtkCanvas::item_going_away (Item* item, boost::optional bounding_box) bool GtkCanvas::on_expose_event (GdkEventExpose* ev) { - Cairo::RefPtr cairo_context = 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), cairo_context); - return true; -} - -/** @return Our Cairo context, or 0 if we don't have one */ -Cairo::RefPtr -GtkCanvas::context () -{ - Glib::RefPtr w = get_window (); - if (!w) { - return Cairo::RefPtr (); +#ifdef USE_CAIRO_IMAGE_SURFACE + if (!canvas_image) { + canvas_image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, get_width(), get_height()); } + Cairo::RefPtr draw_context = Cairo::Context::create (canvas_image); + Cairo::RefPtr window_context = get_window()->create_cairo_context (); +#else + Cairo::RefPtr draw_context = get_window()->create_cairo_context (); +#endif + + /* draw background color */ + + draw_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height); + draw_context->clip_preserve (); + set_source_rgba (draw_context, _bg_color); + draw_context->fill (); + + /* render canvas */ + + render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), draw_context); + +#ifdef USE_CAIRO_IMAGE_SURFACE + /* now blit our private surface back to the GDK one */ + + window_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height); + window_context->clip (); + window_context->set_source (canvas_image, 0, 0); + window_context->set_operator (Cairo::OPERATOR_SOURCE); + window_context->paint (); +#endif - return w->create_cairo_context (); + return true; } /** Handler for GDK scroll events. @@ -763,6 +815,28 @@ GtkCanvas::on_scroll_event (GdkEventScroll* ev) return deliver_event (reinterpret_cast(©)); } +/** Handler for GDK key press events. + * @param ev Event. + * @return true if the event was handled. + */ +bool +GtkCanvas::on_key_press_event (GdkEventKey* ev) +{ + DEBUG_TRACE (PBD::DEBUG::CanvasEvents, "canvas key press\n"); + return deliver_event (reinterpret_cast(ev)); +} + +/** Handler for GDK key release events. + * @param ev Event. + * @return true if the event was handled. + */ +bool +GtkCanvas::on_key_release_event (GdkEventKey* ev) +{ + DEBUG_TRACE (PBD::DEBUG::CanvasEvents, "canvas key release\n"); + return deliver_event (reinterpret_cast(ev)); +} + /** Handler for GDK button press events. * @param ev Event. * @return true if the event was handled. @@ -883,6 +957,22 @@ GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev) bool GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev) { + switch (ev->detail) { + case GDK_NOTIFY_ANCESTOR: + case GDK_NOTIFY_UNKNOWN: + case GDK_NOTIFY_VIRTUAL: + case GDK_NOTIFY_NONLINEAR: + case GDK_NOTIFY_NONLINEAR_VIRTUAL: + /* leaving window, cancel any tooltips */ + stop_tooltip_timeout (); + hide_tooltip (); + break; + default: + /* we don't care about any other kind + of leave event (notably GDK_NOTIFY_INFERIOR) + */ + break; + } _new_current_item = 0; deliver_enter_leave (Duple (ev->x, ev->y), ev->state); return true; @@ -1000,8 +1090,6 @@ GtkCanvas::start_tooltip_timeout (Item* item) little while. */ - std::cerr << "wait for idle now that we're in " << item->name << std::endl; - Glib::signal_idle().connect (sigc::mem_fun (*this, &GtkCanvas::really_start_tooltip_timeout)); } } @@ -1013,12 +1101,8 @@ GtkCanvas::really_start_tooltip_timeout () * wait 1 second and if the timeout isn't cancelled, show the tooltip. */ - std::cerr << "gone idle\n"; - - if (current_tooltip_item) { - std::cerr << "have an item " << current_tooltip_item->name << " now wait 1second\n"; - _current_timeout_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &GtkCanvas::show_tooltip), 1000); + tooltip_timeout_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &GtkCanvas::show_tooltip), tooltip_timeout_msecs); } return false; /* this is called from an idle callback, don't call it again */ @@ -1027,29 +1111,77 @@ GtkCanvas::really_start_tooltip_timeout () void GtkCanvas::stop_tooltip_timeout () { - if (current_tooltip_item) { - std::cerr << "Stop timeout for " << current_tooltip_item->name << "\n"; - } current_tooltip_item = 0; - _current_timeout_connection.disconnect (); + tooltip_timeout_connection.disconnect (); } bool GtkCanvas::show_tooltip () { - if (current_tooltip_item) { - std::cerr << "Would show a tooltip for " << current_tooltip_item->name << '\n'; - } else { - std::cerr << "tooltip timeout expired, but no item\n"; + Rect tooltip_item_bbox; + + if (!current_tooltip_item || current_tooltip_item->tooltip().empty() || !current_tooltip_item->bounding_box()) { + return false; + } + + if (!tooltip_window) { + tooltip_window = new Gtk::Window (Gtk::WINDOW_POPUP); + tooltip_label = manage (new Gtk::Label); + tooltip_label->show (); + tooltip_window->add (*tooltip_label); + tooltip_window->set_border_width (6); + tooltip_window->set_name ("tooltip"); } + tooltip_label->set_text (current_tooltip_item->tooltip()); + + /* figure out where to position the tooltip */ + + Gtk::Widget* toplevel = get_toplevel(); + assert (toplevel); + int pointer_x, pointer_y; + Gdk::ModifierType mask; + + (void) toplevel->get_window()->get_pointer (pointer_x, pointer_y, mask); + + Duple tooltip_window_origin (pointer_x, pointer_y); + + /* convert to root window coordinates */ + + int win_x, win_y; + dynamic_cast(toplevel)->get_position (win_x, win_y); + + tooltip_window_origin = tooltip_window_origin.translate (Duple (win_x, win_y)); + + /* we don't want the pointer to be inside the window when it is + * displayed, because then we generate a leave/enter event pair when + * the window is displayed then hidden - the enter event will + * trigger a new tooltip timeout. + * + * So move the window right of the pointer position by just a enough + * to get it away from the pointer. + */ + + tooltip_window_origin.x += 20; + + /* move the tooltip window into position */ + + tooltip_window->move (tooltip_window_origin.x, tooltip_window_origin.y); + + /* ready to show */ + + tooltip_window->present (); + /* called from a timeout handler, don't call it again */ + return false; } void GtkCanvas::hide_tooltip () { + /* hide it if its there */ + if (tooltip_window) { tooltip_window->hide (); }