diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index 03636889f5..47c0f8b726 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -1668,7 +1668,7 @@ MixerStrip::revert_to_default_display () if (_route->triggerbox() && UIConfiguration::instance().get_show_triggers_inline()) { create_trigger_display (_route->triggerbox()); if (trigger_display->get_parent() != &global_vpacker) { - global_vpacker.pack_start (*trigger_display, true, true); + global_vpacker.pack_start (*trigger_display, Gtk::PACK_SHRINK); trigger_display->show (); global_vpacker.reorder_child (*trigger_display, 4); } @@ -2143,7 +2143,7 @@ void MixerStrip::create_trigger_display (boost::shared_ptr tb) { if (!trigger_display) { - trigger_display = new TriggerBoxWidget (*(tb.get())); /* XXX fix to use shared ptr */ + trigger_display = new TriggerBoxWidget (*(tb.get()), -1., 8*16.); /* XXX fix to use shared ptr */ } } diff --git a/gtk2_ardour/trigger_stopper.cc b/gtk2_ardour/trigger_stopper.cc new file mode 100644 index 0000000000..87e47525ab --- /dev/null +++ b/gtk2_ardour/trigger_stopper.cc @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2021 Paul Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +#include "pbd/compose.h" +#include "pbd/convert.h" + +#include "ardour/region.h" +#include "ardour/triggerbox.h" + +#include "canvas/polygon.h" +#include "canvas/text.h" + +#include "gtkmm2ext/actions.h" +#include "gtkmm2ext/colors.h" +#include "gtkmm2ext/utils.h" + +#include "ardour_ui.h" +#include "gui_thread.h" +#include "trigger_stopper.h" +#include "trigger_ui.h" +#include "public_editor.h" +#include "region_view.h" +#include "selection.h" +#include "timers.h" +#include "ui_config.h" +#include "utils.h" + +#include "pbd/i18n.h" + +using namespace ARDOUR; +using namespace ArdourCanvas; +using namespace Gtkmm2ext; +using namespace PBD; + +TriggerStopper::TriggerStopper (Item* parent, boost::shared_ptr t) + : ArdourCanvas::Rectangle (parent) + , _triggerbox (t) +{ + set_layout_sensitive(true); //why??? + + name = X_("trigger stopper"); + + Event.connect (sigc::mem_fun (*this, &TriggerStopper::event_handler)); + + play_shape = new ArdourCanvas::Polygon (this); + play_shape->set_outline (false); + play_shape->name = X_("stopbutton"); + play_shape->set_ignore_events (true); + play_shape->show (); + + name_text = new Text (this); + name_text->set("Now Playing"); + name_text->set_ignore_events (false); + + /* prefs (theme colors) */ + UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &TriggerStopper::ui_parameter_changed)); + + /* trigger changes */ + _triggerbox->PropertyChanged.connect (trigger_prop_connection, MISSING_INVALIDATOR, boost::bind (&TriggerStopper::prop_change, this, _1), gui_context()); + + /* route changes */ +// dynamic_cast (_triggerbox->owner())->presentation_info().Change.connect (owner_prop_connection, MISSING_INVALIDATOR, boost::bind (&TriggerStopper::owner_prop_change, this, _1), gui_context()); + + PropertyChange changed; + changed.add (ARDOUR::Properties::name); + changed.add (ARDOUR::Properties::running); + prop_change (changed); + + ui_parameter_changed("color-file"); +} + +TriggerStopper::~TriggerStopper () +{ +} + +void +TriggerStopper::render (Rect const & area, Cairo::RefPtr context) const +{ + /* Note that item_to_window() already takes _position into account (as + part of item_to_canvas() + */ + Rect self (item_to_window (_rect)); + const Rect draw = self.intersection (area); + + if (!draw) { + return; + } + + float width = _rect.width(); + float height = _rect.height(); + + const double scale = UIConfiguration::instance().get_ui_scale(); + + if (_fill && !_transparent) { + setup_fill_context (context); + context->rectangle (draw.x0, draw.y0, draw.width(), draw.height()); + context->fill (); + } + + //black area around text + set_source_rgba (context, UIConfiguration::instance().color ("theme:bg2")); + context->rectangle (16*scale, 1*scale, _rect.width()-2*scale, _rect.height()-2*scale); + context->fill (); + +#if 0 +//text +Glib::RefPtr layout (Pango::Layout::create (context)); +layout->set_font_description (UIConfiguration::instance().get_NormalFont()); +layout->set_text (name_text->text()); +//text clipping rect +context->save(); +context->rectangle (2, 1, width-4, height-2); +context->clip(); +//calculate the text size +int tw, th; +layout->get_pixel_size (tw, th); +//render the text (centered vertically) +context->translate( 18*scale, (height/2)-(th/2) ); +set_source_rgba (context, UIConfiguration::instance().color ("neutral:foreground")); +layout->show_in_cairo_context (context); +context->restore (); +#endif + + render_children (area, context); + + //fade-over at right + uint32_t bg_color = UIConfiguration::instance().color ("theme:bg"); + double bg_r,bg_g,bg_b, unused; + Gtkmm2ext::color_to_rgba( bg_color, bg_r, bg_g, bg_b, unused); + Cairo::RefPtr left_pattern = Cairo::LinearGradient::create (_rect.width()-12*scale, 0, _rect.width(), 0); + left_pattern->add_color_stop_rgba (0, 0, 0, 0, 0); + left_pattern->add_color_stop_rgba (1, 0, 0, 0, 1); + context->set_source (left_pattern); + context->rectangle( _rect.width()-12*scale, 2*scale, 10*scale, _rect.height()-4*scale ); + context->fill (); +} + +void +TriggerStopper::owner_prop_change (PropertyChange const & pc) +{ + if (pc.contains (Properties::color)) { + } +} + +void +TriggerStopper::selection_change () +{ +} + +bool +TriggerStopper::event_handler (GdkEvent* ev) +{ + switch (ev->type) { + case GDK_BUTTON_PRESS: + if (ev->button.button == 1) { + _triggerbox->request_stop_all (); + return true; + } + break; + case GDK_ENTER_NOTIFY: + if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) { + play_shape->set_fill_color (UIConfiguration::instance().color("neutral:foregroundest")); + } + redraw (); + break; + case GDK_LEAVE_NOTIFY: + if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) { + play_shape->set_fill_color (UIConfiguration::instance().color("neutral:midground")); + } + redraw (); + break; + default: + break; + } + + return false; +} + +void +TriggerStopper::maybe_update () +{ +/* double nbw; + + if (!_trigger->active()) { + nbw = 0; + } else { + nbw = _trigger->position_as_fraction () * (_allocation.width() - _allocation.height()); + } + + if (nbw) { + const double scale = UIConfiguration::instance().get_ui_scale(); + ArdourCanvas::Rect r (get()); + + active_bar->set (ArdourCanvas::Rect (r.height() * scale, + (r.y0 + 1) * scale, + (r.height() + nbw - 1) * scale, + (r.y1 - 1) * scale)); + active_bar->show (); + } else { + active_bar->hide (); + } + * */ +} + +void +TriggerStopper::_size_allocate (ArdourCanvas::Rect const & alloc) +{ + Rectangle::_size_allocate (alloc); + + const double scale = UIConfiguration::instance().get_ui_scale(); + poly_margin = 2. * scale; + + const Distance width = _rect.width(); + const Distance height = _rect.height(); + + poly_size = height - (poly_margin*2); + + Points p; + p.push_back (Duple (poly_margin, poly_margin)); + p.push_back (Duple (poly_margin, poly_size)); + p.push_back (Duple (poly_size, poly_size)); + p.push_back (Duple (poly_size, poly_margin)); + play_shape->set (p); + + float tleft = poly_size + (poly_margin*3); + float twidth = width-poly_size-(poly_margin*3); + + ArdourCanvas::Rect text_alloc (tleft, 0, twidth, height); //testing + name_text->size_allocate (text_alloc); + name_text->set_position (Duple (tleft, 1.*scale)); + name_text->clamp_width (twidth); + + //font scale may have changed. uiconfig 'embeds' the ui-scale in the font + name_text->set_font_description (UIConfiguration::instance().get_NormalFont()); +} + +void +TriggerStopper::prop_change (PropertyChange const & change) +{ + if (change.contains (ARDOUR::Properties::name) + || change.contains (ARDOUR::Properties::running) + ) { + ARDOUR::Trigger *trigger = _triggerbox->currently_playing(); + + if (trigger) { + name_text->set (trigger->region()->name()); + } else { +// name_text->set (""); + } + + redraw(); + } +} + +void +TriggerStopper::ui_parameter_changed (std::string const& p) +{ + if (p == "color-file") { + set_fill_color (UIConfiguration::instance().color("gtk_background")); + name_text->set_color (UIConfiguration::instance().color("neutral:foreground")); + play_shape->set_fill_color (UIConfiguration::instance().color("neutral:midground")); + } + redraw(); +} + + +//==================================== + +CueStopper::CueStopper (Item* parent, boost::shared_ptr t) + : ArdourCanvas::Rectangle (parent) + , _triggerbox (t) +{ + set_layout_sensitive(true); //why??? + + name = X_("trigger stopper"); + + Event.connect (sigc::mem_fun (*this, &CueStopper::event_handler)); + + play_shape = new ArdourCanvas::Polygon (this); + play_shape->set_outline (false); + play_shape->name = X_("stopbutton"); + play_shape->set_ignore_events (true); + play_shape->show (); + + name_text = new Text (this); + name_text->set("Now Playing"); + name_text->set_ignore_events (false); + + /* prefs (theme colors) */ + UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &CueStopper::ui_parameter_changed)); + + /* trigger changes */ + _triggerbox->PropertyChanged.connect (trigger_prop_connection, MISSING_INVALIDATOR, boost::bind (&CueStopper::prop_change, this, _1), gui_context()); + + /* route changes */ +// dynamic_cast (_triggerbox->owner())->presentation_info().Change.connect (owner_prop_connection, MISSING_INVALIDATOR, boost::bind (&CueStopper::owner_prop_change, this, _1), gui_context()); + + PropertyChange changed; + changed.add (ARDOUR::Properties::name); + changed.add (ARDOUR::Properties::running); + prop_change (changed); + + ui_parameter_changed("color-file"); +} + +CueStopper::~CueStopper () +{ +} + +void +CueStopper::render (Rect const & area, Cairo::RefPtr context) const +{ + /* Note that item_to_window() already takes _position into account (as + part of item_to_canvas() + */ + Rect self (item_to_window (_rect)); + const Rect draw = self.intersection (area); + + if (!draw) { + return; + } + + float width = _rect.width(); + float height = _rect.height(); + + const double scale = UIConfiguration::instance().get_ui_scale(); + + if (_fill && !_transparent) { + setup_fill_context (context); + context->rectangle (draw.x0, draw.y0, draw.width(), draw.height()); + context->fill (); + } + + //black area around text + set_source_rgba (context, UIConfiguration::instance().color ("theme:bg2")); + context->rectangle (16*scale, 1*scale, _rect.width()-2*scale, _rect.height()-2*scale); + context->fill (); + +#if 0 +//text +Glib::RefPtr layout (Pango::Layout::create (context)); +layout->set_font_description (UIConfiguration::instance().get_NormalFont()); +layout->set_text (name_text->text()); +//text clipping rect +context->save(); +context->rectangle (2, 1, width-4, height-2); +context->clip(); +//calculate the text size +int tw, th; +layout->get_pixel_size (tw, th); +//render the text (centered vertically) +context->translate( 18*scale, (height/2)-(th/2) ); +set_source_rgba (context, UIConfiguration::instance().color ("neutral:foreground")); +layout->show_in_cairo_context (context); +context->restore (); +#endif + + render_children (area, context); + + //fade-over at right + uint32_t bg_color = UIConfiguration::instance().color ("theme:bg"); + double bg_r,bg_g,bg_b, unused; + Gtkmm2ext::color_to_rgba( bg_color, bg_r, bg_g, bg_b, unused); + Cairo::RefPtr left_pattern = Cairo::LinearGradient::create (_rect.width()-12*scale, 0, _rect.width(), 0); + left_pattern->add_color_stop_rgba (0, 0, 0, 0, 0); + left_pattern->add_color_stop_rgba (1, 0, 0, 0, 1); + context->set_source (left_pattern); + context->rectangle( _rect.width()-12*scale, 2*scale, 10*scale, _rect.height()-4*scale ); + context->fill (); +} + +void +CueStopper::owner_prop_change (PropertyChange const & pc) +{ + if (pc.contains (Properties::color)) { + } +} + +void +CueStopper::selection_change () +{ +} + +bool +CueStopper::event_handler (GdkEvent* ev) +{ + switch (ev->type) { + case GDK_BUTTON_PRESS: + if (ev->button.button == 1) { + _triggerbox->request_stop_all (); + return true; + } + break; + case GDK_ENTER_NOTIFY: + if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) { + play_shape->set_fill_color (UIConfiguration::instance().color("neutral:foregroundest")); + } + redraw (); + break; + case GDK_LEAVE_NOTIFY: + if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) { + play_shape->set_fill_color (UIConfiguration::instance().color("neutral:midground")); + } + redraw (); + break; + default: + break; + } + + return false; +} + +void +CueStopper::maybe_update () +{ +/* double nbw; + + if (!_trigger->active()) { + nbw = 0; + } else { + nbw = _trigger->position_as_fraction () * (_allocation.width() - _allocation.height()); + } + + if (nbw) { + const double scale = UIConfiguration::instance().get_ui_scale(); + ArdourCanvas::Rect r (get()); + + active_bar->set (ArdourCanvas::Rect (r.height() * scale, + (r.y0 + 1) * scale, + (r.height() + nbw - 1) * scale, + (r.y1 - 1) * scale)); + active_bar->show (); + } else { + active_bar->hide (); + } + * */ +} + +void +CueStopper::_size_allocate (ArdourCanvas::Rect const & alloc) +{ + Rectangle::_size_allocate (alloc); + + const double scale = UIConfiguration::instance().get_ui_scale(); + poly_margin = 2. * scale; + + const Distance width = _rect.width(); + const Distance height = _rect.height(); + + poly_size = height - (poly_margin*2); + + Points p; + p.push_back (Duple (poly_margin, poly_margin)); + p.push_back (Duple (poly_margin, poly_size)); + p.push_back (Duple (poly_size, poly_size)); + p.push_back (Duple (poly_size, poly_margin)); + play_shape->set (p); + + float tleft = poly_size + (poly_margin*3); + float twidth = width-poly_size-(poly_margin*3); + + ArdourCanvas::Rect text_alloc (tleft, 0, twidth, height); //testing + name_text->size_allocate (text_alloc); + name_text->set_position (Duple (tleft, 1.*scale)); + name_text->clamp_width (twidth); + + //font scale may have changed. uiconfig 'embeds' the ui-scale in the font + name_text->set_font_description (UIConfiguration::instance().get_NormalFont()); +} + +void +CueStopper::prop_change (PropertyChange const & change) +{ + if (change.contains (ARDOUR::Properties::name) + || change.contains (ARDOUR::Properties::running) + ) { + ARDOUR::Trigger *trigger = _triggerbox->currently_playing(); + + if (trigger) { + name_text->set (trigger->region()->name()); + } else { +// name_text->set (""); + } + + redraw(); + } +} + +void +CueStopper::ui_parameter_changed (std::string const& p) +{ + if (p == "color-file") { + set_fill_color (UIConfiguration::instance().color("gtk_background")); + name_text->set_color (UIConfiguration::instance().color("neutral:foreground")); + play_shape->set_fill_color (UIConfiguration::instance().color("neutral:midground")); + } + redraw(); +} + diff --git a/gtk2_ardour/trigger_stopper.h b/gtk2_ardour/trigger_stopper.h new file mode 100644 index 0000000000..b6aa593b05 --- /dev/null +++ b/gtk2_ardour/trigger_stopper.h @@ -0,0 +1,117 @@ +/* + * Author Ben Loftis + * Copyright (C) 2021 Paul Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __ardour_trigger_stopper_h__ +#define __ardour_trigger_stopper_h__ + +#include + +#include + +#include "pbd/properties.h" + +#include "ardour/triggerbox.h" + +#include "canvas/table.h" +#include "canvas/canvas.h" +#include "canvas/rectangle.h" + +namespace Gtk { +class FileChooserDialog; +class Menu; +} + +namespace Temporal { + struct BBT_Offset; +} + +namespace ArdourCanvas { + class Text; + class Polygon; +}; + +class TriggerStopper : public ArdourCanvas::Rectangle +{ + public: + TriggerStopper (ArdourCanvas::Item* canvas, boost::shared_ptr); + ~TriggerStopper (); + + void render (ArdourCanvas::Rect const & area, Cairo::RefPtr context) const; + + void _size_allocate (ArdourCanvas::Rect const & alloc); + + ArdourCanvas::Rectangle* play_button; + ArdourCanvas::Rectangle* active_bar; + ArdourCanvas::Polygon* play_shape; + ArdourCanvas::Text* name_text; + + void maybe_update (); + bool event_handler (GdkEvent*); + void selection_change (); + + private: + boost::shared_ptr _triggerbox; + double poly_size; + double poly_margin; + + PBD::ScopedConnection trigger_prop_connection; + void prop_change (PBD::PropertyChange const & change); + void shape_play_button (); + + PBD::ScopedConnection owner_prop_connection; + void owner_prop_change (PBD::PropertyChange const &); + + void ui_parameter_changed (std::string const& p); +}; + + +class CueStopper : public ArdourCanvas::Rectangle +{ + public: + CueStopper (ArdourCanvas::Item* canvas, boost::shared_ptr); + ~CueStopper (); + + void render (ArdourCanvas::Rect const & area, Cairo::RefPtr context) const; + + void _size_allocate (ArdourCanvas::Rect const & alloc); + + ArdourCanvas::Rectangle* play_button; + ArdourCanvas::Rectangle* active_bar; + ArdourCanvas::Polygon* play_shape; + ArdourCanvas::Text* name_text; + + void maybe_update (); + bool event_handler (GdkEvent*); + void selection_change (); + + private: + boost::shared_ptr _triggerbox; + double poly_size; + double poly_margin; + + PBD::ScopedConnection trigger_prop_connection; + void prop_change (PBD::PropertyChange const & change); + void shape_play_button (); + + PBD::ScopedConnection owner_prop_connection; + void owner_prop_change (PBD::PropertyChange const &); + + void ui_parameter_changed (std::string const& p); +}; +#endif /* __ardour_trigger_stopper_h__ */ diff --git a/gtk2_ardour/trigger_strip.cc b/gtk2_ardour/trigger_strip.cc index 87805dcf38..c93a521899 100644 --- a/gtk2_ardour/trigger_strip.cc +++ b/gtk2_ardour/trigger_strip.cc @@ -38,6 +38,7 @@ #include "mixer_ui.h" #include "plugin_selector.h" #include "plugin_ui.h" +#include "trigger_stopper.h" #include "trigger_strip.h" #include "ui_config.h" @@ -60,8 +61,9 @@ TriggerStrip::TriggerStrip (Session* s, boost::shared_ptr rt) , RouteUI (s) , _clear_meters (true) , _pb_selection () + , _stopper_widget ( -1, 16 ) , _processor_box (s, boost::bind (&TriggerStrip::plugin_selector, this), _pb_selection, 0) - , _trigger_display (*rt->triggerbox ()) + , _trigger_display (*rt->triggerbox (), -1., 8*16.) , _panners (s) , _level_meter (s) { @@ -123,9 +125,10 @@ TriggerStrip::init () /* strip layout */ global_vpacker.set_spacing (2); - global_vpacker.pack_start (_name_button, Gtk::PACK_SHRINK); - global_vpacker.pack_start (_trigger_display, true, true); // XXX + global_vpacker.pack_start (_trigger_display, Gtk::PACK_SHRINK); + global_vpacker.pack_start (_stopper_widget, Gtk::PACK_SHRINK); global_vpacker.pack_start (_processor_box, true, true); + global_vpacker.pack_start (_name_button, Gtk::PACK_SHRINK); global_vpacker.pack_start (_panners, Gtk::PACK_SHRINK); global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK); global_vpacker.pack_start (volume_table, Gtk::PACK_SHRINK); @@ -154,6 +157,7 @@ TriggerStrip::init () ArdourMeter::ResetGroupPeakDisplays.connect (sigc::mem_fun (*this, &TriggerStrip::reset_group_peak_display)); /* Visibility */ + _stopper_widget.show (); _name_button.show (); _trigger_display.show (); _processor_box.show (); @@ -182,6 +186,8 @@ TriggerStrip::set_route (boost::shared_ptr rt) { RouteUI::set_route (rt); + _stopper = new TriggerStopper(_stopper_widget.root(), _route->triggerbox()); + _processor_box.set_route (rt); /* Fader/Gain */ diff --git a/gtk2_ardour/trigger_strip.h b/gtk2_ardour/trigger_strip.h index 3af64a26cc..08a71c2b98 100644 --- a/gtk2_ardour/trigger_strip.h +++ b/gtk2_ardour/trigger_strip.h @@ -32,6 +32,7 @@ #include "automation_controller.h" #include "axis_view.h" +#include "fitted_canvas_widget.h" #include "level_meter.h" #include "panner_ui.h" #include "processor_box.h" @@ -40,6 +41,7 @@ #include "triggerbox_ui.h" class PluginSelector; +class TriggerStopper; class TriggerStrip : public AxisView, public RouteUI, public Gtk::EventBox { @@ -118,10 +120,13 @@ private: Gtk::Table volume_table; /* Widgets */ - ArdourWidgets::ArdourButton _name_button; - ProcessorBox _processor_box; - TriggerBoxWidget _trigger_display; - PannerUI _panners; + FittedCanvasWidget _stopper_widget; + TriggerStopper *_stopper; + + ArdourWidgets::ArdourButton _name_button; + ProcessorBox _processor_box; + TriggerBoxWidget _trigger_display; + PannerUI _panners; LevelMeterVBox _level_meter; boost::shared_ptr _gain_control; }; diff --git a/gtk2_ardour/trigger_ui.cc b/gtk2_ardour/trigger_ui.cc index 1c084b7d07..19aec404c4 100644 --- a/gtk2_ardour/trigger_ui.cc +++ b/gtk2_ardour/trigger_ui.cc @@ -337,6 +337,8 @@ std::string TriggerUI::follow_action_to_string (Trigger::FollowAction fa) { switch (fa) { + case Trigger::None: + return _("None"); case Trigger::Stop: return _("Stop"); case Trigger::Again: diff --git a/gtk2_ardour/triggerbox_ui.cc b/gtk2_ardour/triggerbox_ui.cc index af3ccc3f7c..d40f0609d4 100644 --- a/gtk2_ardour/triggerbox_ui.cc +++ b/gtk2_ardour/triggerbox_ui.cc @@ -52,49 +52,39 @@ using namespace ArdourCanvas; using namespace Gtkmm2ext; using namespace PBD; -TriggerEntry::TriggerEntry (Canvas* canvas, ARDOUR::Trigger& t) - : ArdourCanvas::Rectangle (canvas) +TriggerEntry::TriggerEntry (Item* item, ARDOUR::Trigger& t) + : ArdourCanvas::Rectangle (item) , _trigger (t) { - const double scale = UIConfiguration::instance().get_ui_scale(); - const double width = 150. * scale; - const double height = 20. * scale; + set_layout_sensitive(true); //why??? name = string_compose ("trigger %1", _trigger.index()); - Event.connect (sigc::mem_fun (*this, &TriggerEntry::event_handler)); - - poly_margin = 2. * scale; - poly_size = height - (poly_margin * 2.); - - ArdourCanvas::Rect r (0, 0, width, height); - set (r); - set_outline_all (); - - active_bar = new ArdourCanvas::Rectangle (this); - active_bar->set_outline (false); - - owner_color_changed (); + set_outline (false); play_button = new ArdourCanvas::Rectangle (this); - play_button->set (ArdourCanvas::Rect (poly_margin/2., poly_margin/2., poly_size + ((poly_margin/2.) * 2.), height - ((poly_margin/2.) * 2.))); - play_button->set_outline (false); - play_button->set_fill_color (outline_color()); + play_button->set_outline (true); + play_button->set_fill(true); play_button->name = string_compose ("playbutton %1", _trigger.index()); - play_button->hide (); + play_button->show (); play_shape = new ArdourCanvas::Polygon (play_button); - play_shape->set_fill_color (outline_color()); - play_shape->set_outline (false); play_shape->name = string_compose ("playshape %1", _trigger.index()); - play_shape->hide (); + play_shape->show (); - name_text = new Text (this); - name_text->set_font_description (UIConfiguration::instance().get_NormalFont()); - name_text->set_height_based_on_allocation (true); - name_text->set_color (Gtkmm2ext::contrasting_text_color (fill_color())); - name_text->set_position (Duple (play_button->get().width() + (2. * scale), poly_margin)); - name_text->set_ignore_events (true); + name_button = new ArdourCanvas::Rectangle (this); + name_button->set_outline (true); + name_button->set_fill(true); + name_button->name = ("slot_selector_button"); + name_button->show (); + + name_text = new Text (name_button); + name_text->set_ignore_events (false); + name_text->show(); + + /* watch for change in theme */ + UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &TriggerEntry::ui_parameter_changed)); + set_default_colors(); _trigger.PropertyChanged.connect (trigger_prop_connection, MISSING_INVALIDATOR, boost::bind (&TriggerEntry::prop_change, this, _1), gui_context()); dynamic_cast (_trigger.box().owner())->presentation_info().Change.connect (owner_prop_connection, MISSING_INVALIDATOR, boost::bind (&TriggerEntry::owner_prop_change, this, _1), gui_context()); @@ -102,8 +92,9 @@ TriggerEntry::TriggerEntry (Canvas* canvas, ARDOUR::Trigger& t) PropertyChange changed; changed.add (ARDOUR::Properties::name); changed.add (ARDOUR::Properties::running); - prop_change (changed); + + selection_change(); } TriggerEntry::~TriggerEntry () @@ -121,94 +112,56 @@ TriggerEntry::owner_prop_change (PropertyChange const & pc) void TriggerEntry::owner_color_changed () { - set_fill_color (dynamic_cast (_trigger.box().owner())->presentation_info().color()); - selection_change (); - active_bar->set_fill_color (HSV (fill_color()).darker(0.3).color ()); + //ToDo } void TriggerEntry::selection_change () { if (PublicEditor::instance().get_selection().selected (this)) { - set_outline_color (UIConfiguration::instance().color ("selection")); + name_button->set_outline_color (UIConfiguration::instance().color ("alert:red")); } else { - set_outline_color (HSV (fill_color()).opposite().color()); + set_default_colors(); } - -} - -bool -TriggerEntry::event_handler (GdkEvent* ev) -{ - switch (ev->type) { - case GDK_BUTTON_PRESS: - PublicEditor::instance().get_selection().set (this); - break; - case GDK_ENTER_NOTIFY: - if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) { - play_button->show (); - play_shape->show (); - } - redraw (); - break; - case GDK_LEAVE_NOTIFY: - if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) { - play_button->hide (); - play_shape->hide (); - } - redraw (); - break; - default: - break; - } - - return false; } void TriggerEntry::maybe_update () { - double nbw; - - if (!_trigger.active()) { - nbw = 0; - } else { - nbw = _trigger.position_as_fraction () * (_allocation.width() - _allocation.height()); - } - - if (nbw) { - const double scale = UIConfiguration::instance().get_ui_scale(); - ArdourCanvas::Rect r (get()); - - active_bar->set (ArdourCanvas::Rect (r.height() * scale, - (r.y0 + 1) * scale, - (r.height() + nbw - 1) * scale, - (r.y1 - 1) * scale)); - active_bar->show (); - } else { - active_bar->hide (); - } + //what here? } void TriggerEntry::_size_allocate (ArdourCanvas::Rect const & alloc) { - const double scale = UIConfiguration::instance().get_ui_scale(); - Rectangle::_size_allocate (alloc); - const Distance height = get().height(); + const Distance width = _rect.width(); + const Distance height = _rect.height(); - poly_size = height - (poly_margin * 2.); + play_button->set (ArdourCanvas::Rect (0, 0, height, height)); + name_button->set (ArdourCanvas::Rect (height, 0, width, height)); + const double scale = UIConfiguration::instance().get_ui_scale(); + poly_margin = 2. * scale; + poly_size = height - 2*poly_margin; shape_play_button (); - play_button->set (ArdourCanvas::Rect (poly_margin/2., poly_margin/2., poly_size + ((poly_margin/2.) * 2.), height - ((poly_margin/2.) * 2.))); - const Distance lhs = play_button->get().width() + (2. * poly_margin * scale); - ArdourCanvas::Rect text_alloc (lhs, poly_margin, alloc.width() - lhs - (poly_margin * scale), alloc.height() - (2. * poly_margin)); - name_text->size_allocate (text_alloc); + float tleft = height; //make room for the play button + float twidth = name_button->width() - poly_margin*2; - name_text->set_position (Duple (lhs, poly_margin)); + name_text->size_allocate (Rect(0, 0, width, height)); + name_text->set_position (Duple (tleft + poly_margin, poly_margin -0.5)); + name_text->clamp_width ( width - height ); + + //font scale may have changed. uiconfig 'embeds' the ui-scale in the font + name_text->set_font_description (UIConfiguration::instance().get_NormalFont()); +} + +void +TriggerEntry::render (Rect const & area, Cairo::RefPtr context) const +{ + Rectangle::render(area, context); } void @@ -226,15 +179,17 @@ TriggerEntry::shape_play_button () /* region exists; draw triangle to show that we can trigger */ p.push_back (Duple (poly_margin, poly_margin)); p.push_back (Duple (poly_margin, poly_size)); - p.push_back (Duple (poly_size, poly_size / 2.)); + p.push_back (Duple (poly_size, 0.5+poly_size / 2.)); } play_shape->set (p); if (_trigger.active()) { - play_button->set_fill (true); + play_shape->set_outline (false); + play_shape->set_fill (true); } else { - play_button->set_fill (false); + play_shape->set_outline (true); + play_shape->set_fill (false); } } @@ -247,7 +202,6 @@ TriggerEntry::prop_change (PropertyChange const & change) if (_trigger.region()) { name_text->set (short_version (_trigger.name(), 16)); } else { - /* we need some spaces to have something to click on */ name_text->set (""); } @@ -263,6 +217,41 @@ TriggerEntry::prop_change (PropertyChange const & change) } } +void +TriggerEntry::set_default_colors () +{ + set_fill_color (UIConfiguration::instance().color ("theme:bg")); + play_button->set_fill_color (UIConfiguration::instance().color("theme:bg")); + play_button->set_outline_color (UIConfiguration::instance().color("theme:bg")); + name_button->set_fill_color (UIConfiguration::instance().color("theme:bg")); + name_button->set_outline_color (UIConfiguration::instance().color("theme:bg")); + if ((_trigger.index()/2)%2==0) { + set_fill_color (HSV (fill_color()).darker(0.15).color ()); + play_button->set_fill_color (HSV (fill_color()).darker(0.15).color ()); + play_button->set_outline_color (HSV (fill_color()).darker(0.15).color ()); + name_button->set_fill_color (HSV (fill_color()).darker(0.15).color ()); + name_button->set_outline_color (HSV (fill_color()).darker(0.15).color ()); + } + + name_text->set_color (UIConfiguration::instance().color("neutral:midground")); + + play_shape->set_outline_color (UIConfiguration::instance().color("neutral:midground")); + play_shape->set_fill_color (UIConfiguration::instance().color("neutral:midground")); + + /*preserve selection border*/ + if (PublicEditor::instance().get_selection().selected (this)) { + name_button->set_outline_color (UIConfiguration::instance().color ("alert:red")); + } +} + +void +TriggerEntry::ui_parameter_changed (std::string const& p) +{ + if (p == "color-file") { + set_default_colors(); + } +} + /* ---------------------------- */ @@ -271,14 +260,14 @@ Gtkmm2ext::Bindings* TriggerBoxUI::bindings = 0; Glib::RefPtr TriggerBoxUI::trigger_actions; TriggerBoxUI::TriggerBoxUI (ArdourCanvas::Item* parent, TriggerBox& tb) - : Table (parent) + : Rectangle (parent) , _triggerbox (tb) , file_chooser (0) , _context_menu (0) { - set_homogenous (true); - set_row_spacing (4); - set_fill_color (UIConfiguration::instance().color (X_("theme:bg"))); + set_layout_sensitive(true); //why??? + + set_fill_color (UIConfiguration::instance().color(X_("theme:bg"))); set_fill (true); build (); @@ -337,7 +326,6 @@ TriggerBoxUI::build () { Trigger* t; uint64_t n = 0; - uint32_t row = 0; // clear_items (true); @@ -348,30 +336,60 @@ TriggerBoxUI::build () if (!t) { break; } - TriggerEntry* te = new TriggerEntry (canvas(), *t); - attach (te, 0, row++, PackExpand, PackExpand); + TriggerEntry* te = new TriggerEntry (this, *t); _slots.push_back (te); - te->play_button->Event.connect (sigc::bind (sigc::mem_fun (*this, &TriggerBoxUI::bang), n)); - te->name_text->Event.connect (sigc::bind (sigc::mem_fun (*this, &TriggerBoxUI::text_event), n)); - te->Event.connect (sigc::bind (sigc::mem_fun (*this, &TriggerBoxUI::event), n)); + te->play_button->Event.connect (sigc::bind (sigc::mem_fun (*this, &TriggerBoxUI::play_button_event), n)); + te->name_button->Event.connect (sigc::bind (sigc::mem_fun (*this, &TriggerBoxUI::text_button_event), n)); +// te->Event.connect (sigc::bind (sigc::mem_fun (*this, &TriggerBoxUI::event), n)); ++n; } } -bool -TriggerBoxUI::text_event (GdkEvent *ev, uint64_t n) +void +TriggerBoxUI::_size_allocate (ArdourCanvas::Rect const & alloc) { - return false; + Rectangle::_size_allocate (alloc); + + const float width = alloc.width(); + const float height = alloc.height(); + + const float slot_h = height / TriggerBox::default_triggers_per_box; //ToDo + + float ypos = 0; + for (auto & slot : _slots) { + slot->size_allocate (Rect(0, 0, width, slot_h)); + slot->set_position (Duple (0, ypos)); + ypos += slot_h; + slot->show(); + } + } bool -TriggerBoxUI::event (GdkEvent* ev, uint64_t n) +TriggerBoxUI::text_button_event (GdkEvent* ev, uint64_t n) { switch (ev->type) { + case GDK_ENTER_NOTIFY: + if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) { + _slots[n]->name_text->set_fill_color (UIConfiguration::instance().color ("neutral:foreground")); + _slots[n]->name_text->set_color (UIConfiguration::instance().color ("neutral:foreground")); + } + break; + case GDK_LEAVE_NOTIFY: + if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) { + _slots[n]->set_default_colors(); + } + break; case GDK_BUTTON_PRESS: + if (_slots[n]->trigger().region()) { + PublicEditor::instance().get_selection().set (_slots[n]); + //a side-effect of selection-change is that the slot's color is reset. retain the "entered-color" here: + _slots[n]->name_text->set_fill_color (UIConfiguration::instance().color ("neutral:foreground")); + _slots[n]->name_text->set_color (UIConfiguration::instance().color ("neutral:foreground")); + } break; case GDK_2BUTTON_PRESS: edit_trigger (n); @@ -392,9 +410,8 @@ TriggerBoxUI::event (GdkEvent* ev, uint64_t n) return false; } - bool -TriggerBoxUI::bang (GdkEvent *ev, uint64_t n) +TriggerBoxUI::play_button_event (GdkEvent *ev, uint64_t n) { if (!_triggerbox.trigger (n)->region()) { /* this is a stop button */ @@ -432,6 +449,17 @@ TriggerBoxUI::bang (GdkEvent *ev, uint64_t n) break; } break; + case GDK_ENTER_NOTIFY: + if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) { + _slots[n]->play_shape->set_fill_color (UIConfiguration::instance().color ("neutral:foreground")); + _slots[n]->play_shape->set_outline_color (UIConfiguration::instance().color ("neutral:foreground")); + } + break; + case GDK_LEAVE_NOTIFY: + if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) { + _slots[n]->set_default_colors(); + } + break; default: break; } @@ -698,29 +726,23 @@ TriggerBoxUI::rapid_update () /* ------------ */ -TriggerBoxWidget::TriggerBoxWidget (TriggerBox& tb) +TriggerBoxWidget::TriggerBoxWidget (TriggerBox& tb, float w, float h) : FittedCanvasWidget(w,h) { ui = new TriggerBoxUI (root(), tb); set_background_color (UIConfiguration::instance().color (X_("theme:bg"))); } -void -TriggerBoxWidget::size_request (double& w, double& h) const -{ - ui->size_request (w, h); -} - void TriggerBoxWidget::on_map () { - GtkCanvas::on_map (); + FittedCanvasWidget::on_map (); ui->start_updating (); } void TriggerBoxWidget::on_unmap () { - GtkCanvas::on_unmap (); + FittedCanvasWidget::on_unmap (); ui->stop_updating (); } @@ -729,15 +751,10 @@ TriggerBoxWidget::on_unmap () TriggerBoxWindow::TriggerBoxWindow (TriggerBox& tb) { - TriggerBoxWidget* tbw = manage (new TriggerBoxWidget (tb)); + TriggerBoxWidget* tbw = manage (new TriggerBoxWidget (tb, -1., TriggerBox::default_triggers_per_box*16.)); set_title (_("TriggerBox for XXXX")); - double w; - double h; - - tbw->size_request (w, h); - - set_default_size (w, h); + set_default_size (-1., TriggerBox::default_triggers_per_box*16.); add (*tbw); tbw->show (); } diff --git a/gtk2_ardour/triggerbox_ui.h b/gtk2_ardour/triggerbox_ui.h index caef6e66fa..6bf6bbcfb7 100644 --- a/gtk2_ardour/triggerbox_ui.h +++ b/gtk2_ardour/triggerbox_ui.h @@ -31,6 +31,8 @@ #include "canvas/canvas.h" #include "canvas/rectangle.h" +#include "fitted_canvas_widget.h" + namespace Gtk { class FileChooserDialog; class Menu; @@ -48,21 +50,26 @@ namespace ArdourCanvas { class TriggerEntry : public ArdourCanvas::Rectangle { public: - TriggerEntry (ArdourCanvas::Canvas* canvas, ARDOUR::Trigger&); + TriggerEntry (ArdourCanvas::Item* item, ARDOUR::Trigger&); ~TriggerEntry (); ARDOUR::Trigger& trigger() const { return _trigger; } ArdourCanvas::Rectangle* play_button; - ArdourCanvas::Rectangle* active_bar; ArdourCanvas::Polygon* play_shape; + + ArdourCanvas::Rectangle* name_button; ArdourCanvas::Text* name_text; + void render (ArdourCanvas::Rect const & area, Cairo::RefPtr context) const; + void _size_allocate (ArdourCanvas::Rect const &); void maybe_update (); - bool event_handler (GdkEvent*); + void selection_change (); + void set_default_colors(); + private: ARDOUR::Trigger& _trigger; double poly_size; @@ -75,9 +82,11 @@ class TriggerEntry : public ArdourCanvas::Rectangle PBD::ScopedConnection owner_prop_connection; void owner_prop_change (PBD::PropertyChange const &); void owner_color_changed (); + + void ui_parameter_changed (std::string const& p); }; -class TriggerBoxUI : public ArdourCanvas::Table +class TriggerBoxUI : public ArdourCanvas::Rectangle { public: TriggerBoxUI (ArdourCanvas::Item* parent, ARDOUR::TriggerBox&); @@ -92,6 +101,8 @@ class TriggerBoxUI : public ArdourCanvas::Table static void trigger_scene (int32_t); + void _size_allocate (ArdourCanvas::Rect const &); + private: ARDOUR::TriggerBox& _triggerbox; typedef std::vector Slots; @@ -104,9 +115,8 @@ class TriggerBoxUI : public ArdourCanvas::Table static void load_bindings (); static void register_actions (); - bool bang (GdkEvent*, uint64_t); - bool text_event (GdkEvent*, uint64_t); - bool event (GdkEvent*, uint64_t); + bool play_button_event (GdkEvent*, uint64_t); + bool text_button_event (GdkEvent*, uint64_t); void choose_sample (uint64_t n); void sample_chosen (int r, uint64_t n); @@ -125,11 +135,10 @@ class TriggerBoxUI : public ArdourCanvas::Table sigc::connection selection_connection; }; -class TriggerBoxWidget : public ArdourCanvas::GtkCanvas +class TriggerBoxWidget : public FittedCanvasWidget { public: - TriggerBoxWidget (ARDOUR::TriggerBox& tb); - void size_request (double& w, double& h) const; + TriggerBoxWidget (ARDOUR::TriggerBox& tb, float w, float h); void on_map (); void on_unmap (); diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index a1058809d6..5cd9c5bfcc 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -301,6 +301,7 @@ gtk2_ardour_sources = [ 'transpose_dialog.cc', 'trigger_page.cc', 'trigger_strip.cc', + 'trigger_stopper.cc', 'trigger_ui.cc', 'triggerbox_ui.cc', 'ui_config.cc',