diff options
author | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-05 05:41:54 +0000 |
---|---|---|
committer | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-05 05:41:54 +0000 |
commit | cfc0accbc458f940bdf8feed5e24d44100b6930b (patch) | |
tree | cbf3e7d343eea0299418da298630492ce37b622d /views/widget | |
parent | 55f9c5a5466d0c7d5550252b653162d7dc775442 (diff) | |
download | chromium_src-cfc0accbc458f940bdf8feed5e24d44100b6930b.zip chromium_src-cfc0accbc458f940bdf8feed5e24d44100b6930b.tar.gz chromium_src-cfc0accbc458f940bdf8feed5e24d44100b6930b.tar.bz2 |
Add transparency to TYPE_CHILD WidgetGtk.
* This does not work with renderer as parent yet.(I need to investigate renderer what it's doing)
* gtk_widget_set_back_pixmap is not necessary and removed.
* changed to use clear operation to fill a background with transparent color.
BUG=none
TEST=manual: added transparent widget example to view_examples. I also tested with chrome and confirmed that info bubble, app launcher and tab dragging works.
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=46374
Review URL: http://codereview.chromium.org/1629024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46439 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/widget')
-rw-r--r-- | views/widget/widget_gtk.cc | 208 | ||||
-rw-r--r-- | views/widget/widget_gtk.h | 7 |
2 files changed, 167 insertions, 48 deletions
diff --git a/views/widget/widget_gtk.cc b/views/widget/widget_gtk.cc index 4bde019..f7b35e0 100644 --- a/views/widget/widget_gtk.cc +++ b/views/widget/widget_gtk.cc @@ -16,6 +16,8 @@ #include "app/os_exchange_data_provider_gtk.h" #include "base/auto_reset.h" #include "base/compiler_specific.h" +#include "base/message_loop.h" +#include "base/ref_counted.h" #include "base/utf_string_conversions.h" #include "gfx/path.h" #include "views/widget/default_theme_provider.h" @@ -25,6 +27,105 @@ #include "views/widget/tooltip_manager_gtk.h" #include "views/window/window_gtk.h" +namespace { + +// g_object data keys to associate a WidgetGtk object to a GtkWidget. +const char* kWidgetKey = "__VIEWS_WIDGET__"; +const wchar_t* kWidgetWideKey = L"__VIEWS_WIDGET__"; +// A g_object data key to associate a CompositePainter object to a GtkWidget. +const char* kCompositePainterKey = "__VIEWS_COMPOSITE_PAINTER__"; +// A g_object data key to associate the flag whether or not the widget +// is composited to a GtkWidget. gtk_widget_is_composited simply tells +// if x11 supports composition and cannot be used to tell if given widget +// is composited. +const char* kCompositeEnabledKey = "__VIEWS_COMPOSITE_ENABLED__"; + +// CompositePainter draws a composited child widgets image into its +// drawing area. This object is ref counted so that it can disconnect +// expose event handler when all transparent widgets are deleted. +class CompositePainter : public base::RefCounted<CompositePainter> { + public: + explicit CompositePainter(GtkWidget* parent) + : parent_object_(G_OBJECT(parent)) { + handler_id_ = g_signal_connect_after( + parent_object_, "expose_event", G_CALLBACK(OnCompositePaint), NULL); + } + + static void AddCompositePainter(GtkWidget* widget) { + CompositePainter* painter = static_cast<CompositePainter*>( + g_object_get_data(G_OBJECT(widget), kCompositePainterKey)); + if (painter) { + painter->AddRef(); + } else { + g_object_set_data(G_OBJECT(widget), kCompositePainterKey, + new CompositePainter(widget)); + } + } + + static void RemoveCompositePainter(GtkWidget* widget) { + CompositePainter* painter = reinterpret_cast<CompositePainter*>( + g_object_get_data(G_OBJECT(widget), kCompositePainterKey)); + DCHECK(painter); + painter->Release(); + } + + // Enable the composition. + static void SetComposited(GtkWidget* widget) { + DCHECK(GTK_WIDGET_REALIZED(widget)); + gdk_window_set_composited(widget->window, true); + g_object_set_data(G_OBJECT(widget), kCompositeEnabledKey, + const_cast<char*>("")); + } + + // Returns true if the |widget| is composited. + static bool IsComposited(GtkWidget* widget) { + return g_object_get_data(G_OBJECT(widget), kCompositeEnabledKey) != NULL; + } + + private: + friend class base::RefCounted<CompositePainter>; + virtual ~CompositePainter() { + g_signal_handler_disconnect(parent_object_, handler_id_); + g_object_set_data(parent_object_, kCompositePainterKey, NULL); + } + + // Composes a image from one child. + static void CompositeChildWidget(GtkWidget* child, gpointer data) { + GdkEventExpose* event = static_cast<GdkEventExpose*>(data); + GtkWidget* parent = gtk_widget_get_parent(child); + DCHECK(parent); + if (IsComposited(child)) { + cairo_t* cr = gdk_cairo_create(parent->window); + gdk_cairo_set_source_pixmap(cr, child->window, + child->allocation.x, + child->allocation.y); + GdkRegion* region = gdk_region_rectangle(&child->allocation); + gdk_region_intersect(region, event->region); + gdk_cairo_region(cr, region); + cairo_clip(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_paint(cr); + cairo_destroy(cr); + } + } + + // Expose-event handler that compose & draws children's image into + // the |parent|'s drawing area. + static gboolean OnCompositePaint(GtkWidget* parent, GdkEventExpose* event) { + gtk_container_foreach(GTK_CONTAINER(parent), + CompositeChildWidget, + event); + return false; + } + + GObject* parent_object_; + gulong handler_id_; + + DISALLOW_COPY_AND_ASSIGN(CompositePainter); +}; + +} // namespace + namespace views { // During drag and drop GTK sends a drag-leave during a drop. This means we @@ -62,9 +163,6 @@ class WidgetGtk::DropObserver : public MessageLoopForUI::Observer { DISALLOW_COPY_AND_ASSIGN(DropObserver); }; -static const char* kWidgetKey = "__VIEWS_WIDGET__"; -static const wchar_t* kWidgetWideKey = L"__VIEWS_WIDGET__"; - // Returns the position of a widget on screen. static void GetWidgetPositionOnScreen(GtkWidget* widget, int* x, int *y) { // First get the root window. @@ -188,9 +286,8 @@ GtkWindow* WidgetGtk::GetTransientParent() const { } bool WidgetGtk::MakeTransparent() { - // Transparency can only be enabled for windows/popups and only if we haven't - // realized the widget. - DCHECK(!widget_ && type_ != TYPE_CHILD); + // Transparency can only be enabled only if we haven't realized the widget. + DCHECK(!widget_); if (!gdk_screen_is_composited(gdk_screen_get_default())) { // Transparency is only supported for compositing window managers. @@ -397,6 +494,7 @@ void WidgetGtk::Init(GtkWidget* parent, g_signal_connect_after(G_OBJECT(window_contents_), "size_allocate", G_CALLBACK(&OnSizeAllocateThunk), this); + gtk_widget_set_app_paintable(window_contents_, true); g_signal_connect(window_contents_, "expose_event", G_CALLBACK(&OnPaintThunk), this); g_signal_connect(window_contents_, "enter_notify_event", @@ -440,10 +538,6 @@ void WidgetGtk::Init(GtkWidget* parent, G_CALLBACK(&OnKeyPressThunk), this); g_signal_connect(widget_, "key_release_event", G_CALLBACK(&OnKeyReleaseThunk), this); - if (transparent_) { - g_signal_connect(widget_, "expose_event", - G_CALLBACK(&OnWindowPaintThunk), this); - } // Drag and drop. gtk_drag_dest_set(window_contents_, static_cast<GtkDestDefaults>(0), @@ -580,6 +674,13 @@ void WidgetGtk::Close() { void WidgetGtk::CloseNow() { if (widget_) { + if (transparent_ && type_ == TYPE_CHILD) { + GtkWidget* parent = gtk_widget_get_parent(widget_); + if (parent) { + DCHECK(parent != null_parent_); + CompositePainter::RemoveCompositePainter(parent); + } + } gtk_widget_destroy(widget_); widget_ = NULL; } @@ -790,6 +891,10 @@ void WidgetGtk::OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) { } gboolean WidgetGtk::OnPaint(GtkWidget* widget, GdkEventExpose* event) { + if (transparent_) { + // Clear the background before drawing any view and native components. + DrawTransparentBackground(widget, event); + } root_view_->OnPaint(event); return false; // False indicates other widgets should get the event as well. } @@ -1054,22 +1159,10 @@ RootView* WidgetGtk::CreateRootView() { } gboolean WidgetGtk::OnWindowPaint(GtkWidget* widget, GdkEventExpose* event) { - // NOTE: for reasons I don't understand this code is never hit. It should - // be hit when transparent_, but we never get the expose-event for the - // window in this case, even though a stand alone test case triggers it. I'm - // leaving it in just in case. - - // Fill the background totally transparent. We don't need to paint the root - // view here as that is done by OnPaint. + // Clear the background to be totally transparent. We don't need to + // paint the root view here as that is done by OnPaint. DCHECK(transparent_); - int width, height; - gtk_window_get_size(GTK_WINDOW(widget), &width, &height); - cairo_t* cr = gdk_cairo_create(widget->window); - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba(cr, 0, 0, 0, 0); - cairo_rectangle(cr, 0, 0, width, height); - cairo_fill(cr); - cairo_destroy(cr); + DrawTransparentBackground(widget, event); return false; } @@ -1193,7 +1286,19 @@ void WidgetGtk::CreateGtkWidget(GtkWidget* parent, const gfx::Rect& bounds) { gtk_container_add(GTK_CONTAINER(popup), null_parent_); gtk_widget_realize(null_parent_); } + if (transparent_) { + // transparency has to be configured before widget is realized. + DCHECK(parent) << "Transparent widget must have parent when initialized"; + ConfigureWidgetForTransparentBackground(parent); + } gtk_container_add(GTK_CONTAINER(parent ? parent : null_parent_), widget_); + gtk_widget_realize(widget_); + if (transparent_) { + // The widget has to be realized to set composited flag. + // I tried "realize" signal to set this flag, but it did not work + // when the top level is popup. + CompositePainter::SetComposited(widget_); + } } else { widget_ = gtk_window_new( (type_ == TYPE_WINDOW || type_ == TYPE_DECORATED_WINDOW) ? @@ -1228,27 +1333,26 @@ void WidgetGtk::CreateGtkWidget(GtkWidget* parent, const gfx::Rect& bounds) { static_cast<Widget*>(this)); if (transparent_) - ConfigureWidgetForTransparentBackground(); + ConfigureWidgetForTransparentBackground(NULL); if (ignore_events_) ConfigureWidgetForIgnoreEvents(); SetAlwaysOnTop(always_on_top_); + // The widget needs to be realized before handlers like size-allocate can + // function properly. + gtk_widget_realize(widget_); } - - // The widget needs to be realized before handlers like size-allocate can - // function properly. - gtk_widget_realize(widget_); - - // Associate this object with the widget. + // Setting the WidgetKey property to widget_, which is used by + // GetWidgetFromNativeWindow. SetNativeWindowProperty(kWidgetWideKey, this); } -void WidgetGtk::ConfigureWidgetForTransparentBackground() { - DCHECK(widget_ && window_contents_ && widget_ != window_contents_); +void WidgetGtk::ConfigureWidgetForTransparentBackground(GtkWidget* parent) { + DCHECK(widget_ && window_contents_); GdkColormap* rgba_colormap = - gdk_screen_get_rgba_colormap(gdk_screen_get_default()); + gdk_screen_get_rgba_colormap(gtk_widget_get_screen(widget_)); if (!rgba_colormap) { transparent_ = false; return; @@ -1257,19 +1361,21 @@ void WidgetGtk::ConfigureWidgetForTransparentBackground() { // on both the window and fixed. In addition we need to make sure no // decorations are drawn. The last bit is to make sure the widget doesn't // attempt to draw a pixmap in it's background. - gtk_widget_set_colormap(widget_, rgba_colormap); - gtk_widget_set_app_paintable(widget_, true); - gtk_widget_realize(widget_); - gdk_window_set_decorations(widget_->window, - static_cast<GdkWMDecoration>(0)); - // Widget must be realized before setting pixmap. - gdk_window_set_back_pixmap(widget_->window, NULL, FALSE); - + if (type_ != TYPE_CHILD) { + DCHECK(parent == NULL); + gtk_widget_set_colormap(widget_, rgba_colormap); + gtk_widget_set_app_paintable(widget_, true); + g_signal_connect(widget_, "expose_event", + G_CALLBACK(&OnWindowPaintThunk), this); + gtk_widget_realize(widget_); + gdk_window_set_decorations(widget_->window, + static_cast<GdkWMDecoration>(0)); + } else { + DCHECK(parent); + CompositePainter::AddCompositePainter(parent); + } + DCHECK(!GTK_WIDGET_REALIZED(window_contents_)); gtk_widget_set_colormap(window_contents_, rgba_colormap); - gtk_widget_set_app_paintable(window_contents_, true); - gtk_widget_realize(window_contents_); - // Widget must be realized before setting pixmap. - gdk_window_set_back_pixmap(window_contents_->window, NULL, FALSE); } void WidgetGtk::ConfigureWidgetForIgnoreEvents() { @@ -1301,6 +1407,14 @@ void WidgetGtk::HandleGrabBroke() { } } +void WidgetGtk::DrawTransparentBackground(GtkWidget* widget, + GdkEventExpose* event) { + cairo_t* cr = gdk_cairo_create(widget->window); + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + gdk_cairo_region(cr, event->region); + cairo_fill(cr); + cairo_destroy(cr); +} //////////////////////////////////////////////////////////////////////////////// // Widget, public: diff --git a/views/widget/widget_gtk.h b/views/widget/widget_gtk.h index cb5f103..824c11d 100644 --- a/views/widget/widget_gtk.h +++ b/views/widget/widget_gtk.h @@ -75,6 +75,7 @@ class WidgetGtk // invoked before Init. This does a couple of checks and returns true if // the window can be made transparent. The actual work of making the window // transparent is done by ConfigureWidgetForTransparentBackground. + // This works for both child and window types. bool MakeTransparent(); bool is_transparent() const { return transparent_; } @@ -314,7 +315,7 @@ class WidgetGtk // Invoked from create widget to enable the various bits needed for a // transparent background. This is only invoked if MakeTransparent has been // invoked. - void ConfigureWidgetForTransparentBackground(); + void ConfigureWidgetForTransparentBackground(GtkWidget* parent); // Invoked from create widget to enable the various bits needed for a // window which doesn't receive events. This is only invoked if @@ -324,6 +325,10 @@ class WidgetGtk // TODO(sky): documentation void HandleGrabBroke(); + // A utility function to draw a transparent background onto the |widget|. + static void DrawTransparentBackground(GtkWidget* widget, + GdkEventExpose* event); + const Type type_; // Our native views. If we're a window/popup, then widget_ is the window and |