From 57aed7b40d0b922a2e32dbc5c84b585dcb628acf Mon Sep 17 00:00:00 2001 From: Joel Holdsworth Date: Sat, 16 Aug 2008 16:02:12 +0100 Subject: [PATCH] Added resizing of selection area, and some documentation --- src/gui/widgets/timeline-widget.hpp | 15 +++ .../widgets/timeline/timeline-arrow-tool.hpp | 11 ++ .../widgets/timeline/timeline-ibeam-tool.cpp | 123 +++++++++++++++--- .../widgets/timeline/timeline-ibeam-tool.hpp | 103 ++++++++++++++- src/gui/widgets/timeline/timeline-tool.cpp | 10 +- src/gui/widgets/timeline/timeline-tool.hpp | 29 ++++- 6 files changed, 264 insertions(+), 27 deletions(-) diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index cf1c260ea..532354793 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -98,14 +98,29 @@ public: **/ void shift_view(int shift_size); + /** + * Gets the time at which the selection begins. + */ gavl_time_t get_selection_start() const; + /** + * Gets the time at which the selection begins. + */ gavl_time_t get_selection_end() const; + /** + * Sets the period of the selection. + */ void set_selection(gavl_time_t start, gavl_time_t end); + /** + * Gets the type of the tool currently active. + */ timeline::ToolType get_tool() const; + /** + * Sets the type of the tool currently active. + */ void set_tool(timeline::ToolType tool_type); /* ===== Events ===== */ diff --git a/src/gui/widgets/timeline/timeline-arrow-tool.hpp b/src/gui/widgets/timeline/timeline-arrow-tool.hpp index 25b666339..49fa51064 100644 --- a/src/gui/widgets/timeline/timeline-arrow-tool.hpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.hpp @@ -56,8 +56,19 @@ protected: Gdk::Cursor get_cursor() const; protected: + /** + * The event handler for button press events. + */ void on_button_press_event(GdkEventButton* event); + + /** + * The event handler for button release events. + */ void on_button_release_event(GdkEventButton* event); + + /** + * The event handler for mouse move events. + */ void on_motion_notify_event(GdkEventMotion *event); }; diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp index 0e6e39597..92a4376f8 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -23,6 +23,8 @@ #include "timeline-ibeam-tool.hpp" #include "../timeline-widget.hpp" +using namespace lumiera::gui::widgets; + namespace lumiera { namespace gui { namespace widgets { @@ -30,13 +32,15 @@ namespace timeline { // ===== Constants ===== // +const int IBeamTool::DragZoneWidth = 5; const int IBeamTool::ScrollSlideRateDivisor = 16; const int IBeamTool::ScrollSlideEventInterval = 40; // ===== Implementation ===== // IBeamTool::IBeamTool(TimelineBody *timeline_body) : - dragStartTime(0), + dragType(None), + pinnedDragTime(0), scrollSlideRate(0), Tool(timeline_body) { @@ -57,6 +61,26 @@ IBeamTool::get_type() const Gdk::Cursor IBeamTool::get_cursor() const { + // Are we dragging? + // Make the cursor indicate that type of drag + switch(dragType) + { + case Selection: + return Gdk::Cursor(Gdk::XTERM); + case GrabStart: + return Gdk::Cursor(Gdk::LEFT_SIDE); + case GrabEnd: + return Gdk::Cursor(Gdk::RIGHT_SIDE); + } + + // Are we hovering over the ends of the selection? + // Make the cursor indicate that the user can resize the selection. + if(is_mouse_in_start_drag_zone()) + return Gdk::Cursor(Gdk::LEFT_SIDE); + if(is_mouse_in_end_drag_zone()) + return Gdk::Cursor(Gdk::RIGHT_SIDE); + + // By default return an I-beam cursor return Gdk::Cursor(Gdk::XTERM); } @@ -65,29 +89,66 @@ IBeamTool::on_button_press_event(GdkEventButton* event) { Tool::on_button_press_event(event); - lumiera::gui::widgets::TimelineWidget *timeline_widget = - get_timeline_widget(); + TimelineWidget *timeline_widget = get_timeline_widget(); if(event->button == 1) { - dragStartTime = timeline_widget->x_to_time(event->x); - timeline_widget->set_selection(dragStartTime, dragStartTime); + const gavl_time_t time = timeline_widget->x_to_time(event->x); + + if(is_mouse_in_start_drag_zone()) + { + // User began to drag the start of the selection + dragType = GrabStart; + pinnedDragTime = timeline_widget->get_selection_end(); + } + else if(is_mouse_in_end_drag_zone()) + { + // User began to drag the end of the selection + dragType = GrabEnd; + pinnedDragTime = timeline_widget->get_selection_start(); + } + else + { + // User began the drag in clear space, begin a Select drag + dragType = Selection; + pinnedDragTime = time; + timeline_widget->set_selection(time, time); + } } } void IBeamTool::on_button_release_event(GdkEventButton* event) -{ - Tool::on_button_release_event(event); +{ + // Ensure that we can't get a mixed up state + ENSURE(isDragging == (dragType != None)); + ENSURE(isDragging == (event->button == 1)); if(event->button == 1) - set_leading_x(event->x); + { + set_leading_x(event->x); + + // Terminate the drag now the button is released + dragType = None; + + // If there was a scroll slide, terminate it + end_scroll_slide(); + + // Apply the cursor - there are some corner cases where it can + // change by the end of the drag + apply_cursor(); + } + + Tool::on_button_release_event(event); } void IBeamTool::on_motion_notify_event(GdkEventMotion *event) { Tool::on_motion_notify_event(event); + + // Ensure that we can't get a mixed up state + ENSURE(isDragging == (dragType != None)); if(isDragging) { @@ -103,15 +164,14 @@ IBeamTool::on_motion_notify_event(GdkEventMotion *event) (event->x - body_rect.get_width()) / ScrollSlideRateDivisor); else end_scroll_slide(); } + + apply_cursor(); } bool IBeamTool::on_scroll_slide_timer() -{ - lumiera::gui::widgets::TimelineWidget *timeline_widget = - get_timeline_widget(); - - timeline_widget->shift_view(scrollSlideRate); +{ + get_timeline_widget()->shift_view(scrollSlideRate); // Return true to keep the timer going return true; @@ -120,16 +180,13 @@ IBeamTool::on_scroll_slide_timer() void IBeamTool::set_leading_x(const int x) { - REQUIRE(timelineBody != NULL); - lumiera::gui::widgets::TimelineWidget *timeline_widget = - timelineBody->timelineWidget; - REQUIRE(timeline_widget != NULL); - + TimelineWidget *timeline_widget = get_timeline_widget(); + const gavl_time_t time = timeline_widget->x_to_time(x); - if(time > dragStartTime) - timeline_widget->set_selection(dragStartTime, time); + if(time > pinnedDragTime) + timeline_widget->set_selection(pinnedDragTime, time); else - timeline_widget->set_selection(time, dragStartTime); + timeline_widget->set_selection(time, pinnedDragTime); } void @@ -150,6 +207,30 @@ IBeamTool::end_scroll_slide() scrollSlideEvent.disconnect(); } +bool +IBeamTool::is_mouse_in_start_drag_zone() const +{ + TimelineWidget *timeline_widget = get_timeline_widget(); + + const int start_x = timeline_widget->time_to_x( + timeline_widget->get_selection_start()); + + return (mousePoint.get_x() <= start_x && + mousePoint.get_x() > start_x - DragZoneWidth); +} + +bool +IBeamTool::is_mouse_in_end_drag_zone() const +{ + TimelineWidget *timeline_widget = get_timeline_widget(); + + const int end_x = timeline_widget->time_to_x( + timeline_widget->get_selection_end()); + + return (mousePoint.get_x() >= end_x && + mousePoint.get_x() < end_x + DragZoneWidth); +} + } // namespace timeline } // namespace widgets } // namespace gui diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp index a9438ac87..fab09c251 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp @@ -47,22 +47,37 @@ public: */ IBeamTool(TimelineBody *timeline_body); - ~IBeamTool(); - /** * Gets the type of tool represented by this class */ ToolType get_type() const; protected: + + /** + * Destructor + */ + ~IBeamTool(); + /** * Gets the cursor to display for this tool at this moment. */ Gdk::Cursor get_cursor() const; protected: + /** + * The event handler for button press events. + */ void on_button_press_event(GdkEventButton* event); + + /** + * The event handler for button release events. + */ void on_button_release_event(GdkEventButton* event); + + /** + * The event handler for mouse move events. + */ void on_motion_notify_event(GdkEventMotion *event); private: @@ -95,14 +110,96 @@ private: */ void end_scroll_slide(); + /** + * Determines if the cursor is hovering over the start of the + * selection. + */ + bool is_mouse_in_start_drag_zone() const; + + /** + * Determines if the cursor is hovering over the end of the + * selection. + */ + bool is_mouse_in_end_drag_zone() const; + private: + /* ==== Enums ===== */ + + /** + * An enum used to represent the type of drag currently take place. + */ + enum DragType + { + /** + * No drag is occuring + */ + None, + + /** + * A selection drag is occuring. + * @remarks The position of one end of the selection was set at + * mouse-down of the drag, and the other end is set by + * drag-release. + */ + Selection, + + /** + * The start of the selection is being dragged. + */ + GrabStart, + + /** + * The end of the selection is being dragged. + */ + GrabEnd + }; + /* ==== Internals ===== */ - gavl_time_t dragStartTime; + /** + * Specifies the type of drag currently taking place. + */ + DragType dragType; + + /** + * 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; + + /** + * This connection is used to represent the timer which causes scroll + * sliding to occur. + * @remarks Scroll sliding is an animated scroll which occurs when + * the user drags a selection outside the area of the timeline body. + */ sigc::connection scrollSlideEvent; + + /** + * Specifies the rate at which scroll sliding is currently taking + * place. + */ int scrollSlideRate; /* ===== Constants ===== */ + /** + * DragZoneSize defines the width of the zone near to the end of the + * selection in which dragging will cause the selection to resize. + * @remarks The selection marque can be resized by dragging the ends + * of it. Special cursors are shown when the mouse is in this region. + */ + static const int DragZoneWidth; + + /** + * The amount to divide the mouse overshoot by to produce the slide + * scroll rate. + * @remarks Smaller values cause faster scrolling. + */ static const int ScrollSlideRateDivisor; + + /** + * The interval between scroll slide events in ms. + */ static const int ScrollSlideEventInterval; }; diff --git a/src/gui/widgets/timeline/timeline-tool.cpp b/src/gui/widgets/timeline/timeline-tool.cpp index 14b4741eb..3d279c73b 100644 --- a/src/gui/widgets/timeline/timeline-tool.cpp +++ b/src/gui/widgets/timeline/timeline-tool.cpp @@ -23,6 +23,8 @@ #include "timeline-tool.hpp" #include "../timeline-widget.hpp" +using namespace Gdk; + namespace lumiera { namespace gui { namespace widgets { @@ -40,7 +42,7 @@ Tool::apply_cursor() { REQUIRE(timelineBody != NULL); - Glib::RefPtr window = + Glib::RefPtr window = timelineBody->get_window(); if(!window) return false; @@ -68,6 +70,12 @@ Tool::on_button_release_event(GdkEventButton* event) isDragging = false; } +void +Tool::on_motion_notify_event(GdkEventMotion *event) +{ + mousePoint = Point(event->x, event->y); +} + lumiera::gui::widgets::TimelineWidget* Tool::get_timeline_widget() const { diff --git a/src/gui/widgets/timeline/timeline-tool.hpp b/src/gui/widgets/timeline/timeline-tool.hpp index 84061e481..546a60273 100644 --- a/src/gui/widgets/timeline/timeline-tool.hpp +++ b/src/gui/widgets/timeline/timeline-tool.hpp @@ -59,6 +59,11 @@ protected: Tool(TimelineBody *timeline_body); public: + /** + * Destructor to be overriden by derived classes. + * @remarks If this were not present, derrived class destructors + * would not be called. + */ virtual ~Tool() {}; /** @@ -74,9 +79,29 @@ public: public: /* ===== Event Handlers ===== */ + /** + * The event handler for button press events. + * @remarks This can be overriden by the derrived classes, but + * Tool::on_button_press_event must be called at the start + * of the derrived class's override. + */ virtual void on_button_press_event(GdkEventButton* event); + + /** + * The event handler for button release events. + * @remarks This can be overriden by the derrived classes, but + * Tool::on_button_release_event must be called at the end of + * the derrived class's override. + */ virtual void on_button_release_event(GdkEventButton* event); - virtual void on_motion_notify_event(GdkEventMotion *event) {} + + /** + * The event handler for mouse move events. + * @remarks This can be overriden by the derrived classes, but + * Tool::on_motion_notify_event must be called at the start of + * the derrived class's override. + */ + virtual void on_motion_notify_event(GdkEventMotion *event); protected: /* ===== Internal Overrides ===== */ @@ -103,11 +128,11 @@ protected: protected: bool isDragging; + Gdk::Point mousePoint; TimelineBody *timelineBody; }; - } // namespace timeline } // namespace widgets } // namespace gui