Chromium Code Reviews| Index: chrome/views/widget/widget_gtk.cc |
| =================================================================== |
| --- chrome/views/widget/widget_gtk.cc (revision 15583) |
| +++ chrome/views/widget/widget_gtk.cc (working copy) |
| @@ -6,35 +6,60 @@ |
| #include "chrome/views/fill_layout.h" |
| #include "chrome/views/widget/root_view.h" |
| +#include "chrome/views/window/window_gtk.h" |
| namespace views { |
| -WidgetGtk::WidgetGtk() |
| - : widget_(NULL), |
| +// Returns the position of a widget on screen. |
| +static void GetWidgetPositionOnScreen(GtkWidget* widget, int* x, int *y) { |
| + GtkWidget* parent = widget; |
| + while (parent) { |
| + if (GTK_IS_WINDOW(widget)) { |
| + int window_x, window_y; |
| + gtk_window_get_position(GTK_WINDOW(widget), &window_x, &window_y); |
| + *x += window_x; |
| + *y += window_y; |
| + return; |
| + } |
| + // Not a window. |
| + *x += widget->allocation.x; |
| + *y += widget->allocation.y; |
| + parent = gtk_widget_get_parent(parent); |
| + } |
| +} |
| + |
| +WidgetGtk::WidgetGtk(Type type) |
| + : type_(type), |
| + widget_(NULL), |
| + child_widget_parent_(NULL), |
| is_mouse_down_(false), |
| last_mouse_event_was_move_(false) { |
| } |
| WidgetGtk::~WidgetGtk() { |
| - gtk_widget_unref(widget_); |
| - |
| + if (widget_) { |
| + // TODO: make sure this is right. |
| + gtk_widget_destroy(widget_); |
| + child_widget_parent_ = widget_ = NULL; |
| + } |
| // MessageLoopForUI::current()->RemoveObserver(this); |
| } |
| void WidgetGtk::Init(const gfx::Rect& bounds, |
| bool has_own_focus_manager) { |
| - |
| // Force creation of the RootView if it hasn't been created yet. |
| GetRootView(); |
| // Make container here. |
| - widget_ = gtk_drawing_area_new(); |
| - gtk_drawing_area_size(GTK_DRAWING_AREA(widget_), 100, 100); |
| - gtk_widget_show(widget_); |
| + CreateGtkWidget(); |
| // Make sure we receive our motion events. |
| - gtk_widget_set_events(widget_, |
| - gtk_widget_get_events(widget_) | |
| + |
| + // We register everything on the parent of all widgets. At a minimum we need |
| + // painting to happen on the parent (otherwise painting doesn't work at all), |
| + // and similarly we need mouse release events on the parent as windows don't |
| + // get mouse releases. |
| + gtk_widget_add_events(child_widget_parent_, |
| GDK_ENTER_NOTIFY_MASK | |
| GDK_LEAVE_NOTIFY_MASK | |
| GDK_BUTTON_PRESS_MASK | |
| @@ -47,36 +72,35 @@ |
| // TODO(port): if(has_own_focus_manager) block |
| - SetViewForNative(widget_, this); |
| SetRootViewForWidget(widget_, root_view_.get()); |
| // MessageLoopForUI::current()->AddObserver(this); |
| - g_signal_connect_after(G_OBJECT(widget_), "size_allocate", |
| + g_signal_connect_after(G_OBJECT(child_widget_parent_), "size_allocate", |
| G_CALLBACK(CallSizeAllocate), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "expose_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "expose_event", |
| G_CALLBACK(CallPaint), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "enter_notify_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "enter_notify_event", |
| G_CALLBACK(CallEnterNotify), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "leave_notify_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "leave_notify_event", |
| G_CALLBACK(CallLeaveNotify), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "motion_notify_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "motion_notify_event", |
| G_CALLBACK(CallMotionNotify), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "button_press_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "button_press_event", |
| G_CALLBACK(CallButtonPress), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "button_release_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "button_release_event", |
| G_CALLBACK(CallButtonRelease), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "focus_in_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "focus_in_event", |
| G_CALLBACK(CallFocusIn), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "focus_out_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "focus_out_event", |
| G_CALLBACK(CallFocusOut), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "key_press_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "key_press_event", |
| G_CALLBACK(CallKeyPress), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "key_release_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "key_release_event", |
| G_CALLBACK(CallKeyRelease), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "scroll_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "scroll_event", |
| G_CALLBACK(CallScroll), NULL); |
| - g_signal_connect(G_OBJECT(widget_), "visibility_notify_event", |
| + g_signal_connect(G_OBJECT(child_widget_parent_), "visibility_notify_event", |
| G_CALLBACK(CallVisibilityNotify), NULL); |
| // TODO(erg): Ignore these signals for now because they're such a drag. |
| @@ -91,8 +115,23 @@ |
| // G_CALLBACK(drag_data_received_event_cb), NULL); |
| } |
| +void WidgetGtk::AddChild(GtkWidget* child) { |
| + gtk_container_add(GTK_CONTAINER(child_widget_parent_), child); |
| +} |
| + |
| +void WidgetGtk::RemoveChild(GtkWidget* child) { |
| + gtk_container_remove(GTK_CONTAINER(child_widget_parent_), child); |
| +} |
| + |
| +void WidgetGtk::PositionChild(GtkWidget* child, int x, int y, int w, int h) { |
| + GtkAllocation alloc = { x, y, w, h }; |
| + gtk_widget_size_allocate(child, &alloc); |
| + gtk_fixed_move(GTK_FIXED(child_widget_parent_), child, x, y); |
|
Elliot Glaysher
2009/05/07 22:25:29
(General issue; doesn't block this patch.)
If you
|
| +} |
| + |
| void WidgetGtk::SetContentsView(View* view) { |
| - DCHECK(view && widget_) << "Can't be called until after the HWND is created!"; |
| + DCHECK(view && widget_) |
| + << "Can't be called until after the HWND is created!"; |
| // The ContentsView must be set up _after_ the window is created so that its |
| // Widget pointer is valid. |
| root_view_->SetLayoutManager(new FillLayout); |
| @@ -100,26 +139,34 @@ |
| root_view_->RemoveAllChildViews(true); |
| root_view_->AddChildView(view); |
| - // TODO(erg): Terrible hack to work around lack of real sizing mechanics for |
| - // now. |
| - root_view_->SetBounds(0, 0, 100, 100); |
| - root_view_->Layout(); |
| - root_view_->SchedulePaint(); |
| - NOTIMPLEMENTED(); |
| + DCHECK(widget_); // Widget must have been created by now. |
| + |
| + OnSizeAllocate(widget_, &(widget_->allocation)); |
| } |
| void WidgetGtk::GetBounds(gfx::Rect* out, bool including_frame) const { |
| + DCHECK(widget_); |
| + |
| + int x = 0, y = 0, w, h; |
| + if (GTK_IS_WINDOW(widget_)) { |
| + gtk_window_get_position(GTK_WINDOW(widget_), &x, &y); |
| + gtk_window_get_size(GTK_WINDOW(widget_), &w, &h); |
| + } else { |
| + // TODO: make sure this is right. Docs indicate gtk_window_get_position |
| + // returns a value useful to the window manager, which may not be the same |
| + // as the actual location on the screen. |
| + GetWidgetPositionOnScreen(widget_, &x, &y); |
| + w = widget_->allocation.width; |
| + h = widget_->allocation.height; |
| + } |
| + |
| if (including_frame) { |
| + // TODO: Docs indicate it isn't possible to get at this value. We may need |
| + // to turn off all decorations so that the frame is always of a 0x0 size. |
| NOTIMPLEMENTED(); |
| - *out = gfx::Rect(); |
| - return; |
| } |
| - // TODO(erg): Not sure how to implement this. gtk_widget_size_request() |
| - // returns a widget's requested size--not it's actual size. The system of |
| - // containers and such do auto sizing tricks to make everything work within |
| - // the constraints and requested sizes... |
| - NOTIMPLEMENTED(); |
| + return out->SetRect(x, y, w, h); |
| } |
| gfx::NativeView WidgetGtk::GetNativeView() const { |
| @@ -127,7 +174,6 @@ |
| } |
| void WidgetGtk::PaintNow(const gfx::Rect& update_rect) { |
| - // TODO(erg): This is woefully incomplete and is a straw man implementation. |
| gtk_widget_queue_draw_area(widget_, update_rect.x(), update_rect.y(), |
| update_rect.width(), update_rect.height()); |
| } |
| @@ -145,8 +191,9 @@ |
| } |
| bool WidgetGtk::IsActive() const { |
| - NOTIMPLEMENTED(); |
| - return false; |
| + // If this only applies to windows, it shouldn't be in widget. |
| + DCHECK(GTK_IS_WINDOW(widget_)); |
| + return gtk_window_is_active(GTK_WINDOW(widget_)); |
| } |
| TooltipManager* WidgetGtk::GetTooltipManager() { |
| @@ -159,6 +206,43 @@ |
| return false; |
| } |
| +Window* WidgetGtk::GetWindow() { |
| + return GetWindowImpl(widget_); |
| +} |
| + |
| +const Window* WidgetGtk::GetWindow() const { |
| + return GetWindowImpl(widget_); |
| +} |
| + |
| +void WidgetGtk::CreateGtkWidget() { |
| + if (type_ == TYPE_CHILD) { |
| + child_widget_parent_ = widget_ = gtk_fixed_new(); |
| + SetViewForNative(widget_, this); |
| + } else { |
| + widget_ = gtk_window_new( |
| + type_ == TYPE_WINDOW ? GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP); |
| + gtk_window_set_decorated(GTK_WINDOW(widget_), false); |
| + // We'll take care of positioning our window. |
| + gtk_window_set_position(GTK_WINDOW(widget_), GTK_WIN_POS_NONE); |
| + SetWindowForNative(widget_, static_cast<WindowGtk*>(this)); |
| + SetViewForNative(widget_, this); |
| + |
| + child_widget_parent_ = gtk_fixed_new(); |
| + gtk_fixed_set_has_window(GTK_FIXED(child_widget_parent_), true); |
| + gtk_container_add(GTK_CONTAINER(widget_), child_widget_parent_); |
| + gtk_widget_show(child_widget_parent_); |
| + |
| + SetViewForNative(child_widget_parent_, this); |
| + } |
| + gtk_widget_show(widget_); |
| +} |
| + |
| +void WidgetGtk::OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) { |
| + root_view_->SetBounds(0, 0, allocation->width, allocation->height); |
| + root_view_->Layout(); |
| + root_view_->SchedulePaint(); |
| +} |
| + |
| gboolean WidgetGtk::OnMotionNotify(GtkWidget* widget, GdkEventMotion* event) { |
| gfx::Point screen_loc(event->x_root, event->y_root); |
| if (last_mouse_event_was_move_ && last_mouse_move_x_ == screen_loc.x() && |
| @@ -178,7 +262,8 @@ |
| } |
| gboolean WidgetGtk::OnButtonPress(GtkWidget* widget, GdkEventButton* event) { |
| - return ProcessMousePressed(event); |
| + ProcessMousePressed(event); |
| + return true; |
| } |
| gboolean WidgetGtk::OnButtonRelease(GtkWidget* widget, GdkEventButton* event) { |
| @@ -186,9 +271,8 @@ |
| return true; |
| } |
| -gboolean WidgetGtk::OnPaint(GtkWidget* widget, GdkEventExpose* event) { |
| +void WidgetGtk::OnPaint(GtkWidget* widget, GdkEventExpose* event) { |
| root_view_->OnPaint(event); |
| - return true; |
| } |
| gboolean WidgetGtk::OnEnterNotify(GtkWidget* widget, GdkEventCrossing* event) { |
| @@ -213,16 +297,42 @@ |
| return root_view_->ProcessKeyEvent(key_event); |
| } |
| +// static |
| +WindowGtk* WidgetGtk::GetWindowForNative(GtkWidget* widget) { |
| + gpointer user_data = g_object_get_data(G_OBJECT(widget), "chrome-window"); |
| + return static_cast<WindowGtk*>(user_data); |
| +} |
| + |
| +// static |
| +void WidgetGtk::SetWindowForNative(GtkWidget* widget, WindowGtk* window) { |
| + g_object_set_data(G_OBJECT(widget), "chrome-window", window); |
| +} |
| + |
| RootView* WidgetGtk::CreateRootView() { |
| return new RootView(this); |
| } |
| bool WidgetGtk::ProcessMousePressed(GdkEventButton* event) { |
| last_mouse_event_was_move_ = false; |
| + // TODO: move this code into a common place. Also need support for |
| + // double/triple click. |
| + int flags = Event::GetFlagsFromGdkState(event->state); |
| + switch (event->button) { |
| + case 1: |
| + flags |= Event::EF_LEFT_BUTTON_DOWN; |
| + break; |
| + case 2: |
| + flags |= Event::EF_MIDDLE_BUTTON_DOWN; |
| + break; |
| + case 3: |
| + flags |= Event::EF_MIDDLE_BUTTON_DOWN; |
| + break; |
| + default: |
| + // We only deal with 1-3. |
| + break; |
| + } |
| MouseEvent mouse_pressed(Event::ET_MOUSE_PRESSED, |
| - event->x, event->y, |
| -// (dbl_click ? MouseEvent::EF_IS_DOUBLE_CLICK : 0) | |
| - Event::GetFlagsFromGdkState(event->state)); |
| + event->x, event->y, flags); |
| if (root_view_->OnMousePressed(mouse_pressed)) { |
| is_mouse_down_ = true; |
| // TODO(port): Enable this once I figure out what capture is. |
| @@ -288,10 +398,9 @@ |
| gboolean WidgetGtk::CallPaint(GtkWidget* widget, GdkEventExpose* event) { |
| WidgetGtk* widget_gtk = GetViewForNative(widget); |
| - if (!widget_gtk) |
| - return false; |
| - |
| - return widget_gtk->OnPaint(widget, event); |
| + if (widget_gtk) |
| + widget_gtk->OnPaint(widget, event); |
| + return false; // False indicates other widgets should get the event as well. |
| } |
| gboolean WidgetGtk::CallEnterNotify(GtkWidget* widget, GdkEventCrossing* event) { |
| @@ -383,4 +492,17 @@ |
| return widget_gtk->OnVisibilityNotify(widget, event); |
| } |
| +// Returns the first ancestor of |widget| that is a window. |
| +// static |
| +Window* WidgetGtk::GetWindowImpl(GtkWidget* widget) { |
| + GtkWidget* parent = widget; |
| + while (parent) { |
| + WindowGtk* window = GetWindowForNative(widget); |
| + if (window) |
| + return window; |
| + parent = gtk_widget_get_parent(parent); |
| + } |
| + return NULL; |
| } |
| + |
| +} |