Added a mouse chevron to the ruler

This commit is contained in:
Joel Holdsworth 2008-06-25 20:23:53 +01:00
parent 7752130d99
commit 8d63d7adb4
7 changed files with 184 additions and 36 deletions

View file

@ -136,6 +136,7 @@ style "timeline_ruler" = "default_base"
gtkmm__CustomObject_TimelineRuler::annotation_horz_margin = 3 gtkmm__CustomObject_TimelineRuler::annotation_horz_margin = 3
gtkmm__CustomObject_TimelineRuler::annotation_vert_margin = 0 gtkmm__CustomObject_TimelineRuler::annotation_vert_margin = 0
gtkmm__CustomObject_TimelineRuler::min_division_width = 100 gtkmm__CustomObject_TimelineRuler::min_division_width = 100
gtkmm__CustomObject_TimelineRuler::mouse_chevron_size = 5
} }
style "timeline_header_base" = "default_base" style "timeline_header_base" = "default_base"

View file

@ -207,6 +207,12 @@ TimelineWidget::zoom_view(int point, int zoom_size)
set_time_scale(new_time_scale); 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 widgets
} // namespace gui } // namespace gui
} // namespace lumiera } // namespace lumiera

View file

@ -97,6 +97,8 @@ protected:
void zoom_view(int point, int zoom_size); void zoom_view(int point, int zoom_size);
void on_mouse_move_in_body(int x, int y);
protected: protected:
gavl_time_t timeOffset; gavl_time_t timeOffset;
int64_t timeScale; int64_t timeScale;

View file

@ -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 void
TimelineBody::on_scroll() TimelineBody::on_scroll()
{ {
@ -69,6 +78,7 @@ TimelineBody::on_scroll()
bool bool
TimelineBody::on_scroll_event (GdkEventScroll* event) TimelineBody::on_scroll_event (GdkEventScroll* event)
{ {
REQUIRE(event != NULL);
REQUIRE(timelineWidget != NULL); REQUIRE(timelineWidget != NULL);
if(event->state & GDK_CONTROL_MASK) if(event->state & GDK_CONTROL_MASK)
@ -103,18 +113,23 @@ TimelineBody::on_scroll_event (GdkEventScroll* event)
} }
} }
void bool
TimelineBody::on_realize() TimelineBody::on_motion_notify_event(GdkEventMotion *event)
{ {
Widget::on_realize(); REQUIRE(event != NULL);
REQUIRE(timelineWidget != NULL);
// We wish to receive all event notifications timelineWidget->on_mouse_move_in_body(event->x, event->y);
add_events(Gdk::ALL_EVENTS_MASK);
return true;
} }
bool bool
TimelineBody::on_expose_event(GdkEventExpose* event) TimelineBody::on_expose_event(GdkEventExpose* event)
{ {
REQUIRE(event != NULL);
REQUIRE(timelineWidget != NULL);
// This is where we draw on the window // This is where we draw on the window
Glib::RefPtr<Gdk::Window> window = get_window(); Glib::RefPtr<Gdk::Window> window = get_window();
if(!window) if(!window)
@ -128,6 +143,9 @@ TimelineBody::on_expose_event(GdkEventExpose* event)
Gtk::Allocation allocation = get_allocation(); Gtk::Allocation allocation = get_allocation();
Cairo::RefPtr<Cairo::Context> cairo = window->create_cairo_context(); Cairo::RefPtr<Cairo::Context> cairo = window->create_cairo_context();
REQUIRE(style);
REQUIRE(cairo);
// Translate the view by the scroll distance // Translate the view by the scroll distance
cairo->translate(0, cairo->translate(0,
-(int)timelineWidget->verticalAdjustment.get_value()); -(int)timelineWidget->verticalAdjustment.get_value());

View file

@ -43,11 +43,13 @@ class TimelineBody : public Gtk::DrawingArea
/* ===== Events ===== */ /* ===== Events ===== */
protected: protected:
void on_realize();
void on_scroll(); void on_scroll();
bool on_scroll_event (GdkEventScroll* event); bool on_scroll_event(GdkEventScroll* event);
void on_realize(); bool on_motion_notify_event(GdkEventMotion *event);
bool on_expose_event(GdkEventExpose* event); bool on_expose_event(GdkEventExpose* event);

View file

@ -30,6 +30,7 @@ extern "C" {
} }
using namespace Gtk; using namespace Gtk;
using namespace Cairo;
using namespace std; using namespace std;
using namespace lumiera::gui; using namespace lumiera::gui;
using namespace lumiera::gui::widgets; using namespace lumiera::gui::widgets;
@ -44,15 +45,15 @@ TimelineRuler::TimelineRuler() :
Glib::ObjectBase("TimelineRuler"), Glib::ObjectBase("TimelineRuler"),
timeOffset(0), timeOffset(0),
timeScale(1), timeScale(1),
mouseChevronTime(0),
annotationHorzMargin(0), annotationHorzMargin(0),
annotationVertMargin(0), annotationVertMargin(0),
majorTickHeight(0), majorTickHeight(0),
minorLongTickHeight(0), minorLongTickHeight(0),
minorShortTickHeight(0), minorShortTickHeight(0),
minDivisionWidth(100) minDivisionWidth(100),
mouseChevronSize(0)
{ {
set_flags(Gtk::NO_WINDOW); // This widget will not have a window
// Install style properties // Install style properties
register_styles(); register_styles();
} }
@ -61,6 +62,7 @@ void
TimelineRuler::set_time_offset(gavl_time_t time_offset) TimelineRuler::set_time_offset(gavl_time_t time_offset)
{ {
timeOffset = time_offset; timeOffset = time_offset;
rulerImage.clear();
queue_draw(); queue_draw();
} }
@ -69,6 +71,14 @@ TimelineRuler::set_time_scale(int64_t time_scale)
{ {
REQUIRE(time_scale > 0); REQUIRE(time_scale > 0);
timeScale = time_scale; timeScale = time_scale;
rulerImage.clear();
queue_draw();
}
void
TimelineRuler::set_mouse_chevron_time(gavl_time_t time)
{
mouseChevronTime = time;
queue_draw(); queue_draw();
} }
@ -77,6 +87,9 @@ TimelineRuler::on_realize()
{ {
Widget::on_realize(); Widget::on_realize();
// Set event notifications
add_events(Gdk::POINTER_MOTION_MASK);
// Load styles // Load styles
read_styles(); read_styles();
} }
@ -84,30 +97,99 @@ TimelineRuler::on_realize()
bool bool
TimelineRuler::on_expose_event(GdkEventExpose* event) TimelineRuler::on_expose_event(GdkEventExpose* event)
{ {
REQUIRE(event != NULL);
// This is where we draw on the window // This is where we draw on the window
Glib::RefPtr<Gdk::Window> window = get_window(); Glib::RefPtr<Gdk::Window> window = get_window();
if(!window) if(!window)
return false; return false;
// Prepare to render via cairo // Prepare to render via cairo
Allocation allocation = get_allocation(); const 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();
Glib::RefPtr<Pango::Layout> pango_layout = create_pango_layout("");
cairo->translate(allocation.get_x(), allocation.get_y()); 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("");
Glib::RefPtr<Style> style = get_style();
// Render the background, and clip inside the area // Render the background, and clip inside the area
Gdk::Cairo::set_source_color(cairo, style->get_bg(STATE_NORMAL)); Gdk::Cairo::set_source_color(cairo, style->get_bg(STATE_NORMAL));
cairo->rectangle(0, 0, cairo->rectangle(0, 0,
allocation.get_width(), allocation.get_height()); ruler_rect.get_width(), ruler_rect.get_height());
cairo->fill_preserve(); cairo->fill_preserve();
cairo->clip(); cairo->clip();
// Make sure we don't have impossible zoom // Make sure we don't have impossible zoom
if(timeScale <= 0) if(timeScale <= 0)
return true; return;
// Render ruler annotations // Render ruler annotations
Gdk::Cairo::set_source_color(cairo, style->get_fg(STATE_NORMAL)); 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 major_spacing = calculate_major_spacing();
const gavl_time_t minor_spacing = major_spacing / 10; const gavl_time_t minor_spacing = major_spacing / 10;
int64_t time_offset = timeOffset - timeOffset % major_spacing;
if(timeOffset < 0)
time_offset -= major_spacing;
int64_t time_offset = timeOffset - timeOffset % minor_spacing;
int x = 0; int x = 0;
const int64_t x_offset = timeOffset / timeScale; const int64_t x_offset = timeOffset / timeScale;
@ -153,19 +237,30 @@ TimelineRuler::on_expose_event(GdkEventExpose* event)
time_offset += minor_spacing; time_offset += minor_spacing;
} }
while(x < allocation.get_width()); while(x < ruler_rect.get_width());
return true;
} }
void 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 REQUIRE(cairo);
*requisition = Gtk::Requisition(); REQUIRE(ruler_rect.get_width() > 0);
REQUIRE(ruler_rect.get_height() > 0);
requisition->width = 0; // Set the source colour
get_style_property("height", requisition->height); 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 gavl_time_t
@ -256,6 +351,12 @@ TimelineRuler::register_styles() const
"Minimum Division Width", "Minimum Division Width",
"The minimum distance in pixels that two major division may approach.", "The minimum distance in pixels that two major division may approach.",
0, G_MAXINT, 100, G_PARAM_READABLE)); 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 void
@ -267,6 +368,7 @@ TimelineRuler::read_styles()
get_style_property("minor_long_tick_height", minorLongTickHeight); get_style_property("minor_long_tick_height", minorLongTickHeight);
get_style_property("minor_short_tick_height", minorShortTickHeight); get_style_property("minor_short_tick_height", minorShortTickHeight);
get_style_property("min_division_width", minDivisionWidth); get_style_property("min_division_width", minDivisionWidth);
get_style_property("mouse_chevron_size", mouseChevronSize);
} }
} // namespace timeline } // namespace timeline

View file

@ -34,7 +34,7 @@ namespace gui {
namespace widgets { namespace widgets {
namespace timeline { namespace timeline {
class TimelineRuler : public Gtk::Widget class TimelineRuler : public Gtk::DrawingArea
{ {
public: public:
TimelineRuler(); TimelineRuler();
@ -53,6 +53,8 @@ public:
*/ */
void set_time_scale(int64_t time_scale); void set_time_scale(int64_t time_scale);
void set_mouse_chevron_time(gavl_time_t time);
/* ===== Events ===== */ /* ===== Events ===== */
protected: protected:
@ -60,10 +62,20 @@ protected:
bool on_expose_event(GdkEventExpose *event); 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 ===== */ /* ===== Internals ===== */
private: 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; gavl_time_t calculate_major_spacing() const;
void register_styles() const; void register_styles() const;
@ -74,6 +86,7 @@ private:
// View values // View values
gavl_time_t timeOffset; gavl_time_t timeOffset;
int64_t timeScale; int64_t timeScale;
int mouseChevronTime;
// Style values // Style values
int annotationHorzMargin; int annotationHorzMargin;
@ -82,6 +95,10 @@ private:
int minorLongTickHeight; int minorLongTickHeight;
int minorShortTickHeight; int minorShortTickHeight;
int minDivisionWidth; int minDivisionWidth;
int mouseChevronSize;
// Cached ruler image
Cairo::RefPtr<Cairo::ImageSurface> rulerImage;
}; };
} // namespace timeline } // namespace timeline