From 4a00bdf04a8c1a88a3041b409c6a8db601b8adc9 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 6 Sep 2014 00:41:56 +0200 Subject: [PATCH] fundamentally change ArdourButton PangoLayout handling This fixes issues where button text was set during static initialization where the widget style was not yet available, and a lot of related issues where the text was set before style. --- gtk2_ardour/ardour_button.cc | 197 ++++++++++++++++++++++++----------- gtk2_ardour/ardour_button.h | 29 +++--- gtk2_ardour/editor_ops.cc | 2 +- gtk2_ardour/meter_strip.cc | 8 +- gtk2_ardour/mixer_strip.cc | 13 +-- 5 files changed, 158 insertions(+), 91 deletions(-) diff --git a/gtk2_ardour/ardour_button.cc b/gtk2_ardour/ardour_button.cc index 7d42726884..1f96ad8680 100644 --- a/gtk2_ardour/ardour_button.cc +++ b/gtk2_ardour/ardour_button.cc @@ -85,6 +85,9 @@ ArdourButton::ArdourButton (Element e) , _focused (false) , _fixed_colors_set (false) , _fallthrough_to_parent (false) + , _layout_ellipsize_width (-1) + , _ellipsis (Pango::ELLIPSIZE_NONE) + , _update_colors_and_patterns (true) { ARDOUR_UI_UTILS::ColorsChanged.connect (sigc::mem_fun (*this, &ArdourButton::color_handler)); } @@ -135,35 +138,19 @@ ArdourButton::~ArdourButton() if (led_inset_pattern) { cairo_pattern_destroy (led_inset_pattern); } - } void ArdourButton::set_text (const std::string& str) { _text = str; - - if (!_layout && !_text.empty()) { - _layout = Pango::Layout::create (get_pango_context()); + if (!is_realized()) { + return; } - - if (_layout) { - _layout->set_text (str); + ensure_layout (); + if (_layout && _layout->get_text() != _text) { + _layout->set_text (_text); } - - queue_resize (); -} - -void -ArdourButton::set_markup (const std::string& str) -{ - _text = str; - - if (!_layout) { - _layout = Pango::Layout::create (get_pango_context()); - } - - _layout->set_markup (str); queue_resize (); } @@ -185,6 +172,13 @@ ArdourButton::render (cairo_t* cr, cairo_rectangle_t *) { uint32_t text_color; uint32_t led_color; + + if (_update_colors_and_patterns) { + set_colors (); + build_patterns (); + _update_colors_and_patterns = false; + } + if ( active_state() == Gtkmm2ext::ExplicitActive ) { text_color = text_active_color; led_color = led_active_color; @@ -363,6 +357,25 @@ ArdourButton::render (cairo_t* cr, cairo_rectangle_t *) // Text, if any if (!_pixbuf && ((_elements & Text)==Text) && !_text.empty()) { + assert(_layout); +#if 0 // DEBUG style (print on hover) + if (_hovering) { + bool layout_font = true; + Pango::FontDescription fd = _layout->get_font_description(); + if (fd.gobj() == NULL) { + layout_font = false; + fd = get_pango_context()->get_font_description(); + } + printf("%s: f:%dx%d bh:%.0f tw:%d (%dx%d) %s\"%s\"\n", + get_name().c_str(), + char_pixel_width(), char_pixel_height(), + ceil(_text_height * BASELINESTRETCH), + _text_width, + get_width(), get_height(), + layout_font ? "L:" : "W:", + fd.to_string().c_str()); + } +#endif cairo_save (cr); cairo_rectangle (cr, 2, 1, get_width() - 4, get_height() - 2); @@ -487,11 +500,11 @@ ArdourButton::render (cairo_t* cr, cairo_rectangle_t *) } } - //user is currently pressing the button. black outline helps to indicate this + //user is currently pressing the button. dark outline helps to indicate this if (_grabbed && !(_elements & (Inactive|Menu))) { rounded_function (cr, 1, 1, get_width() - 2, get_height() - 2, _corner_radius); cairo_set_line_width(cr, 2); - cairo_set_source_rgba (cr, 0.1, 0.1, 0.1, .5); // XXX no longer 'black' + cairo_set_source_rgba (cr, 0.1, 0.1, 0.1, .5); cairo_stroke (cr); } @@ -530,15 +543,26 @@ ArdourButton::set_diameter (float d) { _diameter = (d*2) + 5.0; - build_patterns (); - queue_resize (); + if (is_realized()) { + queue_resize (); + } } void ArdourButton::set_corner_radius (float r) { _corner_radius = r; - set_dirty (); + CairoWidget::set_dirty (); +} + +void +ArdourButton::on_realize() +{ + CairoWidget::on_realize (); + ensure_layout (); + if (_layout && _layout->get_text() != _text) { + _layout->set_text (_text); + } } void @@ -553,8 +577,9 @@ ArdourButton::on_size_request (Gtk::Requisition* req) if ((_elements & Text) && !_text.empty()) { int ignored; - _layout->get_pixel_size (_text_width, ignored); _text_height = char_pixel_height (); + // if _layout does not exist, char_pixel_height() creates it, + _layout->get_pixel_size (_text_width, ignored); req->width += rint(1.6 * char_pixel_width()); // padding req->width += _text_width; req->height = std::max(req->height, (int) ceil(_text_height * BASELINESTRETCH + 1.0)); @@ -619,6 +644,7 @@ ArdourButton::set_colors () led_active_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led active", name)); led_inactive_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led", name)); } + /** * This sets the colors used for rendering based on two fixed values, rather * than basing them on the button name, and thus information in the GUI config @@ -700,7 +726,7 @@ ArdourButton::build_patterns () cairo_pattern_add_color_stop_rgba (led_inset_pattern, 1, 1,1,1, 0.7); } - set_dirty (); + CairoWidget::set_dirty (); } void @@ -724,7 +750,7 @@ ArdourButton::on_button_press_event (GdkEventButton *ev) } _grabbed = true; - queue_draw (); + CairoWidget::set_dirty (); if (!_act_on_release) { if (_action) { @@ -751,7 +777,7 @@ ArdourButton::on_button_release_event (GdkEventButton *ev) } _grabbed = false; - queue_draw (); + CairoWidget::set_dirty (); if (_hovering) { signal_clicked (); @@ -779,9 +805,8 @@ ArdourButton::set_distinct_led_click (bool yn) void ArdourButton::color_handler () { - set_colors (); - build_patterns (); - set_dirty (); + _update_colors_and_patterns = true; + CairoWidget::set_dirty (); } void @@ -789,39 +814,39 @@ ArdourButton::on_size_allocate (Allocation& alloc) { CairoWidget::on_size_allocate (alloc); setup_led_rect (); - build_patterns (); + _update_colors_and_patterns = true; } void ArdourButton::set_controllable (boost::shared_ptr c) { - watch_connection.disconnect (); - binding_proxy.set_controllable (c); + watch_connection.disconnect (); + binding_proxy.set_controllable (c); } void ArdourButton::watch () { - boost::shared_ptr c (binding_proxy.get_controllable ()); + boost::shared_ptr c (binding_proxy.get_controllable ()); - if (!c) { - warning << _("button cannot watch state of non-existing Controllable\n") << endmsg; - return; - } - - c->Changed.connect (watch_connection, invalidator(*this), boost::bind (&ArdourButton::controllable_changed, this), gui_context()); + if (!c) { + warning << _("button cannot watch state of non-existing Controllable\n") << endmsg; + return; + } + c->Changed.connect (watch_connection, invalidator(*this), boost::bind (&ArdourButton::controllable_changed, this), gui_context()); } void ArdourButton::controllable_changed () { - float val = binding_proxy.get_controllable()->get_value(); + float val = binding_proxy.get_controllable()->get_value(); if (fabs (val) >= 0.5f) { set_active_state (Gtkmm2ext::ExplicitActive); } else { unset_active_state (); } + CairoWidget::set_dirty (); } void @@ -871,9 +896,10 @@ ArdourButton::on_name_changed () _char_pixel_width = 0; _char_pixel_height = 0; _diameter = 0; - set_colors (); - build_patterns (); - queue_resize (); + _update_colors_and_patterns = true; + if (is_realized()) { + queue_resize (); + } } void @@ -907,7 +933,10 @@ void ArdourButton::set_image (const RefPtr& img) { _pixbuf = img; - queue_draw (); + if (is_realized()) { + queue_resize (); + } + CairoWidget::set_dirty (); } void @@ -916,8 +945,8 @@ ArdourButton::set_active_state (Gtkmm2ext::ActiveState s) bool changed = (_active_state != s); CairoWidget::set_active_state (s); if (changed) { - set_colors (); - build_patterns (); + _update_colors_and_patterns = true; + CairoWidget::set_dirty (); } } @@ -927,17 +956,16 @@ ArdourButton::set_visual_state (Gtkmm2ext::VisualState s) bool changed = (_visual_state != s); CairoWidget::set_visual_state (s); if (changed) { - set_colors (); - build_patterns (); + _update_colors_and_patterns = true; + CairoWidget::set_dirty (); } } - bool ArdourButton::on_focus_in_event (GdkEventFocus* ev) { _focused = true; - queue_draw (); + CairoWidget::set_dirty (); return CairoWidget::on_focus_in_event (ev); } @@ -945,7 +973,7 @@ bool ArdourButton::on_focus_out_event (GdkEventFocus* ev) { _focused = false; - queue_draw (); + CairoWidget::set_dirty (); return CairoWidget::on_focus_out_event (ev); } @@ -969,7 +997,7 @@ ArdourButton::on_enter_notify_event (GdkEventCrossing* ev) _hovering = (_elements & Inactive) ? false : true; if (ARDOUR::Config->get_widget_prelight()) { - queue_draw (); + CairoWidget::set_dirty (); } return CairoWidget::on_enter_notify_event (ev); @@ -981,7 +1009,7 @@ ArdourButton::on_leave_notify_event (GdkEventCrossing* ev) _hovering = false; if (ARDOUR::Config->get_widget_prelight()) { - queue_draw (); + CairoWidget::set_dirty (); } return CairoWidget::on_leave_notify_event (ev); @@ -992,7 +1020,7 @@ ArdourButton::set_tweaks (Tweaks t) { if (_tweaks != t) { _tweaks = t; - queue_draw (); + CairoWidget::set_dirty (); } } @@ -1007,27 +1035,70 @@ ArdourButton::action_sensitivity_changed () } void -ArdourButton::recalc_char_pixel_geometry () +ArdourButton::set_layout_ellisize_width (int w) { - if (_char_pixel_height > 0 && _char_pixel_width > 0) { + if (_layout_ellipsize_width == w) { + return; + } + _layout_ellipsize_width = w; + if (!_layout) { + return; + } + if (_layout_ellipsize_width > 0) { + _layout->set_width (_layout_ellipsize_width); + } +} + +void +ArdourButton::set_text_ellipsize (Pango::EllipsizeMode e) +{ + if (_ellipsis == e) { + return; + } + _ellipsis = e; + if (!_layout) { return; } + _layout->set_ellipsize(_ellipsis); + if (is_realized () && _layout_ellipsize_width > 0) { + _layout->set_width (_layout_ellipsize_width); + queue_resize (); + CairoWidget::set_dirty (); + } +} + +void +ArdourButton::ensure_layout () +{ if (!_layout) { + ensure_style (); _layout = Pango::Layout::create (get_pango_context()); + _layout->set_ellipsize(_ellipsis); + if (_layout_ellipsize_width > 0) { + _layout->set_width (_layout_ellipsize_width); + } + } +} + +void +ArdourButton::recalc_char_pixel_geometry () +{ + if (_char_pixel_height > 0 && _char_pixel_width > 0) { + return; } + ensure_layout(); // NB. this is not static, since the geometry is different // depending on the font used. int w, h; - std::string t = _layout->get_text(); std::string x = _("ABCDEFGHIJLKMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); _layout->set_text (x); _layout->get_pixel_size (w, h); - _layout->set_text (t); _char_pixel_height = std::max(4, h); // number of actual chars in the string (not bytes) // Glib to the rescue. Glib::ustring gx(x); _char_pixel_width = std::max(4, w / (int)gx.size()); + _layout->set_text (_text); } void @@ -1051,10 +1122,12 @@ void ArdourButton::set_elements (Element e) { _elements = e; + CairoWidget::set_dirty (); } void ArdourButton::add_elements (Element e) { _elements = (ArdourButton::Element) (_elements | e); + CairoWidget::set_dirty (); } diff --git a/gtk2_ardour/ardour_button.h b/gtk2_ardour/ardour_button.h index 7807c1cb72..3e257c0dbd 100644 --- a/gtk2_ardour/ardour_button.h +++ b/gtk2_ardour/ardour_button.h @@ -31,7 +31,7 @@ class ArdourButton : public CairoWidget , public Gtkmm2ext::Activatable { - public: + public: enum Element { Edge = 0x1, Body = 0x2, @@ -70,13 +70,12 @@ class ArdourButton : public CairoWidget , public Gtkmm2ext::Activatable Element elements() const { return _elements; } void set_elements (Element); void add_elements (Element); - + void set_corner_radius (float); void set_diameter (float); void set_text (const std::string&); const std::string& get_text () {return _text;} - void set_markup (const std::string&); void set_angle (const double); void set_alignment (const float, const float); void get_alignment (float& xa, float& ya) {xa = _xalign; ya = _yalign;}; @@ -84,13 +83,14 @@ class ArdourButton : public CairoWidget , public Gtkmm2ext::Activatable void set_led_left (bool yn); void set_distinct_led_click (bool yn); - Glib::RefPtr layout() const { return _layout; } + void set_layout_ellisize_width (int w); + void set_text_ellipsize (Pango::EllipsizeMode); sigc::signal signal_led_clicked; sigc::signal signal_clicked; boost::shared_ptr get_controllable() { return binding_proxy.get_controllable(); } - void set_controllable (boost::shared_ptr c); + void set_controllable (boost::shared_ptr c); void watch (); void set_related_action (Glib::RefPtr); @@ -107,12 +107,13 @@ class ArdourButton : public CairoWidget , public Gtkmm2ext::Activatable unsigned int char_pixel_width() { if (_char_pixel_width < 1) recalc_char_pixel_geometry() ; return _char_pixel_width; } unsigned int char_pixel_height() { if (_char_pixel_height < 1) recalc_char_pixel_geometry() ; return _char_pixel_height; } - protected: + protected: void render (cairo_t *, cairo_rectangle_t *); void on_size_request (Gtk::Requisition* req); void on_size_allocate (Gtk::Allocation&); void on_style_changed (const Glib::RefPtr&); void on_name_changed (); + void on_realize (); bool on_enter_notify_event (GdkEventCrossing*); bool on_leave_notify_event (GdkEventCrossing*); bool on_focus_in_event (GdkEventFocus*); @@ -122,7 +123,7 @@ class ArdourButton : public CairoWidget , public Gtkmm2ext::Activatable void controllable_changed (); PBD::ScopedConnection watch_connection; - protected: + protected: Glib::RefPtr _layout; Glib::RefPtr _pixbuf; std::string _text; @@ -151,12 +152,10 @@ class ArdourButton : public CairoWidget , public Gtkmm2ext::Activatable uint32_t led_active_color; uint32_t led_inactive_color; - + cairo_pattern_t* convex_pattern; cairo_pattern_t* concave_pattern; - cairo_pattern_t* led_inset_pattern; - cairo_rectangle_t* _led_rect; bool _act_on_release; @@ -165,16 +164,18 @@ class ArdourButton : public CairoWidget , public Gtkmm2ext::Activatable bool _hovering; bool _focused; bool _fixed_colors_set; - bool _fallthrough_to_parent; - + int _layout_ellipsize_width; + Pango::EllipsizeMode _ellipsis; + bool _update_colors_and_patterns; + void setup_led_rect (); void set_colors (); void color_handler (); - void build_patterns (); + void build_patterns (); + void ensure_layout (); void action_toggled (); - void action_sensitivity_changed (); void action_visibility_changed (); void action_tooltip_changed (); diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index ff5524ca7c..b44a05ccfb 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -7075,7 +7075,7 @@ Editor::lock () ArdourButton* b = manage (new ArdourButton); b->set_name ("lock button"); - b->set_markup (string_compose ("%1", _("Click to unlock"))); + b->set_text (_("Click to unlock")); b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock)); lock_dialog->get_vbox()->pack_start (*b); diff --git a/gtk2_ardour/meter_strip.cc b/gtk2_ardour/meter_strip.cc index f25a1a1142..b2ff7ecf1b 100644 --- a/gtk2_ardour/meter_strip.cc +++ b/gtk2_ardour/meter_strip.cc @@ -175,8 +175,8 @@ MeterStrip::MeterStrip (Session* sess, boost::shared_ptr rt) name_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive)); name_label.set_name("meterbridge label"); name_label.set_angle(-90.0); - name_label.layout()->set_ellipsize (Pango::ELLIPSIZE_END); - name_label.layout()->set_width(48 * PANGO_SCALE); + name_label.set_text_ellipsize (Pango::ELLIPSIZE_END); + name_label.set_layout_ellisize_width(48 * PANGO_SCALE); name_label.set_size_request(18, 50); name_label.set_alignment(-1.0, .5); ARDOUR_UI::instance()->set_tip (name_label, _route->name()); @@ -186,7 +186,7 @@ MeterStrip::MeterStrip (Session* sess, boost::shared_ptr rt) number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive)); number_label.set_name("tracknumber label"); number_label.set_angle(-90.0); - number_label.layout()->set_width(18 * PANGO_SCALE); + number_label.set_layout_ellisize_width(18 * PANGO_SCALE); number_label.set_alignment(.5, .5); namebx.set_size_request(18, 52); @@ -540,7 +540,7 @@ MeterStrip::on_size_allocate (Gtk::Allocation& a) namenumberbx.set_size_request(18, nh + tnh); if (_route) { name_label.set_size_request(18, nh + (_route->is_master() ? tnh : -1)); - name_label.layout()->set_width((nh - 4 + (_route->is_master() ? tnh : 0)) * PANGO_SCALE); + name_label.set_layout_ellisize_width ((nh - 4 + (_route->is_master() ? tnh : 0)) * PANGO_SCALE); } VBox::on_size_allocate(a); } diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index 3d6e360a34..3585783ce9 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -251,9 +251,7 @@ MixerStrip::init () bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1); name_button.set_name ("mixer strip button"); - name_button.set_text (" "); /* non empty text, forces creation of the layout */ - name_button.set_text (""); /* back to empty */ - name_button.layout()->set_ellipsize (Pango::ELLIPSIZE_END); + name_button.set_text_ellipsize (Pango::ELLIPSIZE_END); name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized)); ARDOUR_UI::instance()->set_tip (&group_button, _("Mix group"), ""); @@ -1607,12 +1605,7 @@ MixerStrip::name_changed () name_button.set_text (_route->name()); break; case Narrow: - if (_session->config.get_track_name_number()) { - name_button.set_markup(track_number_to_string (_route->track_number (), " ", - PBD::short_version (_route->name (), 5))); - } else { - name_button.set_text (PBD::short_version (_route->name(), 5)); - } + name_button.set_text (PBD::short_version (_route->name(), 5)); break; } @@ -1622,7 +1615,7 @@ MixerStrip::name_changed () void MixerStrip::name_button_resized (Gtk::Allocation& alloc) { - name_button.layout()->set_width (alloc.get_width() * PANGO_SCALE); + name_button.set_layout_ellisize_width (alloc.get_width() * PANGO_SCALE); } bool -- 2.30.2