X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fardour_knob.cc;h=17fbff2f7ee11de3205e291078646b5c627ea7fa;hb=5e7d9d30e28e8022661497244ccd43a1f5a836c6;hp=7021cb9df1807e4a7c048df58adf2c32c7a4f1c3;hpb=fdaa6a151aa6b0022879d282802b2166aec653dc;p=ardour.git diff --git a/gtk2_ardour/ardour_knob.cc b/gtk2_ardour/ardour_knob.cc index 7021cb9df1..17fbff2f7e 100644 --- a/gtk2_ardour/ardour_knob.cc +++ b/gtk2_ardour/ardour_knob.cc @@ -37,7 +37,9 @@ #include "ardour_knob.h" #include "ardour_ui.h" #include "global_signals.h" +#include "timers.h" +#include "canvas/colors.h" #include "canvas/utils.h" #include "i18n.h" @@ -53,12 +55,21 @@ using namespace std; ArdourKnob::Element ArdourKnob::default_elements = ArdourKnob::Element (ArdourKnob::Arc); -ArdourKnob::ArdourKnob (Element e) +ArdourKnob::ArdourKnob (Element e, Flags flags) : _elements (e) , _hovering (false) - , _grabbed (false) + , _grabbed_x (0) + , _grabbed_y (0) + , _val (0) + , _normal (0) + , _dead_zone_delta (0) + , _flags (flags) + , _tooltip (this) { ARDOUR_UI_UTILS::ColorsChanged.connect (sigc::mem_fun (*this, &ArdourKnob::color_handler)); + + // watch automation :( + Timers::rapid_connect (sigc::mem_fun (*this, &ArdourKnob::controllable_changed)); } ArdourKnob::~ArdourKnob() @@ -76,9 +87,16 @@ ArdourKnob::render (cairo_t* cr, cairo_rectangle_t *) const float scale = min(width, height); const float pointer_thickness = 3.0 * (scale/80); //(if the knob is 80 pixels wide, we want a 3-pix line on it) - float start_angle = ((180 - 65) * G_PI) / 180; - float end_angle = ((360 + 65) * G_PI) / 180; - float value_angle = start_angle + (_val * (end_angle - start_angle)); + const float start_angle = ((180 - 65) * G_PI) / 180; + const float end_angle = ((360 + 65) * G_PI) / 180; + + float zero = 0; + if (_flags & ArcToZero) { + zero = _normal; + } + + const float value_angle = start_angle + (_val * (end_angle - start_angle)); + const float zero_angle = start_angle + (zero * (end_angle - start_angle)); float value_x = cos (value_angle); float value_y = sin (value_angle); @@ -89,28 +107,23 @@ ArdourKnob::render (cairo_t* cr, cairo_rectangle_t *) cairo_translate (cr, xc, yc); //after this, everything is based on the center of the knob //get the knob color from the theme - ArdourCanvas::Color knob_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1", get_name())); + ArdourCanvas::Color knob_color = ARDOUR_UI::config()->color (string_compose ("%1", get_name())); float center_radius = 0.48*scale; float border_width = 0.8; bool arc = (_elements & Arc)==Arc; bool bevel = (_elements & Bevel)==Bevel; - bool flat = ARDOUR_UI::config()->get_flat_buttons(); + bool flat = _flat_buttons; if ( arc ) { - center_radius = scale*0.30; + center_radius = scale*0.33; - float inner_progress_radius = scale*0.30; + float inner_progress_radius = scale*0.38; float outer_progress_radius = scale*0.48; float progress_width = (outer_progress_radius-inner_progress_radius); float progress_radius = inner_progress_radius + progress_width/2.0; - float start_angle_x = cos (start_angle); - float start_angle_y = sin (start_angle); - float end_angle_x = cos (end_angle); - float end_angle_y = sin (end_angle); - //dark arc background cairo_set_source_rgb (cr, 0.3, 0.3, 0.3 ); cairo_set_line_width (cr, progress_width); @@ -119,21 +132,27 @@ ArdourKnob::render (cairo_t* cr, cairo_rectangle_t *) //look up the arc colors from the config double red_start, green_start, blue_start, unused; - ArdourCanvas::Color arc_start_color = ARDOUR_UI::config()->color_by_name ( "processor fader: fill start"); + ArdourCanvas::Color arc_start_color = ARDOUR_UI::config()->color ( string_compose ("%1: arc start", get_name())); ArdourCanvas::color_to_rgba( arc_start_color, red_start, green_start, blue_start, unused ); double red_end, green_end, blue_end; - ArdourCanvas::Color arc_end_color = ARDOUR_UI::config()->color_by_name ( "processor fader: fill end" ); + ArdourCanvas::Color arc_end_color = ARDOUR_UI::config()->color ( string_compose ("%1: arc end", get_name()) ); ArdourCanvas::color_to_rgba( arc_end_color, red_end, green_end, blue_end, unused ); //vary the arc color over the travel of the knob - float r = (1.0-_val) * red_end + _val * red_start; - float g = (1.0-_val) * green_end + _val * green_start; - float b = (1.0-_val) * blue_end + _val * blue_start; + float intensity = fabsf (_val - zero) / std::max(zero, (1.f - zero)); + const float intensity_inv = 1.0 - intensity; + float r = intensity_inv * red_end + intensity * red_start; + float g = intensity_inv * green_end + intensity * green_start; + float b = intensity_inv * blue_end + intensity * blue_start; //draw the arc cairo_set_source_rgb (cr, r,g,b); cairo_set_line_width (cr, progress_width); - cairo_arc (cr, 0, 0, progress_radius, start_angle, value_angle); + if (zero_angle > value_angle) { + cairo_arc (cr, 0, 0, progress_radius, value_angle, zero_angle); + } else { + cairo_arc (cr, 0, 0, progress_radius, zero_angle, value_angle); + } cairo_stroke (cr); //shade the arc @@ -147,8 +166,13 @@ ArdourKnob::render (cairo_t* cr, cairo_rectangle_t *) cairo_fill (cr); cairo_pattern_destroy (shade_pattern); } - - //black border + +#if 0 //black border + const float start_angle_x = cos (start_angle); + const float start_angle_y = sin (start_angle); + const float end_angle_x = cos (end_angle); + const float end_angle_y = sin (end_angle); + cairo_set_source_rgb (cr, 0, 0, 0 ); cairo_set_line_width (cr, border_width); cairo_move_to (cr, (outer_progress_radius * start_angle_x), (outer_progress_radius * start_angle_y)); @@ -159,6 +183,7 @@ ArdourKnob::render (cairo_t* cr, cairo_rectangle_t *) cairo_stroke (cr); cairo_arc (cr, 0, 0, outer_progress_radius, start_angle, end_angle); cairo_stroke (cr); +#endif } if (!flat) { @@ -239,7 +264,7 @@ ArdourKnob::render (cairo_t* cr, cairo_rectangle_t *) cairo_stroke (cr); //highlight if grabbed or if mouse is hovering over me - if ( _grabbed || (_hovering && ARDOUR::Config->get_widget_prelight() ) ) { + if (_tooltip.dragging() || (_hovering && ARDOUR_UI::config()->get_widget_prelight() ) ) { cairo_set_source_rgba (cr, 1,1,1, 0.12 ); cairo_arc (cr, 0, 0, center_radius, 0, 2.0*G_PI); cairo_fill (cr); @@ -251,9 +276,16 @@ ArdourKnob::render (cairo_t* cr, cairo_rectangle_t *) void ArdourKnob::on_size_request (Gtk::Requisition* req) { - CairoWidget::on_size_request (req); - - //perhaps render the knob base into a cached image here? + // see ardour-button VectorIcon size, use font scaling as default + CairoWidget::on_size_request (req); // allow to override + + // we're square + if (req->width < req->height) { + req->width = req->height; + } + if (req->height < req->width) { + req->height = req->width; + } } bool @@ -275,7 +307,7 @@ ArdourKnob::on_scroll_event (GdkEventScroll* ev) float val = c->get_interface(); if ( ev->direction == GDK_SCROLL_UP ) - val += scale; + val += scale; else val -= scale; @@ -286,10 +318,22 @@ ArdourKnob::on_scroll_event (GdkEventScroll* ev) } bool -ArdourKnob::on_motion_notify_event (GdkEventMotion *ev) +ArdourKnob::on_motion_notify_event (GdkEventMotion *ev) { - //scale the adjustment based on keyboard modifiers - float scale = 0.0025; + if (!(ev->state & Gdk::BUTTON1_MASK)) { + return true; + } + + boost::shared_ptr c = binding_proxy.get_controllable(); + if (!c) { + return true; + } + + + //scale the adjustment based on keyboard modifiers & GUI size + const float ui_scale = max (1.f, ARDOUR_UI::ui_scale); + float scale = 0.0025 / ui_scale; + if (ev->state & Keyboard::GainFineScaleModifier) { if (ev->state & Keyboard::GainExtraFineScaleModifier) { scale *= 0.01; @@ -299,53 +343,100 @@ ArdourKnob::on_motion_notify_event (GdkEventMotion *ev) } //calculate the travel of the mouse - int y_delta = 0; - if (ev->state & Gdk::BUTTON1_MASK) { - y_delta = _grabbed_y - ev->y; - _grabbed_y = ev->y; - if (y_delta == 0) return TRUE; + int delta = (_grabbed_y - ev->y) - (_grabbed_x - ev->x); + if (delta == 0) { + return true; } - //step the value of the controllable - boost::shared_ptr c = binding_proxy.get_controllable(); - if (c) { - float val = c->get_interface(); - val += y_delta * scale; - c->set_interface(val); + _grabbed_x = ev->x; + _grabbed_y = ev->y; + float val = c->get_interface(); + + if (_flags & Detent) { + const float px_deadzone = 42.f * ui_scale; + + if ((val - _normal) * (val - _normal + delta * scale) < 0) { + /* detent */ + const int tozero = (_normal - val) * scale; + int remain = delta - tozero; + if (abs (remain) > px_deadzone) { + /* slow down passing the default value */ + remain += (remain > 0) ? px_deadzone * -.5 : px_deadzone * .5; + delta = tozero + remain; + _dead_zone_delta = 0; + } else { + c->set_value (c->normal()); + _dead_zone_delta = remain / px_deadzone; + return true; + } + } + + if (fabsf (rintf((val - _normal) / scale) + _dead_zone_delta) < 1) { + c->set_value (c->normal()); + _dead_zone_delta += delta / px_deadzone; + return true; + } + + _dead_zone_delta = 0; } + val += delta * scale; + c->set_interface(val); + return true; } bool ArdourKnob::on_button_press_event (GdkEventButton *ev) { + _grabbed_x = ev->x; _grabbed_y = ev->y; - _grabbed = true; - - set_active_state (Gtkmm2ext::ExplicitActive); + _dead_zone_delta = 0; + + if (ev->type != GDK_BUTTON_PRESS) { + if (_grabbed) { + remove_modal_grab(); + gdk_pointer_ungrab (GDK_CURRENT_TIME); + } + return true; + } if (binding_proxy.button_press_handler (ev)) { return true; } - return false; + if (ev->button != 1 && ev->button != 2) { + return false; + } + + set_active_state (Gtkmm2ext::ExplicitActive); + _tooltip.start_drag(); + add_modal_grab(); + _grabbed = true; + gdk_pointer_grab(ev->window,false, + GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK), + NULL,NULL,ev->time); + return true; } bool ArdourKnob::on_button_release_event (GdkEventButton *ev) { - if ( (_grabbed_y == ev->y) && Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { //no move, shift-click sets to default + _tooltip.stop_drag(); + _grabbed = false; + remove_modal_grab(); + gdk_pointer_ungrab (GDK_CURRENT_TIME); + + if ( (_grabbed_y == ev->y && _grabbed_x == ev->x) && Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { //no move, shift-click sets to default boost::shared_ptr c = binding_proxy.get_controllable(); if (!c) return false; c->set_value (c->normal()); - return true; + return true; } - _grabbed = false; unset_active_state (); - return false; + return true; } void @@ -363,7 +454,7 @@ ArdourKnob::on_size_allocate (Allocation& alloc) void ArdourKnob::set_controllable (boost::shared_ptr c) { - watch_connection.disconnect (); //stop watching the old controllable + watch_connection.disconnect (); //stop watching the old controllable if (!c) return; @@ -371,16 +462,28 @@ ArdourKnob::set_controllable (boost::shared_ptr c) c->Changed.connect (watch_connection, invalidator(*this), boost::bind (&ArdourKnob::controllable_changed, this), gui_context()); + _normal = c->internal_to_interface(c->normal()); + controllable_changed(); } void ArdourKnob::controllable_changed () { - _val = binding_proxy.get_controllable()->get_interface(); //% of knob travel + boost::shared_ptr c = binding_proxy.get_controllable(); + if (!c) return; + + float val = c->get_interface(); + val = min( max(0.0f, val), 1.0f); // clamp - _val = min( max(0.0f, _val), 1.0f); //range check + if (val == _val) { + return; + } + _val = val; + if (!_tooltip_prefix.empty()) { + _tooltip.set_tip (_tooltip_prefix + c->get_user_string()); + } set_dirty(); } @@ -457,3 +560,28 @@ ArdourKnob::add_elements (Element e) { _elements = (ArdourKnob::Element) (_elements | e); } + + +KnobPersistentTooltip::KnobPersistentTooltip (Gtk::Widget* w) + : PersistentTooltip (w, true, 3) + , _dragging (false) +{ +} + +void +KnobPersistentTooltip::start_drag () +{ + _dragging = true; +} + +void +KnobPersistentTooltip::stop_drag () +{ + _dragging = false; +} + +bool +KnobPersistentTooltip::dragging () const +{ + return _dragging; +}