Chromium Code Reviews| Index: chrome/browser/ui/panels/panel_drag_gtk.cc |
| diff --git a/chrome/browser/ui/panels/panel_drag_gtk.cc b/chrome/browser/ui/panels/panel_drag_gtk.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..a08e6fea9e4d90182265cbe5d0d6031398b3b982 |
| --- /dev/null |
| +++ b/chrome/browser/ui/panels/panel_drag_gtk.cc |
| @@ -0,0 +1,295 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/ui/panels/panel_drag_gtk.h" |
| + |
| +#include <gdk/gdkkeysyms.h> |
| + |
| +#include "chrome/browser/ui/panels/panel.h" |
| +#include "chrome/browser/ui/panels/panel_constants.h" |
| +#include "chrome/browser/ui/panels/panel_manager.h" |
| +#include "ui/gfx/gtk_util.h" |
| + |
| +namespace { |
| + |
| +panel::ResizingSides GdkWindowEdgeToResizingSide(GdkWindowEdge& edge) { |
|
dcheng
2012/04/12 00:12:45
GdkWindowEdge instead of GdkWindowEdge& ?
jennb
2012/04/12 18:00:24
Done.
|
| + switch (edge) { |
| + case GDK_WINDOW_EDGE_NORTH_WEST: |
| + return panel::RESIZE_TOP_LEFT; |
| + case GDK_WINDOW_EDGE_NORTH: |
| + return panel::RESIZE_TOP; |
| + case GDK_WINDOW_EDGE_NORTH_EAST: |
| + return panel::RESIZE_TOP_RIGHT; |
| + case GDK_WINDOW_EDGE_WEST: |
| + return panel::RESIZE_LEFT; |
| + case GDK_WINDOW_EDGE_EAST: |
| + return panel::RESIZE_RIGHT; |
| + case GDK_WINDOW_EDGE_SOUTH_WEST: |
| + return panel::RESIZE_BOTTOM_LEFT; |
| + case GDK_WINDOW_EDGE_SOUTH: |
| + return panel::RESIZE_BOTTOM; |
| + case GDK_WINDOW_EDGE_SOUTH_EAST: |
| + return panel::RESIZE_BOTTOM_RIGHT; |
| + default: |
| + return panel::RESIZE_NONE; |
| + } |
| +} |
| + |
| +} // namespace |
| + |
| +// Virtual base class to abstract move vs resize drag logic. |
| +class PanelDragDelegate { |
| + public: |
| + explicit PanelDragDelegate(Panel* panel) : panel_(panel) {} |
| + virtual ~PanelDragDelegate() {} |
|
Evan Stade
2012/04/12 03:25:29
add some vertical space
jennb
2012/04/12 18:00:24
Done.
|
| + Panel* panel() const { return panel_; } |
| + PanelManager* panel_manager() const { return panel_->manager(); } |
| + virtual void DragStarted(gfx::Point point) = 0; |
|
Evan Stade
2012/04/12 03:25:29
docs for the Drag* functions (e.g. what coordinate
jennb
2012/04/12 18:00:24
Done.
|
| + virtual void Dragged(gfx::Point point) = 0; |
| + virtual void DragEnded(bool canceled) = 0; |
| + private: |
| + // Weak pointer to the panel being dragged. |
| + Panel* panel_; |
|
dcheng
2012/04/12 00:12:45
Nit: DISABLE_COPY_AND_ASSIGN for these 3 classes.
jennb
2012/04/12 18:00:24
Done.
|
| +}; |
| + |
| +// Delegate for moving a panel by dragging the mouse. |
| +class MoveDragDelegate : public PanelDragDelegate { |
|
Dmitry Titov
2012/04/12 00:24:44
The same name "Drag" is used in 2 different contex
jennb
2012/04/12 18:00:24
In GTK, move-drag and resize-drag is used to disti
|
| + public: |
| + explicit MoveDragDelegate(Panel* panel) |
| + : PanelDragDelegate(panel) {} |
| + ~MoveDragDelegate() {} |
| + void DragStarted(gfx::Point point) { |
|
dcheng
2012/04/12 00:12:45
Do you need to use OVERRIDE here?
jennb
2012/04/12 18:00:24
Done.
|
| + panel_manager()->StartDragging(panel(), point); |
| + } |
| + void Dragged(gfx::Point point) { |
| + panel_manager()->Drag(point); |
| + } |
| + void DragEnded(bool canceled) { |
| + panel_manager()->EndDragging(canceled); |
| + } |
| +}; |
| + |
| +// Delegate for resizing a panel by dragging the mouse. |
| +class ResizeDragDelegate : public PanelDragDelegate { |
| + public: |
| + ResizeDragDelegate(Panel* panel, GdkWindowEdge& edge) |
|
dcheng
2012/04/12 00:12:45
Ditto for mutable reference.
jennb
2012/04/12 18:00:24
Done.
|
| + : PanelDragDelegate(panel), |
| + resizing_side_(GdkWindowEdgeToResizingSide(edge)) {} |
| + ~ResizeDragDelegate() {} |
| + void DragStarted(gfx::Point point) { |
|
dcheng
2012/04/12 00:12:45
Ditto for OVERRIDE.
jennb
2012/04/12 18:00:24
Done.
|
| + panel_manager()->StartResizingByMouse(panel(), point, resizing_side_); |
| + } |
| + void Dragged(gfx::Point point) { |
| + panel_manager()->ResizeByMouse(point); |
| + } |
| + void DragEnded(bool canceled) { |
| + panel_manager()->EndResizingByMouse(canceled); |
| + } |
| + private: |
| + // The edge from which the panel is being resized. |
| + panel::ResizingSides resizing_side_; |
| +}; |
| + |
| +// Panel drag helper for processing mouse and keyboard events while |
| +// the left mouse button is pressed. |
| +PanelDragGtk::PanelDragGtk(Panel* panel) |
| + : panel_(panel), |
| + drag_state_(NOT_DRAGGING), |
| + initial_mouse_down_(NULL), |
| + click_handler_(NULL), |
| + drag_delegate_(NULL) { |
| + // Create an invisible event box to receive mouse and key events. |
| + drag_widget_ = gtk_event_box_new(); |
| + gtk_event_box_set_visible_window(GTK_EVENT_BOX(drag_widget_), FALSE); |
| + |
| + // Connect signals for events during a drag. |
| + g_signal_connect(drag_widget_, "motion-notify-event", |
| + G_CALLBACK(OnMouseMoveEventThunk), this); |
| + g_signal_connect(drag_widget_, "key-press-event", |
| + G_CALLBACK(OnKeyPressEventThunk), this); |
| + g_signal_connect(drag_widget_, "key-release-event", |
| + G_CALLBACK(OnKeyReleaseEventThunk), this); |
| + g_signal_connect(drag_widget_, "button-press-event", |
| + G_CALLBACK(OnButtonPressEventThunk), this); |
| + g_signal_connect(drag_widget_, "button-release-event", |
| + G_CALLBACK(OnButtonReleaseEventThunk), this); |
| + g_signal_connect(drag_widget_, "grab-broken-event", |
| + G_CALLBACK(OnGrabBrokenEventThunk), this); |
| +} |
| + |
| +PanelDragGtk::~PanelDragGtk() { |
| + EndDrag(true); // Clean up drag state. |
| +} |
| + |
| +bool PanelDragGtk::AssertCleanState() { |
|
dcheng
2012/04/12 00:12:45
I think this can just return void since you always
jennb
2012/04/12 18:00:24
Done.
|
| + DCHECK_EQ(NOT_DRAGGING, drag_state_); |
| + DCHECK(!drag_delegate_); |
| + DCHECK(!initial_mouse_down_); |
| + DCHECK(!click_handler_); |
| + return TRUE; |
| +} |
| + |
| +void PanelDragGtk::InitialWindowEdgeMousePress(GdkEventButton* event, |
| + GdkCursor* cursor, |
| + GdkWindowEdge& edge) { |
| + DCHECK(AssertCleanState()); |
| + drag_delegate_ = new ResizeDragDelegate(panel_, edge); |
| + GrabPointerAndKeyboard(event, cursor); |
| +} |
| + |
| +void PanelDragGtk::InitialTitlebarMousePress(GdkEventButton* event, |
| + GtkWidget* titlebar_widget) { |
| + DCHECK(AssertCleanState()); |
| + click_handler_ = titlebar_widget; |
| + drag_delegate_ = new MoveDragDelegate(panel_); |
| + GrabPointerAndKeyboard(event, gfx::GetCursor(GDK_FLEUR)); // Drag cursor. |
| +} |
| + |
| +void PanelDragGtk::GrabPointerAndKeyboard(GdkEventButton* event, |
| + GdkCursor* cursor) { |
| + // Remember initial mouse event for use in determining when drag |
| + // threshold has been exceeded. |
| + initial_mouse_down_ = gdk_event_copy(reinterpret_cast<GdkEvent*>(event)); |
| + |
| + // Grab pointer and keyboard to make sure we have the focus and get |
| + // all mouse and keyboard events during the drag. |
| + GdkWindow* gdk_window = gtk_widget_get_window(drag_widget_); |
| + DCHECK(gdk_window != NULL); |
|
jennb
2012/04/11 21:59:33
Evan - any idea why this DCHECK fails if I create
Evan Stade
2012/04/12 03:25:29
I may not be understanding the question exactly bu
|
| + GdkGrabStatus pointer_grab_status = |
| + gdk_pointer_grab(gdk_window, |
|
Evan Stade
2012/04/12 03:25:29
2 more spaces indent
jennb
2012/04/12 18:00:24
Done.
|
| + TRUE, |
| + GdkEventMask(GDK_BUTTON_PRESS_MASK | |
| + GDK_BUTTON_RELEASE_MASK | |
| + GDK_POINTER_MOTION_MASK), |
| + NULL, |
| + cursor, |
| + event->time); |
| + GdkGrabStatus keyboard_grab_status = |
| + gdk_keyboard_grab(gdk_window, TRUE, event->time); |
|
Evan Stade
2012/04/12 03:25:29
2 more spaces indent
jennb
2012/04/12 18:00:24
Done.
|
| + if (pointer_grab_status != GDK_GRAB_SUCCESS || |
| + keyboard_grab_status != GDK_GRAB_SUCCESS) { |
| + // Grab could fail if someone else already has the pointer/keyboard |
| + // grabbed. Cancel the drag. |
| + DLOG(ERROR) << "Unable to grab pointer or keyboard (pointer_status=" |
| + << pointer_grab_status << ", keyboard_status=" |
| + << keyboard_grab_status<< ")"; |
|
Evan Stade
2012/04/12 03:25:29
space before <<
jennb
2012/04/12 18:00:24
Done.
|
| + EndDrag(true); |
| + return; |
| + } |
| + |
| + gtk_grab_add(drag_widget_); |
| +} |
| + |
| +void PanelDragGtk::EndDrag(bool canceled) { |
| + if (initial_mouse_down_) { |
| + gdk_event_free(initial_mouse_down_); |
| + initial_mouse_down_ = NULL; |
| + } |
| + |
| + if (drag_delegate_) { |
| + gdk_pointer_ungrab(GDK_CURRENT_TIME); |
| + gdk_keyboard_ungrab(GDK_CURRENT_TIME); |
| + gtk_grab_remove(drag_widget_); |
| + |
| + if (drag_state_ == DRAG_IN_PROGRESS) { |
| + drag_delegate_->DragEnded(canceled); |
| + drag_state_ = NOT_DRAGGING; |
| + } |
| + |
| + delete drag_delegate_; |
| + drag_delegate_ = NULL; |
| + } |
| + |
| + click_handler_ = NULL; |
| +} |
| + |
| +gboolean PanelDragGtk::OnMouseMoveEvent(GtkWidget* widget, |
| + GdkEventMotion* event) { |
| + DCHECK(drag_delegate_); |
| + |
| + gdouble new_x_double; |
| + gdouble new_y_double; |
| + gdk_event_get_root_coords(reinterpret_cast<GdkEvent*>(event), |
| + &new_x_double, &new_y_double); |
| + gint new_x = static_cast<gint>(new_x_double); |
| + gint new_y = static_cast<gint>(new_y_double); |
| + |
| + // Begin dragging only after mouse has moved beyond the drag threshold. |
| + if (drag_state_ == NOT_DRAGGING) { |
| + DCHECK(initial_mouse_down_); |
| + gdouble old_x_double; |
| + gdouble old_y_double; |
| + gdk_event_get_root_coords(initial_mouse_down_, |
| + &old_x_double, &old_y_double); |
| + gint old_x = static_cast<gint>(old_x_double); |
| + gint old_y = static_cast<gint>(old_y_double); |
| + |
| + if (gtk_drag_check_threshold(drag_widget_, old_x, old_y, |
| + new_x, new_y)) { |
| + drag_state_ = DRAG_IN_PROGRESS; |
| + drag_delegate_->DragStarted(gfx::Point(old_x, old_y)); |
| + gdk_event_free(initial_mouse_down_); |
| + initial_mouse_down_ = NULL; |
| + } |
| + } |
| + |
| + if (drag_state_ == DRAG_IN_PROGRESS) |
| + drag_delegate_->Dragged(gfx::Point(new_x, new_y)); |
| + |
| + return TRUE; |
| +} |
| + |
| +gboolean PanelDragGtk::OnButtonPressEvent(GtkWidget* widget, |
| + GdkEventButton* event) { |
| + DCHECK(drag_delegate_); |
| + return TRUE; |
| +} |
| + |
| +gboolean PanelDragGtk::OnButtonReleaseEvent(GtkWidget* widget, |
| + GdkEventButton* event) { |
| + DCHECK(drag_delegate_); |
| + |
| + if (event->button == 1) { |
| + // Treat release as a mouse click if drag was never started. |
| + if (drag_state_ == NOT_DRAGGING && click_handler_) { |
| + gtk_propagate_event(click_handler_, |
| + reinterpret_cast<GdkEvent*>(event)); |
| + } |
| + // Cleanup state regardless. |
| + EndDrag(false); |
| + } |
| + |
| + return TRUE; |
| +} |
| + |
| +gboolean PanelDragGtk::OnKeyPressEvent(GtkWidget* widget, |
| + GdkEventKey* event) { |
| + DCHECK(drag_delegate_); |
| + return TRUE; |
| +} |
| + |
| +gboolean PanelDragGtk::OnKeyReleaseEvent(GtkWidget* widget, |
| + GdkEventKey* event) { |
| + DCHECK(drag_delegate_); |
| + |
| + switch (event->keyval) { |
| + case GDK_Escape: |
| + EndDrag(true); // Cancel drag. |
| + break; |
| + case GDK_Return: |
| + case GDK_KP_Enter: |
| + case GDK_ISO_Enter: |
| + case GDK_space: |
| + EndDrag(false); // Normal end. |
| + break; |
| + } |
| + return TRUE; |
| +} |
| + |
| +gboolean PanelDragGtk::OnGrabBrokenEvent(GtkWidget* widget, |
| + GdkEventGrabBroken* event) { |
| + DCHECK(drag_delegate_); |
| + EndDrag(true); // Cancel drag. |
| + return TRUE; |
| +} |