summaryrefslogtreecommitdiffstats
path: root/chrome/browser/gtk
diff options
context:
space:
mode:
authorerg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-28 19:22:34 +0000
committererg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-28 19:22:34 +0000
commit3a69f0af0b98c8373fd6b5997ce37efd31baa8dd (patch)
treef954ec0d0c56a53b20fe27640a674698a96f28ff /chrome/browser/gtk
parent5beb105cef4ede484df49a02cfc7984ac053b225 (diff)
downloadchromium_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.cc320
-rw-r--r--chrome/browser/gtk/gtk_floating_container.h85
-rw-r--r--chrome/browser/gtk/status_bubble_gtk.cc43
-rw-r--r--chrome/browser/gtk/status_bubble_gtk.h18
-rw-r--r--chrome/browser/gtk/tab_contents_container_gtk.cc79
-rw-r--r--chrome/browser/gtk/tab_contents_container_gtk.h27
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);