diff options
author | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-28 19:22:34 +0000 |
---|---|---|
committer | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-28 19:22:34 +0000 |
commit | 3a69f0af0b98c8373fd6b5997ce37efd31baa8dd (patch) | |
tree | f954ec0d0c56a53b20fe27640a674698a96f28ff /chrome/browser/gtk | |
parent | 5beb105cef4ede484df49a02cfc7984ac053b225 (diff) | |
download | chromium_src-3a69f0af0b98c8373fd6b5997ce37efd31baa8dd.zip chromium_src-3a69f0af0b98c8373fd6b5997ce37efd31baa8dd.tar.gz chromium_src-3a69f0af0b98c8373fd6b5997ce37efd31baa8dd.tar.bz2 |
GTK: Implement GtkFloatingContainer and implement StatusBubble on top of it.
This introduces a hybrid GtkBin/GtkFixed container which exposes a signal to absolutely position widgets. This also fixes the current flickering issues with the status bubble.
http://crbug.com/11635
TEST=Goto a site with a long list of links (I used reddit.com) and move the mouse cursor up and down the list quickly. There shouldn't be flickering in the top left corner.
Review URL: http://codereview.chromium.org/115835
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17095 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/gtk')
-rw-r--r-- | chrome/browser/gtk/gtk_floating_container.cc | 320 | ||||
-rw-r--r-- | chrome/browser/gtk/gtk_floating_container.h | 85 | ||||
-rw-r--r-- | chrome/browser/gtk/status_bubble_gtk.cc | 43 | ||||
-rw-r--r-- | chrome/browser/gtk/status_bubble_gtk.h | 18 | ||||
-rw-r--r-- | chrome/browser/gtk/tab_contents_container_gtk.cc | 79 | ||||
-rw-r--r-- | chrome/browser/gtk/tab_contents_container_gtk.h | 27 |
6 files changed, 489 insertions, 83 deletions
diff --git a/chrome/browser/gtk/gtk_floating_container.cc b/chrome/browser/gtk/gtk_floating_container.cc new file mode 100644 index 0000000..51f5e14 --- /dev/null +++ b/chrome/browser/gtk/gtk_floating_container.cc @@ -0,0 +1,320 @@ +// 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/gtk/gtk_floating_container.h" + +#include <gtk/gtk.h> +#include <gtk/gtkprivate.h> +#include <gtk/gtkmarshal.h> + +#include <algorithm> + +namespace { + +enum { + SET_FLOATING_POSITION, + LAST_SIGNAL +}; + +enum { + CHILD_PROP_0, + CHILD_PROP_X, + CHILD_PROP_Y +}; + +// Returns the GtkFloatingContainerChild associated with |widget| (or NULL if +// |widget| not found). +GtkFloatingContainerChild* GetChild(GtkFloatingContainer* container, + GtkWidget* widget) { + for (GList* floating_children = container->floating_children; + floating_children; floating_children = g_list_next(floating_children)) { + GtkFloatingContainerChild* child = + reinterpret_cast<GtkFloatingContainerChild*>(floating_children->data); + + if (child->widget == widget) + return child; + } + + return NULL; +} + +} // namespace + +G_BEGIN_DECLS + +static void gtk_floating_container_remove(GtkContainer* container, + GtkWidget* widget); +static void gtk_floating_container_forall(GtkContainer* container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); +static void gtk_floating_container_size_request(GtkWidget* widget, + GtkRequisition* requisition); +static void gtk_floating_container_size_allocate(GtkWidget* widget, + GtkAllocation* allocation); +static void gtk_floating_container_set_child_property(GtkContainer* container, + GtkWidget* child, + guint property_id, + const GValue* value, + GParamSpec* pspec); +static void gtk_floating_container_get_child_property(GtkContainer* container, + GtkWidget* child, + guint property_id, + GValue* value, + GParamSpec* pspec); + +static guint floating_container_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE(GtkFloatingContainer, gtk_floating_container, GTK_TYPE_BIN) + +static void gtk_floating_container_class_init( + GtkFloatingContainerClass *klass) { + GtkObjectClass* object_class = + reinterpret_cast<GtkObjectClass*>(klass); + + GtkWidgetClass* widget_class = + reinterpret_cast<GtkWidgetClass*>(klass); + widget_class->size_request = gtk_floating_container_size_request; + widget_class->size_allocate = gtk_floating_container_size_allocate; + + GtkContainerClass* container_class = + reinterpret_cast<GtkContainerClass*>(klass); + container_class->remove = gtk_floating_container_remove; + container_class->forall = gtk_floating_container_forall; + + container_class->set_child_property = + gtk_floating_container_set_child_property; + container_class->get_child_property = + gtk_floating_container_get_child_property; + + gtk_container_class_install_child_property( + container_class, + CHILD_PROP_X, + g_param_spec_int("x", + "X position", + "X position of child widget", + G_MININT, + G_MAXINT, + 0, + static_cast<GParamFlags>(GTK_PARAM_READWRITE))); + + gtk_container_class_install_child_property( + container_class, + CHILD_PROP_Y, + g_param_spec_int("y", + "Y position", + "Y position of child widget", + G_MININT, + G_MAXINT, + 0, + static_cast<GParamFlags>(GTK_PARAM_READWRITE))); + + floating_container_signals[SET_FLOATING_POSITION] = + g_signal_new("set-floating-position", + G_OBJECT_CLASS_TYPE(object_class), + static_cast<GSignalFlags>(G_SIGNAL_RUN_FIRST | + G_SIGNAL_ACTION), + NULL, + NULL, NULL, + gtk_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE); +} + +static void gtk_floating_container_init(GtkFloatingContainer* container) { + GTK_WIDGET_SET_FLAGS(container, GTK_NO_WINDOW); + + container->floating_children = NULL; +} + +static void gtk_floating_container_remove(GtkContainer* container, + GtkWidget* widget) { + g_return_if_fail(GTK_IS_WIDGET(widget)); + + GtkBin* bin = GTK_BIN(container); + if (bin->child == widget) { + ((GTK_CONTAINER_CLASS(gtk_floating_container_parent_class))->remove) + (container, widget); + } else { + // Handle the other case where it's in our |floating_children| list. + GtkFloatingContainer* floating = GTK_FLOATING_CONTAINER(container); + GList* children = floating->floating_children; + gboolean removed_child = false; + while (children) { + GtkFloatingContainerChild* child = + reinterpret_cast<GtkFloatingContainerChild*>(children->data); + + if (child->widget == widget) { + removed_child = true; + gboolean was_visible = GTK_WIDGET_VISIBLE(widget); + + gtk_widget_unparent(widget); + + floating->floating_children = + g_list_remove_link(floating->floating_children, children); + g_list_free(children); + g_free(child); + + if (was_visible && GTK_WIDGET_VISIBLE(container)) + gtk_widget_queue_resize(GTK_WIDGET(container)); + + break; + } + children = children->next; + } + + g_return_if_fail(removed_child); + } +} + +static void gtk_floating_container_forall(GtkContainer* container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) { + GtkBin *bin = GTK_BIN(container); + + g_return_if_fail(callback != NULL); + + if (bin->child) + (*callback)(bin->child, callback_data); + + GtkFloatingContainer* floating = GTK_FLOATING_CONTAINER(container); + GList* children = floating->floating_children; + while (children) { + GtkFloatingContainerChild* child = + reinterpret_cast<GtkFloatingContainerChild*>(children->data); + children = children->next; + + (*callback)(child->widget, callback_data); + } +} + +static void gtk_floating_container_size_request(GtkWidget* widget, + GtkRequisition* requisition) { + GtkBin *bin = GTK_BIN(widget); + if (bin && bin->child) { + gtk_widget_size_request(bin->child, requisition); + } else { + requisition->width = 0; + requisition->height = 0; + } +} + +static void gtk_floating_container_size_allocate(GtkWidget* widget, + GtkAllocation* allocation) { + widget->allocation = *allocation; + + if (!GTK_WIDGET_NO_WINDOW(widget) && GTK_WIDGET_REALIZED(widget)) { + gdk_window_move_resize(widget->window, + allocation->x, + allocation->y, + allocation->width, + allocation->height); + } + + // Give the same allocation to our GtkBin component. + GtkBin* bin = GTK_BIN(widget); + if (bin->child) { + gtk_widget_size_allocate(bin->child, allocation); + } + + // We need to give whoever is pulling our strings a chance to set the "x" and + // "y" properties on all of our children. + g_signal_emit(widget, floating_container_signals[SET_FLOATING_POSITION], 0, + allocation); + + // Our allocation has been set. We've asked our controller to place the other + // widgets. Pass out allocations to all our children based on where they want + // to be. + GtkFloatingContainer* container = GTK_FLOATING_CONTAINER(widget); + GList* children = container->floating_children; + GtkAllocation child_allocation; + GtkRequisition child_requisition; + while (children) { + GtkFloatingContainerChild* child = + reinterpret_cast<GtkFloatingContainerChild*>(children->data); + children = children->next; + + if (GTK_WIDGET_VISIBLE(child->widget)) { + gtk_widget_size_request(child->widget, &child_requisition); + child_allocation.x = child->x; + child_allocation.y = child->y; + child_allocation.width = std::max(1, std::min(child_requisition.width, + allocation->width)); + child_allocation.height = std::max(1, std::min(child_requisition.height, + allocation->height)); + gtk_widget_size_allocate(child->widget, &child_allocation); + } + } +} + +static void gtk_floating_container_set_child_property(GtkContainer* container, + GtkWidget* child, + guint property_id, + const GValue* value, + GParamSpec* pspec) { + GtkFloatingContainerChild* floating_child = + GetChild(GTK_FLOATING_CONTAINER(container), child); + g_return_if_fail(floating_child); + + switch (property_id) { + case CHILD_PROP_X: + floating_child->x = g_value_get_int(value); + gtk_widget_child_notify(child, "x"); + break; + case CHILD_PROP_Y: + floating_child->y = g_value_get_int(value); + gtk_widget_child_notify(child, "y"); + break; + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID( + container, property_id, pspec); + break; + }; +} + +static void gtk_floating_container_get_child_property(GtkContainer* container, + GtkWidget* child, + guint property_id, + GValue* value, + GParamSpec* pspec) { + GtkFloatingContainerChild* floating_child = + GetChild(GTK_FLOATING_CONTAINER(container), child); + g_return_if_fail(floating_child); + + switch (property_id) { + case CHILD_PROP_X: + g_value_set_int(value, floating_child->x); + break; + case CHILD_PROP_Y: + g_value_set_int(value, floating_child->y); + break; + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID( + container, property_id, pspec); + break; + }; +} + +GtkWidget* gtk_floating_container_new() { + return GTK_WIDGET(g_object_new(GTK_TYPE_FLOATING_CONTAINER, NULL)); +} + +void gtk_floating_container_add_floating(GtkFloatingContainer* container, + GtkWidget* widget) { + g_return_if_fail(GTK_IS_FLOATING_CONTAINER(container)); + g_return_if_fail(GTK_IS_WIDGET(widget)); + + GtkFloatingContainerChild* child_info = g_new(GtkFloatingContainerChild, 1); + child_info->widget = widget; + child_info->x = 0; + child_info->y = 0; + + gtk_widget_set_parent(widget, GTK_WIDGET(container)); + + container->floating_children = + g_list_append(container->floating_children, child_info); +} + +G_END_DECLS diff --git a/chrome/browser/gtk/gtk_floating_container.h b/chrome/browser/gtk/gtk_floating_container.h new file mode 100644 index 0000000..b2f4661 --- /dev/null +++ b/chrome/browser/gtk/gtk_floating_container.h @@ -0,0 +1,85 @@ +// 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_GTK_GTK_FLOATING_CONTAINER_H_ +#define CHROME_BROWSER_GTK_GTK_FLOATING_CONTAINER_H_ + +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +// A specialized container, which is a cross between a GtkBin and a +// GtkFixed. This container dervies from GtkBin and the implementation of +// gtk_container_add() is the same: only one GtkWidget can be added through +// that interface. The GtkBin portion contains normal content and is given the +// same allocation that this container has. +// +// In addition, any number of widgets can be through the +// gtk_floating_container_add_floating() method, which provides functionality +// similar to a GtkFixed. Unlike a GtkFixed, coordinates are not set when you +// gtk_fixed_put(). The location of the floating widgets is determined while +// running the "set-floating-position" signal, which is emitted during this +// container's "size-allocate" handler. +// +// The "set-floating-position" signal is (semi-)mandatory if you want widgets +// placed anywhere other than the origin and should have the following +// signature: +// +// void (*set_floating_position)(GtkFloatingContainer* container, +// GtkAllocation* allocation); +// +// Your handler should, for each floating widget, set the "x" and "y" child +// properties. + +G_BEGIN_DECLS + +#define GTK_TYPE_FLOATING_CONTAINER \ + (gtk_floating_container_get_type()) +#define GTK_FLOATING_CONTAINER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_FLOATING_CONTAINER, \ + GtkFloatingContainer)) +#define GTK_FLOATING_CONTAINER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_FLOATING_CONTAINER, \ + GtkFloatingContainerClass)) +#define GTK_IS_FLOATING_CONTAINER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_FLOATING_CONTAINER)) +#define GTK_IS_FLOATING_CONTAINER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_FLOATING_CONTAINER)) +#define GTK_FLOATING_CONTAINER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_FLOATING_CONTAINER, \ + GtkFloatingContainerClass)) + +typedef struct _GtkFloatingContainer GtkFloatingContainer; +typedef struct _GtkFloatingContainerClass GtkFloatingContainerClass; +typedef struct _GtkFloatingContainerChild GtkFloatingContainerChild; + +struct _GtkFloatingContainer { + // Parent class. + GtkBin bin; + + // A GList of all our floating children, in GtkFloatingContainerChild + // structs. Owned by the GtkFloatingContainer. + GList* floating_children; +}; + +struct _GtkFloatingContainerClass { + GtkBinClass parent_class; +}; + +// Internal structure used to associate a widget and its x/y child properties. +struct _GtkFloatingContainerChild { + GtkWidget* widget; + gint x; + gint y; +}; + +GType gtk_floating_container_get_type() G_GNUC_CONST; +GtkWidget* gtk_floating_container_new(); +void gtk_floating_container_add_floating(GtkFloatingContainer* container, + GtkWidget* widget); +// Use gtk_container_remove to remove all widgets; both widgets added with +// gtk_container_add() and gtk_floating_container_add_floating(). + +G_END_DECLS + +#endif // CHROME_BROWSER_GTK_GTK_FLOATING_CONTAINER_H_ diff --git a/chrome/browser/gtk/status_bubble_gtk.cc b/chrome/browser/gtk/status_bubble_gtk.cc index abdacba..041d6b9 100644 --- a/chrome/browser/gtk/status_bubble_gtk.cc +++ b/chrome/browser/gtk/status_bubble_gtk.cc @@ -32,8 +32,7 @@ static const int kHideDelay = 250; } // namespace StatusBubbleGtk::StatusBubbleGtk() - : parent_(NULL), - timer_factory_(this) { + : timer_factory_(this) { InitWidgets(); } @@ -56,13 +55,6 @@ void StatusBubbleGtk::SetStatus(const std::wstring& status) { SetStatus(WideToUTF8(status)); } -void StatusBubbleGtk::SetParentAllocation( - GtkWidget* parent, GtkAllocation* allocation) { - parent_ = parent; - parent_allocation_ = *allocation; - SetStatusBubbleSize(); -} - void StatusBubbleGtk::SetURL(const GURL& url, const std::wstring& languages) { SetStatus(url.possibly_invalid_spec()); } @@ -71,7 +63,6 @@ void StatusBubbleGtk::Show() { // If we were going to hide, stop. timer_factory_.RevokeAll(); - SetStatusBubbleSize(); gtk_widget_show_all(container_.get()); if (container_.get()->window) @@ -91,38 +82,6 @@ void StatusBubbleGtk::HideInASecond() { kHideDelay); } -void StatusBubbleGtk::SetStatusBubbleSize() { - if (parent_) { - GtkRequisition requisition; - gtk_widget_size_request(container_.get(), &requisition); - - // TODO(erg): Previously, I put a call to gtk_fixed_put() here. It appears - // that doing this sets off a size-allocate storm, since gtk_fixed_put() - // calls gtk_widget_queue_resize on the GtkFixed that caused this message. - // The real solution may be creating a subclass of GtkVBox that has extra - // code to deal with floating widgets, but this hack is good enough for - // Friday. evanm says that there's a a GtkFixed subclass in test_shell that - // we'll be stealing for plugin support anyway that should also do the same - // task. - - GtkAllocation widget_allocation; - int child_y = std::max( - parent_allocation_.y + parent_allocation_.height - requisition.height, - 0); - widget_allocation.x = 0; - widget_allocation.y = child_y; - widget_allocation.width = std::max(1, std::min(requisition.width, - parent_allocation_.width)); - widget_allocation.height = std::max(1, requisition.height); - - if (memcmp(&widget_allocation, &container_.get()->allocation, - sizeof widget_allocation) != 0) { - // Only do something when we are actually changing sizes. - gtk_widget_size_allocate(container_.get(), &widget_allocation); - } - } -} - void StatusBubbleGtk::MouseMoved() { // We can't do that fancy sliding behaviour where the status bubble slides // out of the window because the window manager gets in the way. So totally diff --git a/chrome/browser/gtk/status_bubble_gtk.h b/chrome/browser/gtk/status_bubble_gtk.h index e4fb4c3..5de672b 100644 --- a/chrome/browser/gtk/status_bubble_gtk.h +++ b/chrome/browser/gtk/status_bubble_gtk.h @@ -38,11 +38,6 @@ class StatusBubbleGtk : public StatusBubble { void SetStatus(const std::string& status_utf8); - // Notification from our parent GtkFixed about its size. |allocation| is the - // size of our |parent| GtkFixed, and we use it to position our status bubble - // directly on top of the current render view. - void SetParentAllocation(GtkWidget* parent, GtkAllocation* allocation); - // Top of the widget hierarchy for a StatusBubble. This top level widget is // guarenteed to have its gtk_widget_name set to "status-bubble" for // identification. @@ -59,25 +54,12 @@ class StatusBubbleGtk : public StatusBubble { // Builds the widgets, containers, etc. void InitWidgets(); - // An ad hoc, informally-specified, bug-ridden, slow implementation of half - // of GTK's requisition/allocation system. We use this to position the status - // bubble on top of our parent GtkFixed. - void SetStatusBubbleSize(); - // A GtkAlignment that is the child of |slide_widget_|. OwnedWidgetGtk container_; // The GtkLabel holding the text. GtkWidget* label_; - // Our parent GtkFixed. (We don't own this; we only keep a reference as we - // set our own size by notifying |parent_| of our desired size.) - GtkWidget* parent_; - - // |parent_|'s GtkAllocation. We make sure that |container_| lives along the - // bottom of this and doesn't protrude. - GtkAllocation parent_allocation_; - // A timer that hides our window after a delay. ScopedRunnableMethodFactory<StatusBubbleGtk> timer_factory_; }; diff --git a/chrome/browser/gtk/tab_contents_container_gtk.cc b/chrome/browser/gtk/tab_contents_container_gtk.cc index 7b4e6b4..41d2d9a 100644 --- a/chrome/browser/gtk/tab_contents_container_gtk.cc +++ b/chrome/browser/gtk/tab_contents_container_gtk.cc @@ -5,6 +5,7 @@ #include "chrome/browser/gtk/tab_contents_container_gtk.h" #include "base/gfx/native_widget_types.h" +#include "chrome/browser/gtk/gtk_floating_container.h" #include "chrome/browser/gtk/status_bubble_gtk.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" @@ -13,16 +14,14 @@ namespace { // Allocates all normal tab contents views to the size of the passed in -// |allocation|. Ignores StatusBubbles, which are handled separately. +// |allocation|. void ResizeChildren(GtkWidget* widget, void* param) { GtkAllocation* allocation = reinterpret_cast<GtkAllocation*>(param); - if (strcmp(gtk_widget_get_name(widget), "status-bubble") != 0) { - if (widget->allocation.width != allocation->width || - widget->allocation.height != allocation->height) { - gtk_widget_set_size_request(widget, allocation->width, - allocation->height); - } + if (widget->allocation.width != allocation->width || + widget->allocation.height != allocation->height) { + gtk_widget_set_size_request(widget, allocation->width, + allocation->height); } } @@ -30,21 +29,45 @@ void ResizeChildren(GtkWidget* widget, void* param) { TabContentsContainerGtk::TabContentsContainerGtk(StatusBubbleGtk* status_bubble) : tab_contents_(NULL), - status_bubble_(status_bubble), - fixed_(gtk_fixed_new()) { - gtk_fixed_put(GTK_FIXED(fixed_), status_bubble->widget(), 0, 0); + status_bubble_(status_bubble) { + Init(); +} + +TabContentsContainerGtk::~TabContentsContainerGtk() { +} +void TabContentsContainerGtk::Init() { + // A high level overview of the TabContentsContainer: + // + // +- GtkFloatingContainer |floating_| -------------------------------+ + // |+- GtkFixedContainer |fixed_| -----------------------------------+| + // || || + // || || + // || || + // || || + // |+- (StatusBubble) ------+ +- (Popups) ------------+| + // |+ +----------------+ || + // |+-----------------------+ +-----------------------+| + // +------------------------------------------------------------------+ + + floating_ = gtk_floating_container_new(); + + fixed_ = gtk_fixed_new(); g_signal_connect(fixed_, "size-allocate", G_CALLBACK(OnFixedSizeAllocate), this); + gtk_container_add(GTK_CONTAINER(floating_), fixed_); - gtk_widget_show(fixed_); -} + gtk_floating_container_add_floating(GTK_FLOATING_CONTAINER(floating_), + status_bubble_->widget()); + g_signal_connect(floating_, "set-floating-position", + G_CALLBACK(OnSetFloatingPosition), this); -TabContentsContainerGtk::~TabContentsContainerGtk() { + gtk_widget_show(fixed_); + gtk_widget_show(floating_); } void TabContentsContainerGtk::AddContainerToBox(GtkWidget* box) { - gtk_box_pack_start(GTK_BOX(box), fixed_, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(box), floating_, TRUE, TRUE, 0); } void TabContentsContainerGtk::SetTabContents(TabContents* tab_contents) { @@ -140,7 +163,31 @@ void TabContentsContainerGtk::OnFixedSizeAllocate( TabContentsContainerGtk* container) { // Set all the tab contents GtkWidgets to the size of the allocation. gtk_container_foreach(GTK_CONTAINER(fixed), ResizeChildren, allocation); +} - // Tell the status bubble about how large it can be. - container->status_bubble_->SetParentAllocation(fixed, allocation); +// static +void TabContentsContainerGtk::OnSetFloatingPosition( + GtkFloatingContainer* floating_container, GtkAllocation* allocation, + TabContentsContainerGtk* tab_contents_container) { + GtkWidget* widget = tab_contents_container->status_bubble_->widget(); + + // Look at the size request of the status bubble and tell the + // GtkFloatingContainer where we want it positioned. + GtkRequisition requisition; + gtk_widget_size_request(widget, &requisition); + + GValue value = { 0, }; + g_value_init(&value, G_TYPE_INT); + g_value_set_int(&value, 0); + // TODO(erg): Since we're absolutely positioning stuff, we probably have to + // do manual RTL right here. + gtk_container_child_set_property(GTK_CONTAINER(floating_container), + widget, "x", &value); + + int child_y = std::max( + allocation->y + allocation->height - requisition.height, 0); + g_value_set_int(&value, child_y); + gtk_container_child_set_property(GTK_CONTAINER(floating_container), + widget, "y", &value); + g_value_unset(&value); } diff --git a/chrome/browser/gtk/tab_contents_container_gtk.h b/chrome/browser/gtk/tab_contents_container_gtk.h index 457748c..d6df96c 100644 --- a/chrome/browser/gtk/tab_contents_container_gtk.h +++ b/chrome/browser/gtk/tab_contents_container_gtk.h @@ -14,11 +14,15 @@ class RenderViewHost; class StatusBubbleGtk; class TabContents; +typedef struct _GtkFloatingContainer GtkFloatingContainer; + class TabContentsContainerGtk : public NotificationObserver { public: explicit TabContentsContainerGtk(StatusBubbleGtk* status_bubble); ~TabContentsContainerGtk(); + void Init(); + // Inserts our GtkWidget* hierarchy into a GtkBox managed by our owner. void AddContainerToBox(GtkWidget* widget); @@ -58,6 +62,12 @@ class TabContentsContainerGtk : public NotificationObserver { GtkAllocation* allocation, TabContentsContainerGtk* container); + // Handler for |floating_|'s "set-floating-position" signal. During this + // callback, we manually set the position of the status bubble. + static void OnSetFloatingPosition( + GtkFloatingContainer* container, GtkAllocation* allocation, + TabContentsContainerGtk* tab_contents_container); + NotificationRegistrar registrar_; // The currently visible TabContents. @@ -66,13 +76,16 @@ class TabContentsContainerGtk : public NotificationObserver { // The status bubble manager. Always non-NULL. StatusBubbleGtk* status_bubble_; - // We keep a GtkFixed which is inserted into this object's owner's GtkWidget - // hierarchy. We then insert and remove TabContents GtkWidgets into this - // fixed_. This should not be a GtkVBox since there were errors with timing - // where the vbox was horizontally split with the top half displaying the - // current TabContents and bottom half displaying the loading page. In - // addition, we need to position the status bubble on top of the currently - // displayed TabContents so we put that in this part of the hierarchy. + // Top of the TabContentsContainerGtk widget hierarchy. A cross between a + // GtkBin and a GtkFixed, |floating_| has |fixed_| as its one "real" child, + // and the various things that hang off the bottom (status bubble, etc) have + // their positions manually set in OnSetFloatingPosition. + GtkWidget* floating_; + + // We insert and remove TabContents GtkWidgets into this fixed_. This should + // not be a GtkVBox since there were errors with timing where the vbox was + // horizontally split with the top half displaying the current TabContents + // and bottom half displaying the loading page. GtkWidget* fixed_; DISALLOW_COPY_AND_ASSIGN(TabContentsContainerGtk); |