Added support for audacity style playback period, and added some
documentation
This commit is contained in:
parent
885704f0f8
commit
49f87b28bb
7 changed files with 278 additions and 23 deletions
|
|
@ -140,16 +140,15 @@ style "timeline_ruler" = "default_base"
|
|||
gtkmm__CustomObject_TimelineRuler::min_division_width = 100
|
||||
gtkmm__CustomObject_TimelineRuler::mouse_chevron_size = 5
|
||||
gtkmm__CustomObject_TimelineRuler::selection_chevron_size = 5
|
||||
gtkmm__CustomObject_TimelineRuler::playback_arrow_colour = "#2D2D90"
|
||||
gtkmm__CustomObject_TimelineRuler::playback_arrow_alpha = 0.5
|
||||
gtkmm__CustomObject_TimelineRuler::playback_arrow_size = 10
|
||||
gtkmm__CustomObject_TimelineRuler::playback_arrow_stem_size = 3
|
||||
}
|
||||
|
||||
style "timeline_header_base" = "default_base"
|
||||
{
|
||||
# fg[NORMAL] = { 0.77, 0.77, 0.72 }
|
||||
# bg[NORMAL] = { 0.18, 0.19, 0.22 }
|
||||
# bg[ACTIVE] = { 0.20, 0.20, 0.20 }
|
||||
# bg[PRELIGHT] = { 0.20, 0.20, 0.20 }
|
||||
# bg[INSENSITIVE] = { 0.20, 0.20, 0.20 }
|
||||
# bg[SELECTED] = { 0.20, 0.20, 0.20 }
|
||||
|
||||
}
|
||||
|
||||
class "gtkmm__CustomObject_TimelineBody" style:highest "timeline_body"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ TimelineWidget::TimelineWidget() :
|
|||
verticalAdjustment(0, 0, 0),
|
||||
selectionStart(0),
|
||||
selectionEnd(0),
|
||||
playbackPeriodStart(0),
|
||||
playbackPeriodEnd(0),
|
||||
horizontalScroll(horizontalAdjustment),
|
||||
verticalScroll(verticalAdjustment)
|
||||
{
|
||||
|
|
@ -183,8 +185,12 @@ TimelineWidget::get_selection_end() const
|
|||
}
|
||||
|
||||
void
|
||||
TimelineWidget::set_selection(gavl_time_t start, gavl_time_t end)
|
||||
TimelineWidget::set_selection(gavl_time_t start, gavl_time_t end,
|
||||
bool reset_playback_period)
|
||||
{
|
||||
REQUIRE(ruler != NULL);
|
||||
REQUIRE(body != NULL);
|
||||
|
||||
if(start < end)
|
||||
{
|
||||
selectionStart = start;
|
||||
|
|
@ -196,6 +202,46 @@ TimelineWidget::set_selection(gavl_time_t start, gavl_time_t end)
|
|||
selectionStart = end;
|
||||
selectionEnd = start;
|
||||
}
|
||||
|
||||
if(reset_playback_period)
|
||||
{
|
||||
playbackPeriodStart = selectionStart;
|
||||
playbackPeriodEnd = selectionEnd;
|
||||
}
|
||||
|
||||
ruler->queue_draw();
|
||||
body->queue_draw();
|
||||
}
|
||||
|
||||
gavl_time_t
|
||||
TimelineWidget::get_playback_period_start() const
|
||||
{
|
||||
return playbackPeriodStart;
|
||||
}
|
||||
|
||||
gavl_time_t
|
||||
TimelineWidget::get_playback_period_end() const
|
||||
{
|
||||
return playbackPeriodEnd;
|
||||
}
|
||||
|
||||
void
|
||||
TimelineWidget::set_playback_period(gavl_time_t start, gavl_time_t end)
|
||||
{
|
||||
REQUIRE(ruler != NULL);
|
||||
REQUIRE(body != NULL);
|
||||
|
||||
if(start < end)
|
||||
{
|
||||
playbackPeriodStart = start;
|
||||
playbackPeriodEnd = end;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The period is back-to-front, flip it round
|
||||
playbackPeriodStart = end;
|
||||
playbackPeriodEnd = start;
|
||||
}
|
||||
|
||||
ruler->queue_draw();
|
||||
body->queue_draw();
|
||||
|
|
|
|||
|
|
@ -120,8 +120,30 @@ public:
|
|||
|
||||
/**
|
||||
* Sets the period of the selection.
|
||||
* @param start The start time.
|
||||
* @param end The end time.
|
||||
* @param reset_playback_period Specifies whether to set the playback
|
||||
* period to the same as this new selection.
|
||||
*/
|
||||
void set_selection(gavl_time_t start, gavl_time_t end);
|
||||
void set_selection(gavl_time_t start, gavl_time_t end,
|
||||
bool reset_playback_period = true);
|
||||
|
||||
/**
|
||||
* Gets the time at which the playback period begins.
|
||||
*/
|
||||
gavl_time_t get_playback_period_start() const;
|
||||
|
||||
/**
|
||||
* Gets the time at which the playback period ends.
|
||||
*/
|
||||
gavl_time_t get_playback_period_end() const;
|
||||
|
||||
/**
|
||||
* Sets the playback period.
|
||||
* @param start The start time.
|
||||
* @param end The end time.
|
||||
*/
|
||||
void set_playback_period(gavl_time_t start, gavl_time_t end);
|
||||
|
||||
/**
|
||||
* Gets the type of the tool currently active.
|
||||
|
|
@ -169,6 +191,8 @@ protected:
|
|||
// Selection State
|
||||
gavl_time_t selectionStart;
|
||||
gavl_time_t selectionEnd;
|
||||
gavl_time_t playbackPeriodStart;
|
||||
gavl_time_t playbackPeriodEnd;
|
||||
|
||||
int totalHeight;
|
||||
|
||||
|
|
|
|||
|
|
@ -349,23 +349,18 @@ TimelineBody::register_styles() const
|
|||
{
|
||||
GtkWidgetClass *klass = GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS(gobj()));
|
||||
|
||||
gtk_widget_class_install_style_property(
|
||||
GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS(gobj())),
|
||||
g_param_spec_boxed("background",
|
||||
"Track Background",
|
||||
gtk_widget_class_install_style_property(klass,
|
||||
g_param_spec_boxed("background", "Track Background",
|
||||
"The background colour of timeline tracks",
|
||||
GDK_TYPE_COLOR, G_PARAM_READABLE));
|
||||
|
||||
gtk_widget_class_install_style_property(
|
||||
GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS(gobj())),
|
||||
g_param_spec_boxed("selection",
|
||||
"End lines of a selection",
|
||||
gtk_widget_class_install_style_property(klass,
|
||||
g_param_spec_boxed("selection", "End lines of a selection",
|
||||
"The colour of selection limit lines",
|
||||
GDK_TYPE_COLOR, G_PARAM_READABLE));
|
||||
|
||||
gtk_widget_class_install_style_property(klass,
|
||||
g_param_spec_float("selection_alpha",
|
||||
"Selection Alpha",
|
||||
g_param_spec_float("selection_alpha", "Selection Alpha",
|
||||
"The transparency of the selection marque.",
|
||||
0, 1.0, 0.5, G_PARAM_READABLE));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ IBeamTool::on_button_press_event(GdkEventButton* event)
|
|||
Tool::on_button_press_event(event);
|
||||
|
||||
TimelineWidget *timeline_widget = get_timeline_widget();
|
||||
REQUIRE(timeline_widget != NULL);
|
||||
|
||||
if(event->button == 1)
|
||||
{
|
||||
|
|
@ -181,12 +182,16 @@ void
|
|||
IBeamTool::set_leading_x(const int x)
|
||||
{
|
||||
TimelineWidget *timeline_widget = get_timeline_widget();
|
||||
REQUIRE(timeline_widget != NULL);
|
||||
|
||||
const bool set_playback_period = dragType == Selection;
|
||||
const gavl_time_t time = timeline_widget->x_to_time(x);
|
||||
if(time > pinnedDragTime)
|
||||
timeline_widget->set_selection(pinnedDragTime, time);
|
||||
timeline_widget->set_selection(
|
||||
pinnedDragTime, time, set_playback_period);
|
||||
else
|
||||
timeline_widget->set_selection(time, pinnedDragTime);
|
||||
timeline_widget->set_selection(
|
||||
time, pinnedDragTime, set_playback_period);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ namespace timeline {
|
|||
TimelineRuler::TimelineRuler(
|
||||
lumiera::gui::widgets::TimelineWidget *timeline_widget) :
|
||||
Glib::ObjectBase("TimelineRuler"),
|
||||
isDragging(false),
|
||||
pinnedDragTime(0),
|
||||
mouseChevronOffset(0),
|
||||
annotationHorzMargin(0),
|
||||
annotationVertMargin(0),
|
||||
|
|
@ -54,6 +56,9 @@ TimelineRuler::TimelineRuler(
|
|||
minDivisionWidth(100),
|
||||
mouseChevronSize(5),
|
||||
selectionChevronSize(5),
|
||||
playbackArrowAlpha(0.5f),
|
||||
playbackArrowSize(10),
|
||||
playbackArrowStemSize(3),
|
||||
timelineWidget(timeline_widget)
|
||||
{
|
||||
REQUIRE(timelineWidget != NULL);
|
||||
|
|
@ -82,7 +87,10 @@ TimelineRuler::on_realize()
|
|||
Widget::on_realize();
|
||||
|
||||
// Set event notifications
|
||||
add_events(Gdk::POINTER_MOTION_MASK | Gdk::SCROLL_MASK);
|
||||
add_events(Gdk::POINTER_MOTION_MASK |
|
||||
Gdk::SCROLL_MASK |
|
||||
Gdk::BUTTON_PRESS_MASK |
|
||||
Gdk::BUTTON_RELEASE_MASK);
|
||||
|
||||
// Load styles
|
||||
read_styles();
|
||||
|
|
@ -130,16 +138,47 @@ TimelineRuler::on_expose_event(GdkEventExpose* event)
|
|||
// Draw the overlays
|
||||
draw_mouse_chevron(cr, allocation);
|
||||
draw_selection(cr, allocation);
|
||||
draw_playback_period(cr, allocation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TimelineRuler::on_button_press_event(GdkEventButton* event)
|
||||
{
|
||||
REQUIRE(event != NULL);
|
||||
|
||||
if(event->button == 1)
|
||||
{
|
||||
pinnedDragTime = timelineWidget->x_to_time(event->x);
|
||||
isDragging = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TimelineRuler::on_button_release_event(GdkEventButton* event)
|
||||
{
|
||||
REQUIRE(event != NULL);
|
||||
|
||||
if(event->button == 1)
|
||||
isDragging = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TimelineRuler::on_motion_notify_event(GdkEventMotion *event)
|
||||
{
|
||||
REQUIRE(event != NULL);
|
||||
|
||||
set_mouse_chevron_offset(event->x);
|
||||
|
||||
if(isDragging)
|
||||
set_leading_x(event->x);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -162,6 +201,18 @@ TimelineRuler::on_size_allocate(Gtk::Allocation& allocation)
|
|||
rulerImage.clear(); // The widget has changed size - redraw
|
||||
}
|
||||
|
||||
void
|
||||
TimelineRuler::set_leading_x(const int x)
|
||||
{
|
||||
REQUIRE(timelineWidget != NULL);
|
||||
|
||||
const gavl_time_t time = timelineWidget->x_to_time(x);
|
||||
if(time > pinnedDragTime)
|
||||
timelineWidget->set_playback_period(pinnedDragTime, time);
|
||||
else
|
||||
timelineWidget->set_playback_period(time, pinnedDragTime);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineRuler::draw_ruler(Cairo::RefPtr<Cairo::Context> cr,
|
||||
const Gdk::Rectangle ruler_rect)
|
||||
|
|
@ -299,6 +350,74 @@ TimelineRuler::draw_selection(Cairo::RefPtr<Cairo::Context> cr,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
TimelineRuler::draw_playback_period(Cairo::RefPtr<Cairo::Context> cr,
|
||||
const Gdk::Rectangle ruler_rect)
|
||||
{
|
||||
REQUIRE(cr);
|
||||
REQUIRE(ruler_rect.get_width() > 0);
|
||||
REQUIRE(ruler_rect.get_height() > 0);
|
||||
REQUIRE(timelineWidget != NULL);
|
||||
|
||||
// Calculate coordinates
|
||||
const float halfSize = playbackArrowSize / 2;
|
||||
|
||||
const float a = timelineWidget->time_to_x(
|
||||
timelineWidget->playbackPeriodStart) + 1 + 0.5f;
|
||||
const float b = a + halfSize;
|
||||
const float d = timelineWidget->time_to_x(
|
||||
timelineWidget->playbackPeriodEnd) + 0.5f;
|
||||
const float c = d - halfSize;
|
||||
|
||||
const float e = ruler_rect.get_height() - playbackArrowSize - 0.5f;
|
||||
const float f = e + (playbackArrowSize - playbackArrowStemSize) / 2;
|
||||
const float g = ruler_rect.get_height() - playbackArrowSize / 2
|
||||
- 0.5f;
|
||||
const float i = ruler_rect.get_height() - 0.5f;
|
||||
const float h = i - (playbackArrowSize - playbackArrowStemSize) / 2;
|
||||
|
||||
// Contruct the path
|
||||
if(d - a >= playbackArrowSize)
|
||||
{
|
||||
// Draw an arrow: <===>
|
||||
cr->move_to(a, g);
|
||||
cr->line_to(b, e);
|
||||
cr->line_to(b, f);
|
||||
cr->line_to(c, f);
|
||||
cr->line_to(c, e);
|
||||
cr->line_to(d, g);
|
||||
cr->line_to(c, i);
|
||||
cr->line_to(c, h);
|
||||
cr->line_to(b, h);
|
||||
cr->line_to(b, i);
|
||||
cr->line_to(a, g);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The space is too narrow for an arrow, so draw calipers: > <
|
||||
cr->move_to(a, g);
|
||||
cr->rel_line_to(-halfSize, -halfSize);
|
||||
cr->rel_line_to(0, playbackArrowSize);
|
||||
|
||||
cr->move_to(d, g);
|
||||
cr->rel_line_to(halfSize, -halfSize);
|
||||
cr->rel_line_to(0, playbackArrowSize);
|
||||
}
|
||||
|
||||
// Fill
|
||||
cr->set_source_rgba(
|
||||
(float)playbackArrowColour.red / 0xFFFF,
|
||||
(float)playbackArrowColour.green / 0xFFFF,
|
||||
(float)playbackArrowColour.blue / 0xFFFF,
|
||||
playbackArrowAlpha);
|
||||
cr->fill_preserve();
|
||||
|
||||
// Stroke
|
||||
gdk_cairo_set_source_color(cr->cobj(), &playbackArrowColour);
|
||||
cr->set_line_width(1);
|
||||
cr->stroke();
|
||||
}
|
||||
|
||||
gavl_time_t
|
||||
TimelineRuler::calculate_major_spacing() const
|
||||
{
|
||||
|
|
@ -402,6 +521,29 @@ TimelineRuler::register_styles() const
|
|||
"Selection Chevron Size",
|
||||
"The height of the selection chevrons in pixels.",
|
||||
0, G_MAXINT, 5, G_PARAM_READABLE));
|
||||
|
||||
gtk_widget_class_install_style_property(klass,
|
||||
g_param_spec_boxed("playback_arrow_colour",
|
||||
"End lines of a selection",
|
||||
"The colour of selection limit lines",
|
||||
GDK_TYPE_COLOR, G_PARAM_READABLE));
|
||||
|
||||
gtk_widget_class_install_style_property(klass,
|
||||
g_param_spec_float("playback_arrow_alpha", "Playback Arrow Alpha",
|
||||
"The transparency of the playback period arrow.",
|
||||
0, 1.0, 0.5, G_PARAM_READABLE));
|
||||
|
||||
gtk_widget_class_install_style_property(klass,
|
||||
g_param_spec_int("playback_arrow_size",
|
||||
"Playback Arrow Head Size",
|
||||
"The height of the playback arrow head in pixels.",
|
||||
0, G_MAXINT, 10, G_PARAM_READABLE));
|
||||
|
||||
gtk_widget_class_install_style_property(klass,
|
||||
g_param_spec_int("playback_arrow_stem_size",
|
||||
"Playback Arrow Stem Size",
|
||||
"The height of the playback arrow head in pixels.",
|
||||
0, G_MAXINT, 3, G_PARAM_READABLE));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -415,6 +557,13 @@ TimelineRuler::read_styles()
|
|||
get_style_property("min_division_width", minDivisionWidth);
|
||||
get_style_property("mouse_chevron_size", mouseChevronSize);
|
||||
get_style_property("selection_chevron_size", selectionChevronSize);
|
||||
|
||||
playbackArrowColour = WindowManager::read_style_colour_property(
|
||||
*this, "playback_arrow_colour", 0, 0, 0);
|
||||
get_style_property("playback_arrow_alpha", playbackArrowAlpha);
|
||||
get_style_property("playback_arrow_size", playbackArrowSize);
|
||||
get_style_property("playback_arrow_stem_size",
|
||||
playbackArrowStemSize);
|
||||
}
|
||||
|
||||
} // namespace timeline
|
||||
|
|
|
|||
|
|
@ -59,14 +59,34 @@ protected:
|
|||
|
||||
bool on_expose_event(GdkEventExpose *event);
|
||||
|
||||
/**
|
||||
* The event handler for button press events.
|
||||
*/
|
||||
bool on_button_press_event(GdkEventButton* event);
|
||||
|
||||
/**
|
||||
* The event handler for button release events.
|
||||
*/
|
||||
bool on_button_release_event(GdkEventButton* event);
|
||||
|
||||
/**
|
||||
* The event handler for mouse move events.
|
||||
*/
|
||||
bool on_motion_notify_event(GdkEventMotion *event);
|
||||
|
||||
void on_size_request(Gtk::Requisition *requisition);
|
||||
|
||||
void on_size_allocate(Gtk::Allocation& allocation);
|
||||
|
||||
/* ===== Internals ===== */
|
||||
private:
|
||||
/* ===== Internal Methods ===== */
|
||||
|
||||
/**
|
||||
* As the user drags, this function is called to update the position
|
||||
* of the moving end of the playback period.
|
||||
*/
|
||||
void set_leading_x(const int x);
|
||||
|
||||
void draw_ruler(Cairo::RefPtr<Cairo::Context> cairo,
|
||||
const Gdk::Rectangle ruler_rect);
|
||||
|
||||
|
|
@ -75,6 +95,9 @@ private:
|
|||
|
||||
void draw_selection(Cairo::RefPtr<Cairo::Context> cr,
|
||||
const Gdk::Rectangle ruler_rect);
|
||||
|
||||
void draw_playback_period(Cairo::RefPtr<Cairo::Context> cr,
|
||||
const Gdk::Rectangle ruler_rect);
|
||||
|
||||
gavl_time_t calculate_major_spacing() const;
|
||||
|
||||
|
|
@ -83,6 +106,16 @@ private:
|
|||
void read_styles();
|
||||
|
||||
private:
|
||||
|
||||
// State values
|
||||
bool isDragging;
|
||||
|
||||
/**
|
||||
* During a selection drag, one end of the selection is moving with
|
||||
* the mouse, the other is pinned. pinnedDragTime specifies the time
|
||||
* of that point.
|
||||
*/
|
||||
gavl_time_t pinnedDragTime;
|
||||
|
||||
// Indicated values
|
||||
int mouseChevronOffset;
|
||||
|
|
@ -96,7 +129,11 @@ private:
|
|||
int minDivisionWidth;
|
||||
int mouseChevronSize;
|
||||
int selectionChevronSize;
|
||||
|
||||
GdkColor playbackArrowColour;
|
||||
float playbackArrowAlpha;
|
||||
int playbackArrowSize;
|
||||
int playbackArrowStemSize;
|
||||
|
||||
// Owner
|
||||
lumiera::gui::widgets::TimelineWidget *timelineWidget;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue