diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-17 04:23:00 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-17 04:23:00 +0000 |
commit | 4c9471c16ba43fb40017e9f5fae8231799e86ccb (patch) | |
tree | 86d1f5ec140af9841b92108299208ca3e7fdfed7 | |
parent | 061efec5fa1af222d72b887feb5788260cb5248f (diff) | |
download | chromium_src-4c9471c16ba43fb40017e9f5fae8231799e86ccb.zip chromium_src-4c9471c16ba43fb40017e9f5fae8231799e86ccb.tar.gz chromium_src-4c9471c16ba43fb40017e9f5fae8231799e86ccb.tar.bz2 |
Fix build bustage by removing another dependency on browser/gtk by creating a new TabContentsViewGtk specifically for views. This subclasses WidgetGtk similar to how TabContentsViewWin subclasses WidgetWin.
There was a bug in NativeViewHostGtk - reparenting needs to be done atomically using gtk_widget_reparent since GtkWidgets are refcounted and when removed from a container are released, causing a crash when a TabContents is reparented.
The code now compiles thanks to a stubbed BlockedPopupContainer, however there is one remaining issue - the browser window no longer paints and the app instantly hangs. However this is better than the current state so I figured I'd send the code review.
Review URL: http://codereview.chromium.org/126107
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18588 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/views/tab_contents/render_view_context_menu_win.h | 2 | ||||
-rw-r--r-- | chrome/browser/views/tab_contents/tab_contents_view_gtk.cc | 325 | ||||
-rw-r--r-- | chrome/browser/views/tab_contents/tab_contents_view_gtk.h | 87 | ||||
-rw-r--r-- | chrome/chrome.gyp | 13 | ||||
-rw-r--r-- | views/controls/button/native_button_gtk.cc | 8 | ||||
-rw-r--r-- | views/controls/button/native_button_gtk.h | 2 | ||||
-rw-r--r-- | views/controls/native/native_view_host_gtk.cc | 66 | ||||
-rw-r--r-- | views/controls/native/native_view_host_gtk.h | 14 | ||||
-rw-r--r-- | views/widget/widget_gtk.cc | 103 | ||||
-rw-r--r-- | views/widget/widget_gtk.h | 32 |
10 files changed, 540 insertions, 112 deletions
diff --git a/chrome/browser/views/tab_contents/render_view_context_menu_win.h b/chrome/browser/views/tab_contents/render_view_context_menu_win.h index 1d4be70..e897e7e 100644 --- a/chrome/browser/views/tab_contents/render_view_context_menu_win.h +++ b/chrome/browser/views/tab_contents/render_view_context_menu_win.h @@ -28,7 +28,7 @@ class RenderViewContextMenuWin : public RenderViewContextMenu, views::Accelerator* accelerator); virtual void ExecuteCommand(int command_id); - HMENU GetMenuHandle() const { + gfx::NativeMenu GetMenuHandle() const { return (menu_.get() ? menu_->GetNativeMenu() : NULL); } diff --git a/chrome/browser/views/tab_contents/tab_contents_view_gtk.cc b/chrome/browser/views/tab_contents/tab_contents_view_gtk.cc new file mode 100644 index 0000000..a4d8ad6 --- /dev/null +++ b/chrome/browser/views/tab_contents/tab_contents_view_gtk.cc @@ -0,0 +1,325 @@ +// Copyright (c) 2009 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/views/tab_contents/tab_contents_view_gtk.h" + +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +#include "base/string_util.h" +#include "base/gfx/point.h" +#include "base/gfx/rect.h" +#include "base/gfx/size.h" +#include "build/build_config.h" +#include "chrome/browser/blocked_popup_container.h" +#include "chrome/browser/download/download_shelf.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_view_host_factory.h" +#include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" +#include "chrome/browser/tab_contents/interstitial_page.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_delegate.h" +#include "chrome/browser/views/sad_tab_view.h" +#include "chrome/browser/views/tab_contents/render_view_context_menu_win.h" +#include "views/controls/native/native_view_host.h" +#include "views/widget/root_view.h" + +using WebKit::WebInputEvent; + + +namespace { + +// Called when the content view gtk widget is tabbed to, or after the call to +// gtk_widget_child_focus() in TakeFocus(). We return true +// and grab focus if we don't have it. The call to +// FocusThroughTabTraversal(bool) forwards the "move focus forward" effect to +// webkit. +gboolean OnFocus(GtkWidget* widget, GtkDirectionType focus, + TabContents* tab_contents) { + // If we already have focus, let the next widget have a shot at it. We will + // reach this situation after the call to gtk_widget_child_focus() in + // TakeFocus(). + if (gtk_widget_is_focus(widget)) + return FALSE; + + gtk_widget_grab_focus(widget); + bool reverse = focus == GTK_DIR_TAB_BACKWARD; + tab_contents->FocusThroughTabTraversal(reverse); + return TRUE; +} + +// Called when the mouse leaves the widget. We notify our delegate. +gboolean OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event, + TabContents* tab_contents) { + if (tab_contents->delegate()) + tab_contents->delegate()->ContentsMouseEvent(tab_contents, false); + return FALSE; +} + +// Called when the mouse moves within the widget. We notify our delegate. +gboolean OnMouseMove(GtkWidget* widget, GdkEventMotion* event, + TabContents* tab_contents) { + if (tab_contents->delegate()) + tab_contents->delegate()->ContentsMouseEvent(tab_contents, true); + return FALSE; +} + +// See tab_contents_view_win.cc for discussion of mouse scroll zooming. +gboolean OnMouseScroll(GtkWidget* widget, GdkEventScroll* event, + TabContents* tab_contents) { + if ((event->state & gtk_accelerator_get_default_mod_mask()) == + GDK_CONTROL_MASK) { + if (event->direction == GDK_SCROLL_DOWN) { + tab_contents->delegate()->ContentsZoomChange(false); + return TRUE; + } else if (event->direction == GDK_SCROLL_UP) { + tab_contents->delegate()->ContentsZoomChange(true); + return TRUE; + } + } + + return FALSE; +} + +} // namespace + +// static +TabContentsView* TabContentsView::Create(TabContents* tab_contents) { + return new TabContentsViewGtk(tab_contents); +} + +TabContentsViewGtk::TabContentsViewGtk(TabContents* tab_contents) + : TabContentsView(tab_contents), + views::WidgetGtk(TYPE_CHILD), + ignore_next_char_event_(false) { +} + +TabContentsViewGtk::~TabContentsViewGtk() { +} + +void TabContentsViewGtk::CreateView() { + set_delete_on_destroy(false); + WidgetGtk::Init(NULL, gfx::Rect(), false); +} + +RenderWidgetHostView* TabContentsViewGtk::CreateViewForWidget( + RenderWidgetHost* render_widget_host) { + if (render_widget_host->view()) { + // During testing, the view will already be set up in most cases to the + // test view, so we don't want to clobber it with a real one. To verify that + // this actually is happening (and somebody isn't accidentally creating the + // view twice), we check for the RVH Factory, which will be set when we're + // making special ones (which go along with the special views). + DCHECK(RenderViewHostFactory::has_factory()); + return render_widget_host->view(); + } + + RenderWidgetHostViewGtk* view = + new RenderWidgetHostViewGtk(render_widget_host); + view->InitAsChild(); + g_signal_connect(view->native_view(), "focus", + G_CALLBACK(OnFocus), tab_contents()); +/* + g_signal_connect(view->native_view(), "leave-notify-event", + G_CALLBACK(OnLeaveNotify), tab_contents()); +*/ + g_signal_connect(view->native_view(), "motion-notify-event", + G_CALLBACK(OnMouseMove), tab_contents()); + g_signal_connect(view->native_view(), "scroll-event", + G_CALLBACK(OnMouseScroll), tab_contents()); + gtk_widget_add_events(view->native_view(), GDK_LEAVE_NOTIFY_MASK | + GDK_POINTER_MOTION_MASK); + + gtk_fixed_put(GTK_FIXED(GetNativeView()), view->native_view(), 0, 0); + return view; +} + +gfx::NativeView TabContentsViewGtk::GetNativeView() const { + return WidgetGtk::GetNativeView(); +} + +gfx::NativeView TabContentsViewGtk::GetContentNativeView() const { + if (!tab_contents()->render_widget_host_view()) + return NULL; + return tab_contents()->render_widget_host_view()->GetNativeView(); +} + +gfx::NativeWindow TabContentsViewGtk::GetTopLevelNativeWindow() const { + GtkWidget* window = gtk_widget_get_ancestor(GetNativeView(), GTK_TYPE_WINDOW); + return window ? GTK_WINDOW(window) : NULL; +} + +void TabContentsViewGtk::GetContainerBounds(gfx::Rect* out) const { + GetBounds(out, false); +} + +void TabContentsViewGtk::StartDragging(const WebDropData& drop_data) { + NOTIMPLEMENTED(); + + // Until we have d'n'd implemented, just immediately pretend we're + // already done with the drag and drop so we don't get stuck + // thinking we're in mid-drag. + // TODO(port): remove me when the above NOTIMPLEMENTED is fixed. + if (tab_contents()->render_view_host()) + tab_contents()->render_view_host()->DragSourceSystemDragEnded(); +} + +void TabContentsViewGtk::OnContentsDestroy() { + // TODO(brettw) this seems like maybe it can be moved into OnDestroy and this + // function can be deleted? If you're adding more here, consider whether it + // can be moved into OnDestroy which is a Windows message handler as the + // window is being torn down. + + // When a tab is closed all its child plugin windows are destroyed + // automatically. This happens before plugins get any notification that its + // instances are tearing down. + // + // Plugins like Quicktime assume that their windows will remain valid as long + // as they have plugin instances active. Quicktime crashes in this case + // because its windowing code cleans up an internal data structure that the + // handler for NPP_DestroyStream relies on. + // + // The fix is to detach plugin windows from web contents when it is going + // away. This will prevent the plugin windows from getting destroyed + // automatically. The detached plugin windows will get cleaned up in proper + // sequence as part of the usual cleanup when the plugin instance goes away. + NOTIMPLEMENTED(); +} + +void TabContentsViewGtk::SetPageTitle(const std::wstring& title) { + // Set the window name to include the page title so it's easier to spot + // when debugging (e.g. via xwininfo -tree). + gfx::NativeView content_view = GetContentNativeView(); + if (content_view && content_view->window) + gdk_window_set_title(content_view->window, WideToUTF8(title).c_str()); +} + +void TabContentsViewGtk::OnTabCrashed() { + NOTIMPLEMENTED(); +} + +void TabContentsViewGtk::SizeContents(const gfx::Size& size) { + // TODO(brettw) this is a hack and should be removed. See tab_contents_view.h. + WasSized(size); +} + +void TabContentsViewGtk::Focus() { + if (tab_contents()->interstitial_page()) { + tab_contents()->interstitial_page()->Focus(); + return; + } + + if (sad_tab_.get()) { + sad_tab_->RequestFocus(); + return; + } + + RenderWidgetHostView* rwhv = tab_contents()->render_widget_host_view(); + gtk_widget_grab_focus(rwhv ? rwhv->GetNativeView() : GetNativeView()); +} + +void TabContentsViewGtk::SetInitialFocus() { + if (tab_contents()->FocusLocationBarByDefault()) + tab_contents()->delegate()->SetFocusToLocationBar(); + else + Focus(); +} + +void TabContentsViewGtk::StoreFocus() { + NOTIMPLEMENTED(); +} + +void TabContentsViewGtk::RestoreFocus() { + NOTIMPLEMENTED(); + SetInitialFocus(); +} + +void TabContentsViewGtk::UpdateDragCursor(bool is_drop_target) { + NOTIMPLEMENTED(); +} + +void TabContentsViewGtk::GotFocus() { + tab_contents()->delegate()->TabContentsFocused(tab_contents()); +} + +void TabContentsViewGtk::TakeFocus(bool reverse) { + // This is called when we the renderer asks us to take focus back (i.e., it + // has iterated past the last focusable element on the page). + gtk_widget_child_focus(GTK_WIDGET(GetTopLevelNativeWindow()), + reverse ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD); +} + +void TabContentsViewGtk::HandleKeyboardEvent( + const NativeWebKeyboardEvent& event) { + NOTIMPLEMENTED(); + // TODO(port): could be an accelerator, pass to parent. +} + +void TabContentsViewGtk::ShowContextMenu(const ContextMenuParams& params) { + // Allow delegates to handle the context menu operation first. + if (tab_contents()->delegate()->HandleContextMenu(params)) + return; + + context_menu_.reset(new RenderViewContextMenuWin(tab_contents(), params)); + context_menu_->Init(); + + gfx::Point screen_point(params.x, params.y); + views::View::ConvertPointToScreen(GetRootView(), &screen_point); + + // Enable recursive tasks on the message loop so we can get updates while + // the context menu is being displayed. + bool old_state = MessageLoop::current()->NestableTasksAllowed(); + MessageLoop::current()->SetNestableTasksAllowed(true); + context_menu_->RunMenuAt(screen_point.x(), screen_point.y()); + MessageLoop::current()->SetNestableTasksAllowed(old_state); +} + +void TabContentsViewGtk::OnSizeAllocate(GtkWidget* widget, + GtkAllocation* allocation) { + WasSized(gfx::Size(allocation->width, allocation->height)); +} + +void TabContentsViewGtk::WasHidden() { + tab_contents()->HideContents(); +} + +void TabContentsViewGtk::WasShown() { + tab_contents()->ShowContents(); +} + +void TabContentsViewGtk::WasSized(const gfx::Size& size) { + if (tab_contents()->interstitial_page()) + tab_contents()->interstitial_page()->SetSize(size); + if (tab_contents()->render_widget_host_view()) + tab_contents()->render_widget_host_view()->SetSize(size); + + // TODO(brettw) this function can probably be moved to this class. + tab_contents()->RepositionSupressedPopupsToFit(); +} + +// TODO(port): port BlockedPopupContainerViewWin... + +class BlockedPopupContainerViewGtk : public BlockedPopupContainerView { + public: + BlockedPopupContainerViewGtk() {} + virtual ~BlockedPopupContainerViewGtk() {} + + // Overridden from BlockedPopupContainerView: + virtual void SetPosition() {} + virtual void ShowView() {} + virtual void UpdateLabel() {} + virtual void HideView() {} + virtual void Destroy() { + delete this; + } + + private: + DISALLOW_COPY_AND_ASSIGN(BlockedPopupContainerViewGtk); +}; + +// static +BlockedPopupContainerView* BlockedPopupContainerView::Create( + BlockedPopupContainer* container) { + return new BlockedPopupContainerViewGtk; +} diff --git a/chrome/browser/views/tab_contents/tab_contents_view_gtk.h b/chrome/browser/views/tab_contents/tab_contents_view_gtk.h new file mode 100644 index 0000000..900fecc --- /dev/null +++ b/chrome/browser/views/tab_contents/tab_contents_view_gtk.h @@ -0,0 +1,87 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_BROWSER_VIEWS_TAB_CONTENTS_TAB_CONTENTS_VIEW_GTK_H_ +#define CHROME_BROWSER_VIEWS_TAB_CONTENTS_TAB_CONTENTS_VIEW_GTK_H_ + +#include "base/gfx/size.h" +#include "base/scoped_ptr.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" +#include "views/widget/widget_gtk.h" + +class RenderViewContextMenuWin; +class SadTabView; +namespace views { +class NativeViewHost; +} + +// Gtk-specific implementation of the TabContentsView for the views-based front +// end. It is a WidgetGtk that contains all of the contents of the tab and +// associated child views. +class TabContentsViewGtk : public TabContentsView, + public views::WidgetGtk { + public: + // The corresponding TabContents is passed in the constructor, and manages our + // lifetime. This doesn't need to be the case, but is this way currently + // because that's what was easiest when they were split. + explicit TabContentsViewGtk(TabContents* tab_contents); + virtual ~TabContentsViewGtk(); + + // TabContentsView implementation -------------------------------------------- + + virtual void CreateView(); + virtual RenderWidgetHostView* CreateViewForWidget( + RenderWidgetHost* render_widget_host); + virtual gfx::NativeView GetNativeView() const; + virtual gfx::NativeView GetContentNativeView() const; + virtual gfx::NativeWindow GetTopLevelNativeWindow() const; + virtual void GetContainerBounds(gfx::Rect* out) const; + virtual void OnContentsDestroy(); + virtual void SetPageTitle(const std::wstring& title); + virtual void OnTabCrashed(); + virtual void SizeContents(const gfx::Size& size); + virtual void Focus(); + virtual void SetInitialFocus(); + virtual void StoreFocus(); + virtual void RestoreFocus(); + + // Backend implementation of RenderViewHostDelegate::View. + virtual void ShowContextMenu(const ContextMenuParams& params); + virtual void StartDragging(const WebDropData& drop_data); + virtual void UpdateDragCursor(bool is_drop_target); + virtual void GotFocus(); + virtual void TakeFocus(bool reverse); + virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event); + + private: + // Signal handlers ----------------------------------------------------------- + + // Overridden from views::WidgetGtk: + virtual void OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation); + + // Handles notifying the TabContents and other operations when the window was + // shown or hidden. + void WasHidden(); + void WasShown(); + + // Handles resizing of the contents. This will notify the RenderWidgetHostView + // of the change, reposition popups, and the find in page bar. + void WasSized(const gfx::Size& size); + + // --------------------------------------------------------------------------- + + // Used to render the sad tab. This will be non-NULL only when the sad tab is + // visible. + scoped_ptr<SadTabView> sad_tab_; + + // Whether to ignore the next CHAR keyboard event. + bool ignore_next_char_event_; + + // The context menu. Callbacks are asynchronous so we need to keep it around. + scoped_ptr<RenderViewContextMenuWin> context_menu_; + + DISALLOW_COPY_AND_ASSIGN(TabContentsViewGtk); +}; + +#endif // CHROME_BROWSER_VIEWS_TAB_CONTENTS_TAB_CONTENTS_VIEW_GTK_H_ diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index a407299..18cef6d 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1597,6 +1597,8 @@ 'browser/views/tab_contents/render_view_context_menu_win.h', 'browser/views/tab_contents/render_view_context_menu_external_win.cc', 'browser/views/tab_contents/render_view_context_menu_external_win.h', + 'browser/views/tab_contents/tab_contents_view_gtk.cc', + 'browser/views/tab_contents/tab_contents_view_gtk.h', 'browser/views/tab_contents/tab_contents_view_win.cc', 'browser/views/tab_contents/tab_contents_view_win.h', 'browser/views/tabs/dragged_tab_controller.cc', @@ -1890,8 +1892,12 @@ ['include', '^browser/views/status_bubble_views.h'], ['include', '^browser/views/tab_contents/native_tab_contents_container_gtk.cc'], ['include', '^browser/views/tab_contents/native_tab_contents_container_gtk.h'], + ['include', '^browser/views/tab_contents/render_view_context_menu_win.cc'], + ['include', '^browser/views/tab_contents/render_view_context_menu_win.h'], ['include', '^browser/views/tab_contents/tab_contents_container.cc'], ['include', '^browser/views/tab_contents/tab_contents_container.h'], + ['include', '^browser/views/tab_contents/tab_contents_view_gtk.cc'], + ['include', '^browser/views/tab_contents/tab_contents_view_gtk.h'], ['include', '^browser/views/tab_icon_view.cc'], ['include', '^browser/views/tab_icon_view.h'], ['include', '^browser/views/tabs/dragged_tab_controller.cc'], @@ -1932,6 +1938,13 @@ # More GTK stuff to exclude outside of the browser/gtk directory ['exclude', '^browser/bookmarks/bookmark_context_menu_gtk.cc'], + + # Other excluded stuff. + ['exclude', '^browser/extensions/external_registry_extension_provider_win.cc'], + ['exclude', '^browser/tab_contents/tab_contents_view_gtk.cc'], + ['exclude', '^browser/tab_contents/tab_contents_view_gtk.h'], + ['exclude', '^browser/tab_contents/render_view_context_menu_gtk.cc'], + ['exclude', '^browser/tab_contents/render_view_context_menu_gtk.h'], ], }], ['linux2==1',{ diff --git a/views/controls/button/native_button_gtk.cc b/views/controls/button/native_button_gtk.cc index c97d1e0..1680019 100644 --- a/views/controls/button/native_button_gtk.cc +++ b/views/controls/button/native_button_gtk.cc @@ -83,7 +83,7 @@ gfx::Size NativeButtonGtk::GetPreferredSize() { void NativeButtonGtk::CreateNativeControl() { GtkWidget* widget = gtk_button_new(); g_signal_connect(G_OBJECT(widget), "clicked", - G_CALLBACK(CallClicked), NULL); + G_CALLBACK(CallClicked), this); NativeControlCreated(widget); } @@ -96,10 +96,8 @@ void NativeButtonGtk::NativeControlCreated(GtkWidget* widget) { } // static -void NativeButtonGtk::CallClicked(GtkButton* widget) { - View* view = NativeViewHostGtk::GetViewForNative(GTK_WIDGET(widget)); - if (view) - static_cast<NativeButtonGtk*>(view)->OnClicked(); +void NativeButtonGtk::CallClicked(GtkButton* widget, NativeButtonGtk* button) { + button->OnClicked(); } void NativeButtonGtk::OnClicked() { diff --git a/views/controls/button/native_button_gtk.h b/views/controls/button/native_button_gtk.h index e719202..6c3f600 100644 --- a/views/controls/button/native_button_gtk.h +++ b/views/controls/button/native_button_gtk.h @@ -36,7 +36,7 @@ class NativeButtonGtk : public NativeControlGtk, public NativeButtonWrapper { virtual bool IsCheckbox() const { return false; } private: - static void CallClicked(GtkButton* widget); + static void CallClicked(GtkButton* widget, NativeButtonGtk* button); // Invoked when the user clicks on the button. void OnClicked(); diff --git a/views/controls/native/native_view_host_gtk.cc b/views/controls/native/native_view_host_gtk.cc index 7f59144..6fa7325 100644 --- a/views/controls/native/native_view_host_gtk.cc +++ b/views/controls/native/native_view_host_gtk.cc @@ -24,47 +24,21 @@ NativeViewHostGtk::NativeViewHostGtk(NativeViewHost* host) NativeViewHostGtk::~NativeViewHostGtk() { } -// static -View* NativeViewHostGtk::GetViewForNative(GtkWidget* widget) { - gpointer user_data = g_object_get_data(G_OBJECT(widget), "chrome-view"); - return static_cast<View*>(user_data); -} - -// static -void NativeViewHostGtk::SetViewForNative(GtkWidget* widget, View* view) { - g_object_set_data(G_OBJECT(widget), "chrome-view", view); -} - //////////////////////////////////////////////////////////////////////////////// // NativeViewHostGtk, NativeViewHostWrapper implementation: void NativeViewHostGtk::NativeViewAttached() { DCHECK(host_->native_view()); - GtkWidget* current_parent = gtk_widget_get_parent(host_->native_view()); - GtkWidget* new_parent = host_->GetWidget()->GetNativeView(); - // Only adjust the parent if the parent actually changed. - if (current_parent != new_parent) { - // First hide the new window. We don't want anything to draw (like sub-hwnd - // borders), when we change the parent below. - gtk_widget_hide(host_->native_view()); - - if (current_parent) { - gtk_container_remove(GTK_CONTAINER(current_parent), - host_->native_view()); - } - - // Adds a mapping between the GtkWidget and us. - SetViewForNative(host_->native_view(), host_); - - if (!destroy_signal_id_) { - destroy_signal_id_ = g_signal_connect(G_OBJECT(host_->native_view()), - "destroy", G_CALLBACK(CallDestroy), - NULL); - } + if (gtk_widget_get_parent(host_->native_view())) + GetHostWidget()->ReparentChild(host_->native_view()); + else + GetHostWidget()->AddChild(host_->native_view()); - // Set the parent. - static_cast<WidgetGtk*>(host_->GetWidget())->AddChild(host_->native_view()); + if (!destroy_signal_id_) { + destroy_signal_id_ = g_signal_connect(G_OBJECT(host_->native_view()), + "destroy", G_CALLBACK(CallDestroy), + this); } // Always layout though. @@ -88,9 +62,9 @@ void NativeViewHostGtk::NativeViewDetaching() { void NativeViewHostGtk::AddedToWidget() { if (!host_->native_view()) return; - WidgetGtk* parent_widget = static_cast<WidgetGtk*>(host_->GetWidget()); + WidgetGtk* parent_widget = GetHostWidget(); GtkWidget* widget_parent = gtk_widget_get_parent(host_->native_view()); - GtkWidget* parent_widget_widget = parent_widget->child_widget_parent(); + GtkWidget* parent_widget_widget = parent_widget->window_contents(); if (widget_parent != parent_widget_widget) { g_object_ref(host_->native_view()); if (widget_parent) @@ -110,10 +84,10 @@ void NativeViewHostGtk::RemovedFromWidget() { if (!host_->native_view()) return; - WidgetGtk* parent_widget = static_cast<WidgetGtk*>(host_->GetWidget()); + WidgetGtk* parent_widget = GetHostWidget(); gtk_widget_hide(host_->native_view()); if (parent_widget) { - gtk_container_remove(GTK_CONTAINER(parent_widget->child_widget_parent()), + gtk_container_remove(GTK_CONTAINER(parent_widget->window_contents()), host_->native_view()); } } @@ -157,8 +131,7 @@ void NativeViewHostGtk::UninstallClip() { } void NativeViewHostGtk::ShowWidget(int x, int y, int w, int h) { - WidgetGtk* parent = static_cast<WidgetGtk*>(host_->GetWidget()); - parent->PositionChild(host_->native_view(), x, y, w, h); + GetHostWidget()->PositionChild(host_->native_view(), x, y, w, h); gtk_widget_show(host_->native_view()); } @@ -173,13 +146,14 @@ void NativeViewHostGtk::SetFocus() { //////////////////////////////////////////////////////////////////////////////// // NativeViewHostGtk, private: -// static -void NativeViewHostGtk::CallDestroy(GtkObject* object) { - View* view = GetViewForNative(GTK_WIDGET(object)); - if (!view) - return; +WidgetGtk* NativeViewHostGtk::GetHostWidget() const { + return static_cast<WidgetGtk*>(host_->GetWidget()); +} - return static_cast<NativeViewHost*>(view)->NativeViewDestroyed(); +// static +void NativeViewHostGtk::CallDestroy(GtkObject* object, + NativeViewHostGtk* host) { + return host->host_->NativeViewDestroyed(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/views/controls/native/native_view_host_gtk.h b/views/controls/native/native_view_host_gtk.h index 40a6a65..154da70 100644 --- a/views/controls/native/native_view_host_gtk.h +++ b/views/controls/native/native_view_host_gtk.h @@ -14,17 +14,13 @@ namespace views { class View; +class WidgetGtk; class NativeViewHostGtk : public NativeViewHostWrapper { public: explicit NativeViewHostGtk(NativeViewHost* host); virtual ~NativeViewHostGtk(); - // Sets and retrieves the View associated with a particular widget. - // TODO(beng): move to NativeViewHost, and have take gfx::NativeViews. - static View* GetViewForNative(GtkWidget* widget); - static void SetViewForNative(GtkWidget* widget, View* view); - // Overridden from NativeViewHostWrapper: virtual void NativeViewAttached(); virtual void NativeViewDetaching(); @@ -38,6 +34,11 @@ class NativeViewHostGtk : public NativeViewHostWrapper { virtual void SetFocus(); private: + WidgetGtk* GetHostWidget() const; + + // Invoked from the 'destroy' signal. + static void CallDestroy(GtkObject* object, NativeViewHostGtk* host); + // Our associated NativeViewHost. NativeViewHost* host_; @@ -48,9 +49,6 @@ class NativeViewHostGtk : public NativeViewHostWrapper { // Signal handle id for 'destroy' signal. gulong destroy_signal_id_; - // Invoked from the 'destroy' signal. - static void CallDestroy(GtkObject* object); - DISALLOW_COPY_AND_ASSIGN(NativeViewHostGtk); }; diff --git a/views/widget/widget_gtk.cc b/views/widget/widget_gtk.cc index 7d7e74b..1aec6a4 100644 --- a/views/widget/widget_gtk.cc +++ b/views/widget/widget_gtk.cc @@ -53,6 +53,9 @@ static int GetFlagsForEventButton(const GdkEventButton& event) { return flags; } +// static +GtkWidget* WidgetGtk::null_parent_ = NULL; + //////////////////////////////////////////////////////////////////////////////// // WidgetGtk, public: @@ -60,7 +63,7 @@ WidgetGtk::WidgetGtk(Type type) : is_window_(false), type_(type), widget_(NULL), - child_widget_parent_(NULL), + window_contents_(NULL), is_mouse_down_(false), has_capture_(false), last_mouse_event_was_move_(false), @@ -84,7 +87,7 @@ void WidgetGtk::Init(GtkWidget* parent, #endif // Make container here. - CreateGtkWidget(); + CreateGtkWidget(parent); // Make sure we receive our motion events. @@ -92,7 +95,7 @@ void WidgetGtk::Init(GtkWidget* parent, // 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_, + gtk_widget_add_events(window_contents_, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | @@ -109,33 +112,34 @@ void WidgetGtk::Init(GtkWidget* parent, MessageLoopForUI::current()->AddObserver(this); - g_signal_connect_after(G_OBJECT(child_widget_parent_), "size_allocate", + // TODO(beng): make these take this rather than NULL. + g_signal_connect_after(G_OBJECT(window_contents_), "size_allocate", G_CALLBACK(CallSizeAllocate), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "expose_event", + g_signal_connect(G_OBJECT(window_contents_), "expose_event", G_CALLBACK(CallPaint), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "enter_notify_event", + g_signal_connect(G_OBJECT(window_contents_), "enter_notify_event", G_CALLBACK(CallEnterNotify), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "leave_notify_event", + g_signal_connect(G_OBJECT(window_contents_), "leave_notify_event", G_CALLBACK(CallLeaveNotify), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "motion_notify_event", + g_signal_connect(G_OBJECT(window_contents_), "motion_notify_event", G_CALLBACK(CallMotionNotify), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "button_press_event", + g_signal_connect(G_OBJECT(window_contents_), "button_press_event", G_CALLBACK(CallButtonPress), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "button_release_event", + g_signal_connect(G_OBJECT(window_contents_), "button_release_event", G_CALLBACK(CallButtonRelease), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "grab_broken_event", + g_signal_connect(G_OBJECT(window_contents_), "focus_out_event", + G_CALLBACK(CallFocusOut), NULL); + g_signal_connect(G_OBJECT(window_contents_), "grab_broken_event", G_CALLBACK(CallGrabBrokeEvent), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "grab_notify", + g_signal_connect(G_OBJECT(window_contents_), "grab_notify", G_CALLBACK(CallGrabNotify), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "focus_out_event", - G_CALLBACK(CallFocusOut), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "key_press_event", + g_signal_connect(G_OBJECT(window_contents_), "key_press_event", G_CALLBACK(CallKeyPress), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "key_release_event", + g_signal_connect(G_OBJECT(window_contents_), "key_release_event", G_CALLBACK(CallKeyRelease), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "scroll_event", + g_signal_connect(G_OBJECT(window_contents_), "scroll_event", G_CALLBACK(CallScroll), NULL); - g_signal_connect(G_OBJECT(child_widget_parent_), "visibility_notify_event", + g_signal_connect(G_OBJECT(window_contents_), "visibility_notify_event", G_CALLBACK(CallVisibilityNotify), NULL); // In order to receive notification when the window is no longer the front @@ -167,10 +171,12 @@ void WidgetGtk::Init(GtkWidget* parent, tooltip_manager_.reset(new TooltipManagerGtk(this)); if (type_ == TYPE_CHILD) { - WidgetGtk* parent_widget = GetViewForNative(parent); - parent_widget->AddChild(widget_); - parent_widget->PositionChild(widget_, bounds.x(), bounds.y(), - bounds.width(), bounds.height()); + if (parent) { + WidgetGtk* parent_widget = GetViewForNative(parent); + parent_widget->AddChild(widget_); + parent_widget->PositionChild(widget_, bounds.x(), bounds.y(), + bounds.width(), bounds.height()); + } } else { if (bounds.width() > 0 && bounds.height() > 0) gtk_window_resize(GTK_WINDOW(widget_), bounds.width(), bounds.height()); @@ -199,11 +205,15 @@ bool WidgetGtk::MakeTransparent() { } void WidgetGtk::AddChild(GtkWidget* child) { - gtk_container_add(GTK_CONTAINER(child_widget_parent_), child); + gtk_container_add(GTK_CONTAINER(window_contents_), child); } void WidgetGtk::RemoveChild(GtkWidget* child) { - gtk_container_remove(GTK_CONTAINER(child_widget_parent_), child); + gtk_container_remove(GTK_CONTAINER(window_contents_), child); +} + +void WidgetGtk::ReparentChild(GtkWidget* child) { + gtk_widget_reparent(child, window_contents_); } void WidgetGtk::PositionChild(GtkWidget* child, int x, int y, int w, int h) { @@ -211,7 +221,7 @@ void WidgetGtk::PositionChild(GtkWidget* child, int x, int y, int w, int h) { // For some reason we need to do both of these to size a widget. gtk_widget_size_allocate(child, &alloc); gtk_widget_set_size_request(child, w, h); - gtk_fixed_move(GTK_FIXED(child_widget_parent_), child, x, y); + gtk_fixed_move(GTK_FIXED(window_contents_), child, x, y); } void WidgetGtk::SetContentsView(View* view) { @@ -389,10 +399,17 @@ void WidgetGtk::DidProcessEvent(GdkEvent* event) { //////////////////////////////////////////////////////////////////////////////// // TODO(beng): organize into sections: -void WidgetGtk::CreateGtkWidget() { +void WidgetGtk::CreateGtkWidget(GtkWidget* parent) { if (type_ == TYPE_CHILD) { - child_widget_parent_ = widget_ = gtk_fixed_new(); + window_contents_ = widget_ = gtk_fixed_new(); gtk_fixed_set_has_window(GTK_FIXED(widget_), true); + if (!parent && !null_parent_) { + GtkWidget* popup = gtk_window_new(GTK_WINDOW_POPUP); + null_parent_ = gtk_fixed_new(); + gtk_container_add(GTK_CONTAINER(popup), null_parent_); + gtk_widget_realize(null_parent_); + } + gtk_container_add(GTK_CONTAINER(parent ? parent : null_parent_), widget_); SetViewForNative(widget_, this); } else { widget_ = gtk_window_new( @@ -403,16 +420,15 @@ void WidgetGtk::CreateGtkWidget() { 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); + window_contents_ = gtk_fixed_new(); + gtk_fixed_set_has_window(GTK_FIXED(window_contents_), true); + gtk_container_add(GTK_CONTAINER(widget_), window_contents_); + gtk_widget_show(window_contents_); + SetViewForNative(window_contents_, this); if (transparent_) ConfigureWidgetForTransparentBackground(); } - // The widget needs to be realized before handlers like size-allocate can // function properly. gtk_widget_realize(widget_); @@ -500,12 +516,12 @@ gboolean WidgetGtk::OnGrabBrokeEvent(GtkWidget* widget, GdkEvent* event) { } void WidgetGtk::OnGrabNotify(GtkWidget* widget, gboolean was_grabbed) { - gtk_grab_remove(child_widget_parent_); + gtk_grab_remove(window_contents_); HandleGrabBroke(); } void WidgetGtk::OnDestroy(GtkWidget* widget) { - widget_ = child_widget_parent_ = NULL; + widget_ = window_contents_ = NULL; root_view_->OnWidgetDestroyed(); if (delete_on_destroy_) delete this; @@ -564,7 +580,7 @@ bool WidgetGtk::ProcessMousePressed(GdkEventButton* event) { is_mouse_down_ = true; if (!has_capture_) { has_capture_ = true; - gtk_grab_add(child_widget_parent_); + gtk_grab_add(window_contents_); } return true; } @@ -581,7 +597,7 @@ void WidgetGtk::ProcessMouseReleased(GdkEventButton* event) { if (has_capture_ && ReleaseCaptureOnMouseReleased()) { has_capture_ = false; - gtk_grab_remove(child_widget_parent_); + gtk_grab_remove(window_contents_); } is_mouse_down_ = false; root_view_->OnMouseReleased(mouse_up, false); @@ -763,8 +779,7 @@ Window* WidgetGtk::GetWindowImpl(GtkWidget* widget) { } void WidgetGtk::ConfigureWidgetForTransparentBackground() { - DCHECK(widget_ && child_widget_parent_ && - widget_ != child_widget_parent_); + DCHECK(widget_ && window_contents_ && widget_ != window_contents_); GdkColormap* rgba_colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default()); @@ -785,12 +800,12 @@ void WidgetGtk::ConfigureWidgetForTransparentBackground() { // Widget must be realized before setting pixmap. gdk_window_set_back_pixmap(widget_->window, NULL, FALSE); - gtk_widget_set_colormap(child_widget_parent_, rgba_colormap); - gtk_widget_set_app_paintable(child_widget_parent_, true); - GTK_WIDGET_UNSET_FLAGS(child_widget_parent_, GTK_DOUBLE_BUFFERED); - gtk_widget_realize(child_widget_parent_); + gtk_widget_set_colormap(window_contents_, rgba_colormap); + gtk_widget_set_app_paintable(window_contents_, true); + GTK_WIDGET_UNSET_FLAGS(window_contents_, GTK_DOUBLE_BUFFERED); + gtk_widget_realize(window_contents_); // Widget must be realized before setting pixmap. - gdk_window_set_back_pixmap(child_widget_parent_->window, NULL, FALSE); + gdk_window_set_back_pixmap(window_contents_->window, NULL, FALSE); } void WidgetGtk::HandleGrabBroke() { diff --git a/views/widget/widget_gtk.h b/views/widget/widget_gtk.h index 2442f74..b5dd6a0 100644 --- a/views/widget/widget_gtk.h +++ b/views/widget/widget_gtk.h @@ -56,15 +56,25 @@ class WidgetGtk : public Widget, public MessageLoopForUI::Observer { delete_on_destroy_ = delete_on_destroy; } + // Adds and removes the specified widget as a child of this widget's contents. + // These methods make sure to add the widget to the window's contents + // container if this widget is a window. void AddChild(GtkWidget* child); void RemoveChild(GtkWidget* child); + // A safe way to reparent a child widget to this widget. Calls + // gtk_widget_reparent which handles refcounting to avoid destroying the + // widget when removing it from its old parent. + void ReparentChild(GtkWidget* child); + // Positions a child GtkWidget at the specified location and bounds. void PositionChild(GtkWidget* child, int x, int y, int w, int h); - // Parent GtkWidget all children are added to. This is not necessarily - // the same as returned by GetNativeView. - GtkWidget* child_widget_parent() const { return child_widget_parent_; } + // Parent GtkWidget all children are added to. When this WidgetGtk corresponds + // to a top level window, this is the GtkFixed within the GtkWindow, not the + // GtkWindow itself. For child widgets, this is the same GtkFixed as + // |widget_|. + GtkWidget* window_contents() const { return window_contents_; } virtual void SetContentsView(View* view); @@ -174,7 +184,10 @@ class WidgetGtk : public Widget, public MessageLoopForUI::Observer { static Window* GetWindowImpl(GtkWidget* widget); // Creates the GtkWidget. - void CreateGtkWidget(); + void CreateGtkWidget(GtkWidget* parent); + + // Attaches the widget contents to the window's widget. + void AttachGtkWidgetToWindow(); // Invoked from create widget to enable the various bits needed for a // transparent background. This is only invoked if MakeTransparent has been @@ -186,10 +199,15 @@ class WidgetGtk : public Widget, public MessageLoopForUI::Observer { const Type type_; // Our native views. If we're a window/popup, then widget_ is the window and - // child_widget_parent_ is a GtkFixed. If we're not a window/popup, then - // widget_ and child_widget_parent_ are a GtkFixed. + // window_contents_ is a GtkFixed. If we're not a window/popup, then widget_ + // and window_contents_ point to the same GtkFixed. GtkWidget* widget_; - GtkWidget* child_widget_parent_; + GtkWidget* window_contents_; + + // Child GtkWidgets created with no parent need to be parented to a valid top + // level window otherwise Gtk throws a fit. |null_parent_| is an invisible + // popup that such GtkWidgets are parented to. + static GtkWidget* null_parent_; // The root of the View hierarchy attached to this window. scoped_ptr<RootView> root_view_; |