From f3e581be0dca4dad96cca58c7cf11bc76876615d Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 5 Jan 2026 16:23:12 -0700 Subject: [PATCH] redesign bottom of pianoroll (automation/MIDI CC buttons) This should hopefully disambiguate where to click and why --- gtk2_ardour/pianoroll.cc | 195 +++++++++++++++++++++++++-------------- gtk2_ardour/pianoroll.h | 51 +++++++--- 2 files changed, 161 insertions(+), 85 deletions(-) diff --git a/gtk2_ardour/pianoroll.cc b/gtk2_ardour/pianoroll.cc index f6eda019e2..3825425317 100644 --- a/gtk2_ardour/pianoroll.cc +++ b/gtk2_ardour/pianoroll.cc @@ -136,6 +136,8 @@ Pianoroll::rebuild_parameter_button_map() void Pianoroll::reset_user_cc_choice (std::string name, Evoral::Parameter param, MetaButton* metabutton) { +#ifdef PIANOROLL_USER_BUTTONS + EC_LOCAL_TEMPO_SCOPE; ParameterButtonMap::iterator iter; @@ -150,6 +152,7 @@ Pianoroll::reset_user_cc_choice (std::string name, Evoral::Parameter param, Meta parameter_button_map.insert (std::make_pair (metabutton, param)); metabutton->set_by_menutext (name); +#endif } void @@ -185,6 +188,8 @@ Pianoroll::add_multi_controller_item (Gtk::Menu_Helpers::MenuList&, const std::string& name, MetaButton* mb) { +#ifdef PIANOROLL_USER_BUTTONS + EC_LOCAL_TEMPO_SCOPE; using namespace Gtk; @@ -231,22 +236,22 @@ Pianoroll::add_multi_controller_item (Gtk::Menu_Helpers::MenuList&, */ mb->add_item (name, menu_text, *chn_menu, [](){}); +#endif } void Pianoroll::build_lower_toolbar () { EC_LOCAL_TEMPO_SCOPE; + Gtk::RadioButtonGroup edit_group; horizontal_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &Pianoroll::scrolled)); - ArdourButton::Element elements = ArdourButton::Element (ArdourButton::Text|ArdourButton::Indicator|ArdourButton::Edge|ArdourButton::Body); - - velocity_button = new ArdourButton (_("Velocity"), elements); - bender_button = new ArdourButton (_("Bender"), elements); - pressure_button = new ArdourButton (_("Pressure"), elements); - expression_button = new ArdourButton (_("Expression"), elements); - modulation_button = new ArdourButton (_("Modulation"), elements); + velocity_button = new ControllerControls (-1, _("Velocity"), edit_group); + bender_button = new ControllerControls (MIDI_CMD_BENDER, _("Bender"), edit_group); + pressure_button = new ControllerControls (MIDI_CMD_CHANNEL_PRESSURE, _("Pressure"), edit_group); + expression_button = new ControllerControls (MIDI_CTL_MSB_EXPRESSION, _("Expression"), edit_group); + modulation_button = new ControllerControls (MIDI_CTL_MSB_MODWHEEL, _("Modulation"), edit_group); #ifdef PIANOROLL_USER_BUTTONS cc_dropdown1 = new MetaButton (); @@ -263,18 +268,6 @@ Pianoroll::build_lower_toolbar () #endif rebuild_parameter_button_map (); - /* Only need to do this once because i->first is the actual button, - * which does not change even when the parameter_button_map is rebuilt. - */ - - for (ParameterButtonMap::iterator i = parameter_button_map.begin(); i != parameter_button_map.end(); ++i) { - i->first->set_active_color (0xff0000ff); - i->first->set_distinct_led_click (true); - i->first->set_led_left (true); - i->first->set_act_on_release (false); - i->first->set_fallthrough_to_parent (true); - } - // button_bar.set_homogeneous (true); button_bar.set_spacing (6); button_bar.set_border_width (6); @@ -289,26 +282,26 @@ Pianoroll::build_lower_toolbar () button_bar.pack_start (*cc_dropdown3, false, false); #endif - velocity_button->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_button_event), ARDOUR::MidiVelocityAutomation, 0)); - pressure_button->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_button_event), ARDOUR::MidiChannelPressureAutomation, 0)); - bender_button->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_button_event), ARDOUR::MidiPitchBenderAutomation, 0)); - modulation_button->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_button_event), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_MODWHEEL)); - expression_button->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_button_event), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_EXPRESSION)); + velocity_button->show_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_show_button_click), ARDOUR::MidiVelocityAutomation, 0)); + pressure_button->show_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_show_button_click), ARDOUR::MidiChannelPressureAutomation, 0)); + bender_button->show_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_show_button_click), ARDOUR::MidiPitchBenderAutomation, 0)); + modulation_button->show_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_show_button_click), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_MODWHEEL)); + expression_button->show_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_show_button_click), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_EXPRESSION)); - velocity_button->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_led_click), ARDOUR::MidiVelocityAutomation, 0)); - pressure_button->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_led_click), ARDOUR::MidiChannelPressureAutomation, 0)); - bender_button->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_led_click), ARDOUR::MidiPitchBenderAutomation, 0)); - modulation_button->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_led_click), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_MODWHEEL)); - expression_button->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_led_click), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_EXPRESSION)); + velocity_button->edit_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_active_button_click), ARDOUR::MidiVelocityAutomation, 0)); + pressure_button->edit_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_active_button_click), ARDOUR::MidiChannelPressureAutomation, 0)); + bender_button->edit_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_active_button_click), ARDOUR::MidiPitchBenderAutomation, 0)); + modulation_button->edit_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_active_button_click), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_MODWHEEL)); + expression_button->edit_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_active_button_click), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_EXPRESSION)); #ifdef PIANOROLL_USER_BUTTONS - cc_dropdown1->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_button_event), cc_dropdown1), false); - cc_dropdown2->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_button_event), cc_dropdown2), false); - cc_dropdown3->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_button_event), cc_dropdown3), false); + cc_dropdown1->show_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_active_button_click), cc_dropdown1), false); + cc_dropdown2->show_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_active_button_click), cc_dropdown2), false); + cc_dropdown3->show_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_active_button_click), cc_dropdown3), false); - cc_dropdown1->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown1)); - cc_dropdown2->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown2)); - cc_dropdown3->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown3)); + cc_dropdown1->edit_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown1)); + cc_dropdown2->edit_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown2)); + cc_dropdown3->edit_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown3)); cc_dropdown1->signal_map().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::build_cc_menu), cc_dropdown1)); cc_dropdown2->signal_map().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::build_cc_menu), cc_dropdown2)); @@ -1483,8 +1476,9 @@ Pianoroll::set_region (std::shared_ptr region) } bool -Pianoroll::user_automation_button_event (GdkEventButton* ev, MetaButton* mb) +Pianoroll::user_automation_active_button_click (GdkEventButton* ev, MetaButton* mb) { +#ifdef PIANOROLL_USER_BUTTONS EC_LOCAL_TEMPO_SCOPE; if (mb->is_menu_popup_event (ev)) { @@ -1505,12 +1499,14 @@ Pianoroll::user_automation_button_event (GdkEventButton* ev, MetaButton* mb) view->set_active_automation (i->second); } +#endif return true; } void -Pianoroll::user_led_click (GdkEventButton* ev, MetaButton* metabutton) +Pianoroll::user_automation_show_button_click (GdkEventButton* ev, MetaButton* metabutton) { +#ifdef PIANOROLL_USER_BUTTONS EC_LOCAL_TEMPO_SCOPE; if (ev->button != 1) { @@ -1523,43 +1519,31 @@ Pianoroll::user_led_click (GdkEventButton* ev, MetaButton* metabutton) return; } - automation_button_event (ev, i->second.type(), i->second.id()); -} - -bool -Pianoroll::automation_button_event (GdkEventButton* ev, Evoral::ParameterType type, int id) -{ - EC_LOCAL_TEMPO_SCOPE; - - if (ev->button != 1) { - return false; - } - - if (view) { - view->set_active_automation (Evoral::Parameter (type, _visible_channel, id)); - } - - return true; + automation_active_button_click (ev, i->second.type(), i->second.id()); +#endif } void -Pianoroll::automation_led_click (GdkEventButton* ev, Evoral::ParameterType type, int id) +Pianoroll::automation_active_button_click (Evoral::ParameterType type, int id) { EC_LOCAL_TEMPO_SCOPE; - if (ev->button != 1) { - return; + if (view) { + Evoral::Parameter p (type, id); + std::string str (ARDOUR::EventTypeMap::instance().to_symbol (p)); + std::cerr << "set active for " << str << std::endl; + view->set_active_automation (Evoral::Parameter (type, _visible_channel, id)); } +} - switch (ev->type) { - case GDK_BUTTON_RELEASE: - if (view) { - Evoral::Parameter param (type, _visible_channel, id); - view->toggle_visibility (param); - } - break; - default: - break; +void +Pianoroll::automation_show_button_click (Evoral::ParameterType type, int id) +{ + EC_LOCAL_TEMPO_SCOPE; + + if (view) { + Evoral::Parameter param (type, _visible_channel, id); + view->toggle_visibility (param); } } @@ -1570,23 +1554,24 @@ Pianoroll::automation_state_changed () assert (view); + for (ParameterButtonMap::iterator i = parameter_button_map.begin(); i != parameter_button_map.end(); ++i) { std::string str (ARDOUR::EventTypeMap::instance().to_symbol (i->second)); /* Indicate active automation state with selected/not-selected visual state */ if (view->is_active_automation (i->second)) { - i->first->set_visual_state (Gtkmm2ext::Selected); + i->first->set_editing (true); } else { - i->first->set_visual_state (Gtkmm2ext::NoVisualState); + i->first->set_editing (false); } /* Indicate visible automation state with explicit widget active state (LED) */ if (view->is_visible_automation (i->second)) { - i->first->set_active_state (Gtkmm2ext::ExplicitActive); + i->first->set_showing (true); } else { - i->first->set_active_state (Gtkmm2ext::Off); + i->first->set_showing (false); } } } @@ -2137,3 +2122,73 @@ Pianoroll::midiviews_from_region_selection (RegionSelection const &) const return mv; } + +/*----*/ + +using namespace ArdourWidgets; + +ControllerControls::ControllerControls (int num, std::string const & str, Gtk::RadioButtonGroup& group) + : number (num) +{ + using namespace Gtk; + + ArdourButton::Element elements = ArdourButton::Element (ArdourButton::VectorIcon|ArdourButton::Edge|ArdourButton::Body); + + show_hide_button = new ArdourButton (elements); + edit_button = new ArdourButton (elements); + name.set_text (str); + + show_hide_button->set_icon (ArdourIcon::HideEye); + edit_button->set_icon (ArdourIcon::ToolDraw); + + show_hide_button->signal_clicked.connect (sigc::mem_fun (show_clicked, &sigc::signal::emit)); + edit_button->signal_clicked.connect (sigc::mem_fun (edit_clicked, &sigc::signal::emit)); + + show_hide_button->set_active_color (0xff0000ff); + show_hide_button->set_act_on_release (false); + show_hide_button->set_fallthrough_to_parent (false); + + edit_button->set_active_color (0xff0000ff); + edit_button->set_act_on_release (false); + edit_button->set_fallthrough_to_parent (false); + + set_spacing (6); + set_border_width (12); + pack_start (*show_hide_button, false, false); + pack_start (*edit_button, false, false); + pack_start (name, false, false, 6); + show_all (); +} + +ControllerControls::~ControllerControls () +{ +} + +bool +ControllerControls::showing() const +{ + return show_hide_button->active_state() != Gtkmm2ext::Off; +} + +bool +ControllerControls::editing() const +{ + return edit_button->get_active(); +} + +void +ControllerControls::set_showing (bool yn) +{ + show_hide_button->set_active_state (yn ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off); + if (!yn) { + show_hide_button->set_icon (ArdourIcon::EditorShowAutoOnTouch); + } else { + show_hide_button->set_icon (ArdourIcon::HideEye); + } +} + +void +ControllerControls::set_editing (bool yn) +{ + edit_button->set_active_state (yn ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off); +} diff --git a/gtk2_ardour/pianoroll.h b/gtk2_ardour/pianoroll.h index e723dccf56..b76760f6aa 100644 --- a/gtk2_ardour/pianoroll.h +++ b/gtk2_ardour/pianoroll.h @@ -23,6 +23,7 @@ #include "pbd/timer.h" #include +#include #include "canvas/ruler.h" #include "widgets/eventboxext.h" @@ -52,6 +53,25 @@ namespace ArdourWidgets { class PianorollMidiView; class PianorollMidiBackground; +struct ControllerControls : public Gtk::HBox { + ControllerControls (int num, std::string const & name, Gtk::RadioButtonGroup& group); + ~ControllerControls(); + + ArdourWidgets::ArdourButton* show_hide_button; + ArdourWidgets::ArdourButton* edit_button; + Gtk::Label name; + int number; + + bool showing() const; + bool editing() const; + + sigc::signal show_clicked; + sigc::signal edit_clicked; + + void set_showing (bool); + void set_editing (bool); +}; + class Pianoroll : public CueEditor { public: @@ -156,16 +176,17 @@ class Pianoroll : public CueEditor ArdourCanvas::Rectangle* meter_bar; ArdourCanvas::PianoRollHeader* prh; - ArdourWidgets::ArdourButton* velocity_button; - ArdourWidgets::ArdourButton* bender_button; - ArdourWidgets::ArdourButton* pressure_button; - ArdourWidgets::ArdourButton* expression_button; - ArdourWidgets::ArdourButton* modulation_button; - ArdourWidgets::MetaButton* cc_dropdown1; - ArdourWidgets::MetaButton* cc_dropdown2; - ArdourWidgets::MetaButton* cc_dropdown3; - - typedef std::map ParameterButtonMap; + ControllerControls* velocity_button; + ControllerControls* bender_button; + ControllerControls* pressure_button; + ControllerControls* expression_button; + ControllerControls* modulation_button; +#ifdef PIANOROLL_USER_BUTTONS + ControllerControls cc_dropdown1; + ControllerControls cc_dropdown2; + ControllerControls cc_dropdown3; +#endif + typedef std::map ParameterButtonMap; ParameterButtonMap parameter_button_map; void rebuild_parameter_button_map (); @@ -205,11 +226,11 @@ class Pianoroll : public CueEditor bool idle_data_captured (); - bool user_automation_button_event (GdkEventButton* ev, ArdourWidgets::MetaButton* mb); - bool automation_button_event (GdkEventButton*, Evoral::ParameterType type, int id); - bool automation_button_click (Evoral::ParameterType type, int id, ARDOUR::SelectionOperation); - void automation_led_click (GdkEventButton*, Evoral::ParameterType type, int id); - void user_led_click (GdkEventButton* ev, ArdourWidgets::MetaButton* metabutton); + bool user_automation_active_button_click (GdkEventButton* ev, ArdourWidgets::MetaButton* mb); + void user_automation_show_button_click (GdkEventButton* ev, ArdourWidgets::MetaButton* metabutton); + + void automation_active_button_click (Evoral::ParameterType type, int id); + void automation_show_button_click (Evoral::ParameterType type, int id); int _visible_channel;