cont'd work on mini-timeline:

* add context-menu to configure visible time-span
* prelight markers on mouse-over
* constrain active click area (mouse-up)
* change scroll-event granularity: multiple of configured span.
This commit is contained in:
Robin Gareus 2016-12-19 19:12:13 +01:00
parent 53be243f6a
commit d82abddfff
2 changed files with 174 additions and 24 deletions

View File

@ -48,7 +48,13 @@ MiniTimeline::MiniTimeline ()
, _px_per_sample (0)
, _time_granularity (0)
, _time_span_samples (0)
, _marker_height (0)
, _pointer_x (-1)
, _pointer_y (-1)
, _minitl_context_menu (0)
{
add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK);
_layout = Pango::Layout::create (get_pango_context());
UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &MiniTimeline::set_colors));
@ -66,6 +72,8 @@ MiniTimeline::MiniTimeline ()
MiniTimeline::~MiniTimeline ()
{
delete _minitl_context_menu;
_minitl_context_menu = 0;
}
void
@ -75,6 +83,8 @@ MiniTimeline::session_going_away ()
session_connection.drop_connections ();
SessionHandlePtr::session_going_away ();
_jumplist.clear ();
delete _minitl_context_menu;
_minitl_context_menu = 0;
}
void
@ -163,6 +173,19 @@ MiniTimeline::on_size_allocate (Gtk::Allocation& alloc)
calculate_time_spacing ();
}
void
MiniTimeline::set_span (framecnt_t ts)
{
assert (_session);
if (_session->config.get_minitimeline_span () == ts) {
return;
}
_session->config.set_minitimeline_span (ts);
calculate_time_spacing ();
update_minitimeline ();
}
void
MiniTimeline::super_rapid_update ()
{
@ -224,7 +247,7 @@ MiniTimeline::calculate_time_spacing ()
return;
}
const framepos_t time_span = _session->config.get_minitimeline_span () / 2;
const framecnt_t time_span = _session->config.get_minitimeline_span () / 2;
_time_span_samples = time_span * _session->nominal_frame_rate ();
_time_granularity = _session->nominal_frame_rate () * ceil (2. * time_span / _n_labels);
_px_per_sample = get_width () / (2. * _time_span_samples);
@ -287,8 +310,9 @@ MiniTimeline::draw_dots (cairo_t* cr, int left, int right, int y, ArdourCanvas::
}
int
MiniTimeline::draw_mark (cairo_t* cr, int x0, int x1, int h, const std::string& label)
MiniTimeline::draw_mark (cairo_t* cr, int x0, int x1, const std::string& label, bool& prelight)
{
int h = _marker_height;
/* ArdourMarker shape
* MH = 13
*
@ -310,13 +334,19 @@ MiniTimeline::draw_mark (cairo_t* cr, int x0, int x1, int h, const std::string&
int lw, lh;
_layout->set_text (label);
_layout->get_pixel_size (lw, lh);
int rw = std::min (x1, x0 + w2 + lw + 2);
if (_pointer_y >= 0 && _pointer_y < h && _pointer_x >= x0 && _pointer_x <= rw) {
prelight = true;
}
// TODO cache in set_colors()
uint32_t color = UIConfiguration::instance().color (
prelight ? "entered marker" : "location marker");
// TODO cache, set_colors()
uint32_t color = UIConfiguration::instance().color ("location marker");
double r, g, b, a;
ArdourCanvas::color_to_rgba (color, r, g, b, a);
int rw = std::min (x1, x0 + w2 + lw + 2);
if (rw < x0) {
rw = x1;
} else {
@ -421,9 +451,9 @@ MiniTimeline::render (cairo_t* cr, cairo_rectangle_t*)
_layout->set_text (X_("Marker@"));
_layout->get_pixel_size (tw, th);
const int mh = th + 2;
assert (mh > 4);
const int mw = (mh - 1) / 4;
_marker_height = th + 2;
assert (_marker_height > 4);
const int mw = (_marker_height - 1) / 4;
lmin -= mw / _px_per_sample;
lmax += mw / _px_per_sample;
@ -460,7 +490,8 @@ MiniTimeline::render (cairo_t* cr, cairo_rectangle_t*)
LocationMarkerSort location_marker_sort;
std::sort (lm.begin(), lm.end(), location_marker_sort);
for (std::vector<LocationMarker>::const_iterator l = lm.begin(); l != lm.end();) {
int id = 0;
for (std::vector<LocationMarker>::const_iterator l = lm.begin(); l != lm.end(); ++id) {
framepos_t when = (*l).when;
int x0 = floor (get_width() * .5 + (when - p) * _px_per_sample);
int x1 = get_width();
@ -468,8 +499,9 @@ MiniTimeline::render (cairo_t* cr, cairo_rectangle_t*)
if (++l != lm.end()) {
x1 = floor (get_width() * .5 + ((*l).when - p) * _px_per_sample) - 1 - mw;
}
x1 = draw_mark (cr, x0, x1, mh, label);
_jumplist.push_back (JumpRange (x0 - mw, x1, when));
bool prelight = false;
x1 = draw_mark (cr, x0, x1, label, prelight);
_jumplist.push_back (JumpRange (x0 - mw, x1, when, prelight));
}
/* playhead on top */
@ -487,19 +519,72 @@ MiniTimeline::render (cairo_t* cr, cairo_rectangle_t*)
cairo_fill (cr);
}
void
MiniTimeline::build_minitl_context_menu ()
{
using namespace Gtk;
using namespace Gtk::Menu_Helpers;
assert (_session);
const framecnt_t time_span = _session->config.get_minitimeline_span ();
_minitl_context_menu = new Gtk::Menu();
MenuList& items = _minitl_context_menu->items();
// ideally this would have a heading (or rather be a sub-menu to "Visible Time")
std::map<framecnt_t, std::string> spans;
spans[30] = _("30 sec");
spans[60] = _("1 min");
spans[120] = _("2 mins");
spans[300] = _("5 mins");
spans[600] = _("10 mins");
spans[1200] = _("20 mins");
RadioMenuItem::Group span_group;
for (std::map<framecnt_t, std::string>::const_iterator i = spans.begin (); i != spans.end (); ++i) {
items.push_back (RadioMenuElem (span_group, i->second, sigc::bind (sigc::mem_fun (*this, &MiniTimeline::set_span), i->first)));
if (time_span == i->first) {
static_cast<RadioMenuItem*>(&items.back())->set_active ();
}
}
}
void
MiniTimeline::show_minitl_context_menu ()
{
if (_minitl_context_menu == 0) {
build_minitl_context_menu ();
}
_minitl_context_menu->popup (1, gtk_get_current_event_time());
}
bool
MiniTimeline::on_button_press_event (GdkEventButton *ev)
{
if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
if (_session) {
show_minitl_context_menu ();
}
return true;
}
return true;
}
bool
MiniTimeline::on_button_release_event (GdkEventButton *ev)
{
if (!_session) { return true; }
if (ev->y < 0 || ev->y > get_height () || ev->x < 0 || ev->x > get_width ()) {
return true;
}
for (JumpList::const_iterator i = _jumplist.begin (); i != _jumplist.end(); ++i) {
if (i->left < ev->x && ev->x < i->right) {
if (ev->button == 3) {
PublicEditor::instance().center_screen (i->to);
} else if (ev->button == 1) {
if (ev->y <= _marker_height) {
for (JumpList::const_iterator i = _jumplist.begin (); i != _jumplist.end(); ++i) {
if (i->left < ev->x && ev->x < i->right) {
_session->request_locate (i->to, _session->transport_rolling ());
return true;
}
return true;
}
}
@ -511,12 +596,58 @@ MiniTimeline::on_button_release_event (GdkEventButton *ev)
return true;
}
bool
MiniTimeline::on_motion_notify_event (GdkEventMotion *ev)
{
if (!_session) { return true; }
_pointer_x = ev->x;
_pointer_y = ev->y;
bool need_expose = false;
for (JumpList::const_iterator i = _jumplist.begin (); i != _jumplist.end(); ++i) {
if (i->left < ev->x && ev->x < i->right && ev->y <= _marker_height) {
if (!(*i).prelight) {
need_expose = true;
break;
}
} else {
if ((*i).prelight) {
need_expose = true;
break;
}
}
}
if (need_expose) {
update_minitimeline ();
}
return true;
}
bool
MiniTimeline::on_leave_notify_event (GdkEventCrossing *ev)
{
CairoWidget::on_leave_notify_event (ev);
_pointer_x = _pointer_y = -1;
for (JumpList::const_iterator i = _jumplist.begin (); i != _jumplist.end(); ++i) {
if ((*i).prelight) {
update_minitimeline ();
break;
}
}
return true;
}
bool
MiniTimeline::on_scroll_event (GdkEventScroll *ev)
{
if (!_session) { return true; }
const framecnt_t time_span = _session->config.get_minitimeline_span ();
framepos_t when = _session->audible_frame ();
double scale = 2.0;
double scale = time_span / 60.0;
if (ev->state & Gtkmm2ext::Keyboard::GainFineScaleModifier) {
if (ev->state & Gtkmm2ext::Keyboard::GainExtraFineScaleModifier) {

View File

@ -25,6 +25,7 @@
#include "pbd/signals.h"
#include "ardour/ardour.h"
#include "ardour/types.h"
#include "ardour/session_handle.h"
#include "gtkmm2ext/cairo_widget.h"
@ -36,6 +37,10 @@ namespace ARDOUR {
class Session;
}
namespace Gtk {
class Menu;
}
class MiniTimeline : public CairoWidget, public ARDOUR::SessionHandlePtr, public PBD::ScopedConnectionList
{
@ -60,13 +65,20 @@ private:
void calculate_time_spacing ();
void update_minitimeline ();
void draw_dots (cairo_t*, int left, int right, int y, ArdourCanvas::Color);
int draw_mark (cairo_t*, int x0, int x1, int h, const std::string&);
int draw_mark (cairo_t*, int x0, int x1, const std::string&, bool& prelight);
void render (cairo_t*, cairo_rectangle_t*);
void format_time (framepos_t when);
bool on_button_release_event (GdkEventButton *ev);
bool on_button_press_event (GdkEventButton*);
bool on_button_release_event (GdkEventButton*);
bool on_scroll_event (GdkEventScroll*);
bool on_motion_notify_event (GdkEventMotion*);
bool on_leave_notify_event (GdkEventCrossing*);
void build_minitl_context_menu ();
void show_minitl_context_menu ();
void set_span (ARDOUR::framecnt_t);
Glib::RefPtr<Pango::Layout> _layout;
sigc::connection super_rapid_connection;
@ -80,15 +92,22 @@ private:
int _n_labels;
double _px_per_sample;
framepos_t _time_granularity;
framepos_t _time_span_samples;
ARDOUR::framecnt_t _time_granularity;
ARDOUR::framecnt_t _time_span_samples;
int _marker_height;
int _pointer_x;
int _pointer_y;
Gtk::Menu* _minitl_context_menu;
struct JumpRange {
JumpRange (int l, int r, framepos_t t)
: left (l), right (r), to (t) {}
JumpRange (int l, int r, framepos_t t, bool p = false)
: left (l), right (r), to (t), prelight (p) {}
int left;
int right;
framepos_t to;
bool prelight;
};
typedef std::list <JumpRange> JumpList;