Added a mouse chevron to the ruler
This commit is contained in:
parent
7752130d99
commit
8d63d7adb4
7 changed files with 184 additions and 36 deletions
|
|
@ -136,6 +136,7 @@ style "timeline_ruler" = "default_base"
|
|||
gtkmm__CustomObject_TimelineRuler::annotation_horz_margin = 3
|
||||
gtkmm__CustomObject_TimelineRuler::annotation_vert_margin = 0
|
||||
gtkmm__CustomObject_TimelineRuler::min_division_width = 100
|
||||
gtkmm__CustomObject_TimelineRuler::mouse_chevron_size = 5
|
||||
}
|
||||
|
||||
style "timeline_header_base" = "default_base"
|
||||
|
|
|
|||
|
|
@ -207,6 +207,12 @@ TimelineWidget::zoom_view(int point, int zoom_size)
|
|||
set_time_scale(new_time_scale);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineWidget::on_mouse_move_in_body(int x, int y)
|
||||
{
|
||||
ruler.set_mouse_chevron_time(x * timeScale + timeOffset);
|
||||
}
|
||||
|
||||
} // namespace widgets
|
||||
} // namespace gui
|
||||
} // namespace lumiera
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ protected:
|
|||
void shift_view(int shift_size);
|
||||
|
||||
void zoom_view(int point, int zoom_size);
|
||||
|
||||
void on_mouse_move_in_body(int x, int y);
|
||||
|
||||
protected:
|
||||
gavl_time_t timeOffset;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,15 @@ TimelineBody::TimelineBody(lumiera::gui::widgets::TimelineWidget *timeline_widge
|
|||
|
||||
}
|
||||
|
||||
void
|
||||
TimelineBody::on_realize()
|
||||
{
|
||||
Widget::on_realize();
|
||||
|
||||
// We wish to receive all event notifications
|
||||
add_events(Gdk::POINTER_MOTION_MASK);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineBody::on_scroll()
|
||||
{
|
||||
|
|
@ -69,6 +78,7 @@ TimelineBody::on_scroll()
|
|||
bool
|
||||
TimelineBody::on_scroll_event (GdkEventScroll* event)
|
||||
{
|
||||
REQUIRE(event != NULL);
|
||||
REQUIRE(timelineWidget != NULL);
|
||||
|
||||
if(event->state & GDK_CONTROL_MASK)
|
||||
|
|
@ -102,19 +112,24 @@ TimelineBody::on_scroll_event (GdkEventScroll* event)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TimelineBody::on_realize()
|
||||
|
||||
bool
|
||||
TimelineBody::on_motion_notify_event(GdkEventMotion *event)
|
||||
{
|
||||
Widget::on_realize();
|
||||
REQUIRE(event != NULL);
|
||||
REQUIRE(timelineWidget != NULL);
|
||||
|
||||
// We wish to receive all event notifications
|
||||
add_events(Gdk::ALL_EVENTS_MASK);
|
||||
timelineWidget->on_mouse_move_in_body(event->x, event->y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TimelineBody::on_expose_event(GdkEventExpose* event)
|
||||
{
|
||||
REQUIRE(event != NULL);
|
||||
REQUIRE(timelineWidget != NULL);
|
||||
|
||||
// This is where we draw on the window
|
||||
Glib::RefPtr<Gdk::Window> window = get_window();
|
||||
if(!window)
|
||||
|
|
@ -124,10 +139,13 @@ TimelineBody::on_expose_event(GdkEventExpose* event)
|
|||
read_styles();
|
||||
|
||||
// Prepare to render via cairo
|
||||
Glib::RefPtr<Style> style = get_style();
|
||||
Glib::RefPtr<Style> style = get_style();
|
||||
Gtk::Allocation allocation = get_allocation();
|
||||
Cairo::RefPtr<Cairo::Context> cairo = window->create_cairo_context();
|
||||
|
||||
|
||||
REQUIRE(style);
|
||||
REQUIRE(cairo);
|
||||
|
||||
// Translate the view by the scroll distance
|
||||
cairo->translate(0,
|
||||
-(int)timelineWidget->verticalAdjustment.get_value());
|
||||
|
|
|
|||
|
|
@ -43,12 +43,14 @@ class TimelineBody : public Gtk::DrawingArea
|
|||
|
||||
/* ===== Events ===== */
|
||||
protected:
|
||||
void on_realize();
|
||||
|
||||
void on_scroll();
|
||||
|
||||
bool on_scroll_event (GdkEventScroll* event);
|
||||
bool on_scroll_event(GdkEventScroll* event);
|
||||
|
||||
bool on_motion_notify_event(GdkEventMotion *event);
|
||||
|
||||
void on_realize();
|
||||
|
||||
bool on_expose_event(GdkEventExpose* event);
|
||||
|
||||
/* ===== Internals ===== */
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ extern "C" {
|
|||
}
|
||||
|
||||
using namespace Gtk;
|
||||
using namespace Cairo;
|
||||
using namespace std;
|
||||
using namespace lumiera::gui;
|
||||
using namespace lumiera::gui::widgets;
|
||||
|
|
@ -44,15 +45,15 @@ TimelineRuler::TimelineRuler() :
|
|||
Glib::ObjectBase("TimelineRuler"),
|
||||
timeOffset(0),
|
||||
timeScale(1),
|
||||
mouseChevronTime(0),
|
||||
annotationHorzMargin(0),
|
||||
annotationVertMargin(0),
|
||||
majorTickHeight(0),
|
||||
minorLongTickHeight(0),
|
||||
minorShortTickHeight(0),
|
||||
minDivisionWidth(100)
|
||||
{
|
||||
set_flags(Gtk::NO_WINDOW); // This widget will not have a window
|
||||
|
||||
minDivisionWidth(100),
|
||||
mouseChevronSize(0)
|
||||
{
|
||||
// Install style properties
|
||||
register_styles();
|
||||
}
|
||||
|
|
@ -61,6 +62,7 @@ void
|
|||
TimelineRuler::set_time_offset(gavl_time_t time_offset)
|
||||
{
|
||||
timeOffset = time_offset;
|
||||
rulerImage.clear();
|
||||
queue_draw();
|
||||
}
|
||||
|
||||
|
|
@ -69,6 +71,14 @@ TimelineRuler::set_time_scale(int64_t time_scale)
|
|||
{
|
||||
REQUIRE(time_scale > 0);
|
||||
timeScale = time_scale;
|
||||
rulerImage.clear();
|
||||
queue_draw();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineRuler::set_mouse_chevron_time(gavl_time_t time)
|
||||
{
|
||||
mouseChevronTime = time;
|
||||
queue_draw();
|
||||
}
|
||||
|
||||
|
|
@ -76,6 +86,9 @@ void
|
|||
TimelineRuler::on_realize()
|
||||
{
|
||||
Widget::on_realize();
|
||||
|
||||
// Set event notifications
|
||||
add_events(Gdk::POINTER_MOTION_MASK);
|
||||
|
||||
// Load styles
|
||||
read_styles();
|
||||
|
|
@ -84,30 +97,99 @@ TimelineRuler::on_realize()
|
|||
bool
|
||||
TimelineRuler::on_expose_event(GdkEventExpose* event)
|
||||
{
|
||||
REQUIRE(event != NULL);
|
||||
|
||||
// This is where we draw on the window
|
||||
Glib::RefPtr<Gdk::Window> window = get_window();
|
||||
if(!window)
|
||||
return false;
|
||||
|
||||
// Prepare to render via cairo
|
||||
Allocation allocation = get_allocation();
|
||||
const int height = allocation.get_height();
|
||||
Glib::RefPtr<Style> style = get_style();
|
||||
Cairo::RefPtr<Cairo::Context> cairo = window->create_cairo_context();
|
||||
const Allocation allocation = get_allocation();
|
||||
|
||||
Cairo::RefPtr<Context> cairo = window->create_cairo_context();
|
||||
REQUIRE(cairo);
|
||||
|
||||
// Draw the ruler
|
||||
if(!rulerImage)
|
||||
{
|
||||
// We have no cached rendering - it must be redrawn
|
||||
// but do we need ro allocate a new image?
|
||||
if(!rulerImage ||
|
||||
rulerImage->get_width() != allocation.get_width() ||
|
||||
rulerImage->get_height() != allocation.get_height())
|
||||
rulerImage = ImageSurface::create(FORMAT_RGB24,
|
||||
allocation.get_width(), allocation.get_height());
|
||||
|
||||
ENSURE(rulerImage);
|
||||
|
||||
Cairo::RefPtr<Context> image_cairo = Context::create(rulerImage);
|
||||
ENSURE(image_cairo);
|
||||
|
||||
draw_ruler(image_cairo, allocation);
|
||||
}
|
||||
|
||||
// Draw the cached ruler image
|
||||
cairo->set_source(rulerImage, 0, 0);
|
||||
cairo->paint();
|
||||
|
||||
// Draw the mouse chevron
|
||||
draw_mouse_chevron(cairo, allocation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TimelineRuler::on_motion_notify_event(GdkEventMotion *event)
|
||||
{
|
||||
REQUIRE(event != NULL);
|
||||
|
||||
set_mouse_chevron_time(event->x * timeScale + timeOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TimelineRuler::on_size_request (Gtk::Requisition *requisition)
|
||||
{
|
||||
REQUIRE(requisition != NULL);
|
||||
|
||||
// Initialize the output parameter
|
||||
*requisition = Gtk::Requisition();
|
||||
|
||||
requisition->width = 0;
|
||||
get_style_property("height", requisition->height);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineRuler::on_size_allocate(Gtk::Allocation& allocation)
|
||||
{
|
||||
Widget::on_size_allocate(allocation);
|
||||
rulerImage.clear(); // The widget has changed size - redraw
|
||||
}
|
||||
|
||||
void
|
||||
TimelineRuler::draw_ruler(Cairo::RefPtr<Cairo::Context> cairo,
|
||||
Gdk::Rectangle ruler_rect)
|
||||
{
|
||||
REQUIRE(cairo);
|
||||
REQUIRE(ruler_rect.get_width() > 0);
|
||||
REQUIRE(ruler_rect.get_height() > 0);
|
||||
|
||||
// Preparation steps
|
||||
const int height = ruler_rect.get_height();
|
||||
Glib::RefPtr<Pango::Layout> pango_layout = create_pango_layout("");
|
||||
|
||||
cairo->translate(allocation.get_x(), allocation.get_y());
|
||||
|
||||
Glib::RefPtr<Style> style = get_style();
|
||||
|
||||
// Render the background, and clip inside the area
|
||||
Gdk::Cairo::set_source_color(cairo, style->get_bg(STATE_NORMAL));
|
||||
cairo->rectangle(0, 0,
|
||||
allocation.get_width(), allocation.get_height());
|
||||
ruler_rect.get_width(), ruler_rect.get_height());
|
||||
cairo->fill_preserve();
|
||||
cairo->clip();
|
||||
|
||||
// Make sure we don't have impossible zoom
|
||||
if(timeScale <= 0)
|
||||
return true;
|
||||
return;
|
||||
|
||||
// Render ruler annotations
|
||||
Gdk::Cairo::set_source_color(cairo, style->get_fg(STATE_NORMAL));
|
||||
|
|
@ -115,8 +197,10 @@ TimelineRuler::on_expose_event(GdkEventExpose* event)
|
|||
const gavl_time_t major_spacing = calculate_major_spacing();
|
||||
const gavl_time_t minor_spacing = major_spacing / 10;
|
||||
|
||||
|
||||
int64_t time_offset = timeOffset - timeOffset % minor_spacing;
|
||||
int64_t time_offset = timeOffset - timeOffset % major_spacing;
|
||||
if(timeOffset < 0)
|
||||
time_offset -= major_spacing;
|
||||
|
||||
int x = 0;
|
||||
const int64_t x_offset = timeOffset / timeScale;
|
||||
|
||||
|
|
@ -153,19 +237,30 @@ TimelineRuler::on_expose_event(GdkEventExpose* event)
|
|||
|
||||
time_offset += minor_spacing;
|
||||
}
|
||||
while(x < allocation.get_width());
|
||||
|
||||
return true;
|
||||
while(x < ruler_rect.get_width());
|
||||
}
|
||||
|
||||
void
|
||||
TimelineRuler::on_size_request (Gtk::Requisition *requisition)
|
||||
TimelineRuler::draw_mouse_chevron(Cairo::RefPtr<Cairo::Context> cairo,
|
||||
Gdk::Rectangle ruler_rect)
|
||||
{
|
||||
// Initialize the output parameter
|
||||
*requisition = Gtk::Requisition();
|
||||
REQUIRE(cairo);
|
||||
REQUIRE(ruler_rect.get_width() > 0);
|
||||
REQUIRE(ruler_rect.get_height() > 0);
|
||||
|
||||
requisition->width = 0;
|
||||
get_style_property("height", requisition->height);
|
||||
// Set the source colour
|
||||
Glib::RefPtr<Style> style = get_style();
|
||||
Gdk::Cairo::set_source_color(cairo, style->get_fg(STATE_NORMAL));
|
||||
|
||||
const int x = (mouseChevronTime - timeOffset) / timeScale;
|
||||
cairo->move_to(x + 0.5,
|
||||
ruler_rect.get_height());
|
||||
cairo->line_to(x + mouseChevronSize + 0.5,
|
||||
ruler_rect.get_height() - mouseChevronSize);
|
||||
cairo->line_to(x - mouseChevronSize + 0.5,
|
||||
ruler_rect.get_height() - mouseChevronSize);
|
||||
|
||||
cairo->fill();
|
||||
}
|
||||
|
||||
gavl_time_t
|
||||
|
|
@ -256,6 +351,12 @@ TimelineRuler::register_styles() const
|
|||
"Minimum Division Width",
|
||||
"The minimum distance in pixels that two major division may approach.",
|
||||
0, G_MAXINT, 100, G_PARAM_READABLE));
|
||||
|
||||
gtk_widget_class_install_style_property(klass,
|
||||
g_param_spec_int("mouse_chevron_size",
|
||||
"Mouse Chevron Size",
|
||||
"The height of the mouse chevron in pixels.",
|
||||
0, G_MAXINT, 5, G_PARAM_READABLE));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -267,6 +368,7 @@ TimelineRuler::read_styles()
|
|||
get_style_property("minor_long_tick_height", minorLongTickHeight);
|
||||
get_style_property("minor_short_tick_height", minorShortTickHeight);
|
||||
get_style_property("min_division_width", minDivisionWidth);
|
||||
get_style_property("mouse_chevron_size", mouseChevronSize);
|
||||
}
|
||||
|
||||
} // namespace timeline
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ namespace gui {
|
|||
namespace widgets {
|
||||
namespace timeline {
|
||||
|
||||
class TimelineRuler : public Gtk::Widget
|
||||
class TimelineRuler : public Gtk::DrawingArea
|
||||
{
|
||||
public:
|
||||
TimelineRuler();
|
||||
|
|
@ -52,6 +52,8 @@ public:
|
|||
* zero.
|
||||
*/
|
||||
void set_time_scale(int64_t time_scale);
|
||||
|
||||
void set_mouse_chevron_time(gavl_time_t time);
|
||||
|
||||
/* ===== Events ===== */
|
||||
protected:
|
||||
|
|
@ -60,10 +62,20 @@ protected:
|
|||
|
||||
bool on_expose_event(GdkEventExpose *event);
|
||||
|
||||
void on_size_request (Gtk::Requisition *requisition);
|
||||
bool on_motion_notify_event(GdkEventMotion *event);
|
||||
|
||||
void on_size_request(Gtk::Requisition *requisition);
|
||||
|
||||
void on_size_allocate(Gtk::Allocation& allocation);
|
||||
|
||||
/* ===== Internals ===== */
|
||||
private:
|
||||
void draw_ruler(Cairo::RefPtr<Cairo::Context> cairo,
|
||||
Gdk::Rectangle ruler_rect);
|
||||
|
||||
void draw_mouse_chevron(Cairo::RefPtr<Cairo::Context> cairo,
|
||||
Gdk::Rectangle ruler_rect);
|
||||
|
||||
gavl_time_t calculate_major_spacing() const;
|
||||
|
||||
void register_styles() const;
|
||||
|
|
@ -74,6 +86,7 @@ private:
|
|||
// View values
|
||||
gavl_time_t timeOffset;
|
||||
int64_t timeScale;
|
||||
int mouseChevronTime;
|
||||
|
||||
// Style values
|
||||
int annotationHorzMargin;
|
||||
|
|
@ -82,6 +95,10 @@ private:
|
|||
int minorLongTickHeight;
|
||||
int minorShortTickHeight;
|
||||
int minDivisionWidth;
|
||||
int mouseChevronSize;
|
||||
|
||||
// Cached ruler image
|
||||
Cairo::RefPtr<Cairo::ImageSurface> rulerImage;
|
||||
};
|
||||
|
||||
} // namespace timeline
|
||||
|
|
|
|||
Loading…
Reference in a new issue