Commit 3a69f0af authored by erg@google.com's avatar erg@google.com

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
parent 5beb105c
This diff is collapsed.
// 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_
...@@ -32,8 +32,7 @@ static const int kHideDelay = 250; ...@@ -32,8 +32,7 @@ static const int kHideDelay = 250;
} // namespace } // namespace
StatusBubbleGtk::StatusBubbleGtk() StatusBubbleGtk::StatusBubbleGtk()
: parent_(NULL), : timer_factory_(this) {
timer_factory_(this) {
InitWidgets(); InitWidgets();
} }
...@@ -56,13 +55,6 @@ void StatusBubbleGtk::SetStatus(const std::wstring& status) { ...@@ -56,13 +55,6 @@ void StatusBubbleGtk::SetStatus(const std::wstring& status) {
SetStatus(WideToUTF8(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) { void StatusBubbleGtk::SetURL(const GURL& url, const std::wstring& languages) {
SetStatus(url.possibly_invalid_spec()); SetStatus(url.possibly_invalid_spec());
} }
...@@ -71,7 +63,6 @@ void StatusBubbleGtk::Show() { ...@@ -71,7 +63,6 @@ void StatusBubbleGtk::Show() {
// If we were going to hide, stop. // If we were going to hide, stop.
timer_factory_.RevokeAll(); timer_factory_.RevokeAll();
SetStatusBubbleSize();
gtk_widget_show_all(container_.get()); gtk_widget_show_all(container_.get());
if (container_.get()->window) if (container_.get()->window)
...@@ -91,38 +82,6 @@ void StatusBubbleGtk::HideInASecond() { ...@@ -91,38 +82,6 @@ void StatusBubbleGtk::HideInASecond() {
kHideDelay); 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() { void StatusBubbleGtk::MouseMoved() {
// We can't do that fancy sliding behaviour where the status bubble slides // 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 // out of the window because the window manager gets in the way. So totally
......
...@@ -38,11 +38,6 @@ class StatusBubbleGtk : public StatusBubble { ...@@ -38,11 +38,6 @@ class StatusBubbleGtk : public StatusBubble {
void SetStatus(const std::string& status_utf8); 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 // 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 // guarenteed to have its gtk_widget_name set to "status-bubble" for
// identification. // identification.
...@@ -59,25 +54,12 @@ class StatusBubbleGtk : public StatusBubble { ...@@ -59,25 +54,12 @@ class StatusBubbleGtk : public StatusBubble {
// Builds the widgets, containers, etc. // Builds the widgets, containers, etc.
void InitWidgets(); 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_|. // A GtkAlignment that is the child of |slide_widget_|.
OwnedWidgetGtk container_; OwnedWidgetGtk container_;
// The GtkLabel holding the text. // The GtkLabel holding the text.
GtkWidget* label_; 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. // A timer that hides our window after a delay.
ScopedRunnableMethodFactory<StatusBubbleGtk> timer_factory_; ScopedRunnableMethodFactory<StatusBubbleGtk> timer_factory_;
}; };
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "chrome/browser/gtk/tab_contents_container_gtk.h" #include "chrome/browser/gtk/tab_contents_container_gtk.h"
#include "base/gfx/native_widget_types.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/gtk/status_bubble_gtk.h"
#include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
...@@ -13,16 +14,14 @@ ...@@ -13,16 +14,14 @@
namespace { namespace {
// Allocates all normal tab contents views to the size of the passed in // 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) { void ResizeChildren(GtkWidget* widget, void* param) {
GtkAllocation* allocation = reinterpret_cast<GtkAllocation*>(param); GtkAllocation* allocation = reinterpret_cast<GtkAllocation*>(param);
if (strcmp(gtk_widget_get_name(widget), "status-bubble") != 0) { if (widget->allocation.width != allocation->width ||
if (widget->allocation.width != allocation->width || widget->allocation.height != allocation->height) {
widget->allocation.height != allocation->height) { gtk_widget_set_size_request(widget, allocation->width,
gtk_widget_set_size_request(widget, allocation->width, allocation->height);
allocation->height);
}
} }
} }
...@@ -30,21 +29,45 @@ void ResizeChildren(GtkWidget* widget, void* param) { ...@@ -30,21 +29,45 @@ void ResizeChildren(GtkWidget* widget, void* param) {
TabContentsContainerGtk::TabContentsContainerGtk(StatusBubbleGtk* status_bubble) TabContentsContainerGtk::TabContentsContainerGtk(StatusBubbleGtk* status_bubble)
: tab_contents_(NULL), : tab_contents_(NULL),
status_bubble_(status_bubble), status_bubble_(status_bubble) {
fixed_(gtk_fixed_new()) { Init();
gtk_fixed_put(GTK_FIXED(fixed_), status_bubble->widget(), 0, 0); }
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_signal_connect(fixed_, "size-allocate",
G_CALLBACK(OnFixedSizeAllocate), this); 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) { 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) { void TabContentsContainerGtk::SetTabContents(TabContents* tab_contents) {
...@@ -140,7 +163,31 @@ void TabContentsContainerGtk::OnFixedSizeAllocate( ...@@ -140,7 +163,31 @@ void TabContentsContainerGtk::OnFixedSizeAllocate(
TabContentsContainerGtk* container) { TabContentsContainerGtk* container) {
// Set all the tab contents GtkWidgets to the size of the allocation. // Set all the tab contents GtkWidgets to the size of the allocation.
gtk_container_foreach(GTK_CONTAINER(fixed), ResizeChildren, allocation); gtk_container_foreach(GTK_CONTAINER(fixed), ResizeChildren, allocation);
}
// Tell the status bubble about how large it can be. // static
container->status_bubble_->SetParentAllocation(fixed, allocation); 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);
} }
...@@ -14,11 +14,15 @@ class RenderViewHost; ...@@ -14,11 +14,15 @@ class RenderViewHost;
class StatusBubbleGtk; class StatusBubbleGtk;
class TabContents; class TabContents;
typedef struct _GtkFloatingContainer GtkFloatingContainer;
class TabContentsContainerGtk : public NotificationObserver { class TabContentsContainerGtk : public NotificationObserver {
public: public:
explicit TabContentsContainerGtk(StatusBubbleGtk* status_bubble); explicit TabContentsContainerGtk(StatusBubbleGtk* status_bubble);
~TabContentsContainerGtk(); ~TabContentsContainerGtk();
void Init();
// Inserts our GtkWidget* hierarchy into a GtkBox managed by our owner. // Inserts our GtkWidget* hierarchy into a GtkBox managed by our owner.
void AddContainerToBox(GtkWidget* widget); void AddContainerToBox(GtkWidget* widget);
...@@ -58,6 +62,12 @@ class TabContentsContainerGtk : public NotificationObserver { ...@@ -58,6 +62,12 @@ class TabContentsContainerGtk : public NotificationObserver {
GtkAllocation* allocation, GtkAllocation* allocation,
TabContentsContainerGtk* container); 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_; NotificationRegistrar registrar_;
// The currently visible TabContents. // The currently visible TabContents.
...@@ -66,13 +76,16 @@ class TabContentsContainerGtk : public NotificationObserver { ...@@ -66,13 +76,16 @@ class TabContentsContainerGtk : public NotificationObserver {
// The status bubble manager. Always non-NULL. // The status bubble manager. Always non-NULL.
StatusBubbleGtk* status_bubble_; StatusBubbleGtk* status_bubble_;
// We keep a GtkFixed which is inserted into this object's owner's GtkWidget // Top of the TabContentsContainerGtk widget hierarchy. A cross between a
// hierarchy. We then insert and remove TabContents GtkWidgets into this // GtkBin and a GtkFixed, |floating_| has |fixed_| as its one "real" child,
// fixed_. This should not be a GtkVBox since there were errors with timing // and the various things that hang off the bottom (status bubble, etc) have
// where the vbox was horizontally split with the top half displaying the // their positions manually set in OnSetFloatingPosition.
// current TabContents and bottom half displaying the loading page. In GtkWidget* floating_;
// 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. // 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_; GtkWidget* fixed_;
DISALLOW_COPY_AND_ASSIGN(TabContentsContainerGtk); DISALLOW_COPY_AND_ASSIGN(TabContentsContainerGtk);
......
...@@ -909,6 +909,8 @@ ...@@ -909,6 +909,8 @@
'browser/gtk/go_button_gtk.h', 'browser/gtk/go_button_gtk.h',
'browser/gtk/gtk_chrome_button.cc', 'browser/gtk/gtk_chrome_button.cc',
'browser/gtk/gtk_chrome_button.h', 'browser/gtk/gtk_chrome_button.h',
'browser/gtk/gtk_floating_container.cc',
'browser/gtk/gtk_floating_container.h',
'browser/gtk/hung_renderer_dialog_gtk.cc', 'browser/gtk/hung_renderer_dialog_gtk.cc',
'browser/gtk/import_dialog_gtk.cc', 'browser/gtk/import_dialog_gtk.cc',
'browser/gtk/import_dialog_gtk.h', 'browser/gtk/import_dialog_gtk.h',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment