From 14421681253f09f119dad82364f5ada40f58f2fc Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 11 Feb 2012 16:26:52 +0000 Subject: [PATCH] Implement wxBG_STYLE_TRANSPARENT support for wxGTK. Use composited windows if supported by GTK+ for wxWindows with this background style. Also add wxWindow::IsTransparentBackgroundSupported() and show how to use it in the sample. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@70569 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 1 + include/wx/gtk/window.h | 2 +- include/wx/window.h | 10 ++- interface/wx/defs.h | 9 ++- interface/wx/window.h | 48 +++++++++++- samples/erase/erase.cpp | 35 +++++++-- src/common/wincmn.cpp | 33 ++++++++ src/gtk/dcclient.cpp | 13 ++- src/gtk/window.cpp | 170 +++++++++++++++++++++++++++++++++++++--- 9 files changed, 297 insertions(+), 24 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 3191c2f838..7cf5a48302 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -488,6 +488,7 @@ All (GUI): GTK: +- Implement support for wxBG_STYLE_TRANSPARENT (Armel Asselin). - Fix wxNotebook best size calculation. MSW: diff --git a/include/wx/gtk/window.h b/include/wx/gtk/window.h index 90b1a765a7..55b034c795 100644 --- a/include/wx/gtk/window.h +++ b/include/wx/gtk/window.h @@ -81,6 +81,7 @@ public: virtual bool SetFont( const wxFont &font ); virtual bool SetBackgroundStyle(wxBackgroundStyle style) ; + virtual bool IsTransparentBackgroundSupported(wxString* reason = NULL) const; virtual int GetCharHeight() const; virtual int GetCharWidth() const; @@ -399,7 +400,6 @@ private: bool DoScrollByUnits(ScrollDir dir, ScrollUnit unit, int units); virtual void AddChildGTK(wxWindowGTK* child); - DECLARE_DYNAMIC_CLASS(wxWindowGTK) wxDECLARE_NO_COPY_CLASS(wxWindowGTK); }; diff --git a/include/wx/window.h b/include/wx/window.h index b644b17f55..c4442b2141 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -1029,8 +1029,7 @@ public: wxColour GetForegroundColour() const; // Set/get the background style. - virtual bool SetBackgroundStyle(wxBackgroundStyle style) - { m_backgroundStyle = style; return true; } + virtual bool SetBackgroundStyle(wxBackgroundStyle style); wxBackgroundStyle GetBackgroundStyle() const { return m_backgroundStyle; } @@ -1039,6 +1038,13 @@ public: // from a parent window virtual bool HasTransparentBackground() { return false; } + // Returns true if background transparency is supported for this + // window, i.e. if calling SetBackgroundStyle(wxBG_STYLE_TRANSPARENT) + // has a chance of succeeding. If reason argument is non-NULL, returns a + // user-readable explanation of why it isn't supported if the return + // value is false. + virtual bool IsTransparentBackgroundSupported(wxString* reason = NULL) const; + // set/retrieve the font for the window (SetFont() returns true if the // font really changed) virtual bool SetFont(const wxFont& font) = 0; diff --git a/interface/wx/defs.h b/interface/wx/defs.h index 98906d323b..33b3e5af06 100644 --- a/interface/wx/defs.h +++ b/interface/wx/defs.h @@ -546,9 +546,14 @@ enum wxBackgroundStyle /* this style is deprecated and doesn't do anything, don't use */ wxBG_STYLE_COLOUR, - /* this is a Mac-only style, don't use in portable code */ - wxBG_STYLE_TRANSPARENT, + /** + Indicates that the window background is not erased, letting the parent + window show through. + Currently this style is only supported in wxOSX and wxGTK with + compositing available, see wxWindow::IsTransparentBackgroundSupported(). + */ + wxBG_STYLE_TRANSPARENT, }; diff --git a/interface/wx/window.h b/interface/wx/window.h index 7228e0edcb..dce860350c 100644 --- a/interface/wx/window.h +++ b/interface/wx/window.h @@ -1755,11 +1755,57 @@ public: @c wxBG_STYLE_PAINT is a simpler and more efficient solution to the same problem. + + Under wxGTK and wxOSX, you can use ::wxBG_STYLE_TRANSPARENT to obtain + full transparency of the window background. Note that wxGTK supports + this only since GTK 2.12 with a compositing manager enabled, call + IsTransparentBackgroundSupported() to check whether this is the case. + + Also, on order for @c SetBackgroundStyle(wxBG_STYLE_TRANSPARENT) to + work, it must be called before Create(). If you're using your own + wxWindow-derived class you should write your code in the following way: + @code + class MyWidget : public wxWindow + { + public: + MyWidget(wxWindow* parent, ...) + : wxWindow() // Use default ctor here! + { + // Do this first: + SetBackgroundStyle(wxBG_STYLE_TRANSPARENT); + + // And really create the window afterwards: + Create(parent, ...); + } + }; + @endcode + @see SetBackgroundColour(), GetForegroundColour(), - SetTransparent() + SetTransparent(), IsTransparentBackgroundSupported() */ virtual bool SetBackgroundStyle(wxBackgroundStyle style); + /** + Checks whether using transparent background might work. + + If this function returns @false, calling SetBackgroundStyle() with + ::wxBG_STYLE_TRANSPARENT is not going to work. If it returns @true, + setting transparent style should normally succeed. + + Notice that this function would typically be called on the parent of a + window you want to set transparent background style for as the window + for which this method is called must be fully created. + + @param reason + If not @NULL, a reason message is provided if transparency is not + supported. + + @return @true if background transparency is supported. + + @since 2.9.4 + */ + virtual bool IsTransparentBackgroundSupported(wxString *reason = NULL) const; + /** Sets the font for this window. This function should not be called for the parent window if you don't want its font to be inherited by its children, diff --git a/samples/erase/erase.cpp b/samples/erase/erase.cpp index 6600a1125c..ee468651b6 100644 --- a/samples/erase/erase.cpp +++ b/samples/erase/erase.cpp @@ -32,6 +32,7 @@ #include "wx/custombgwin.h" #include "wx/dcbuffer.h" +#include "wx/artprov.h" // ---------------------------------------------------------------------------- // resources @@ -146,13 +147,28 @@ public: ControlWithTransparency(wxWindow *parent, const wxPoint& pos, const wxSize& size) - : wxWindow(parent, wxID_ANY, pos, size, wxBORDER_NONE) { + wxString reason; + if ( parent->IsTransparentBackgroundSupported(&reason) ) + { + SetBackgroundStyle (wxBG_STYLE_TRANSPARENT); + m_message = "This is custom control with transparency"; + } + else + { + m_message = "Transparency not supported, check tooltip."; + } + + Create (parent, wxID_ANY, pos, size, wxBORDER_NONE); Connect(wxEVT_PAINT, wxPaintEventHandler(ControlWithTransparency::OnPaint)); - } - virtual bool HasTransparentBackground() { return true; } + if ( !reason.empty() ) + { + // This can be only done now, after creating the window. + SetToolTip(reason); + } + } private: void OnPaint( wxPaintEvent& WXUNUSED(event) ) @@ -165,8 +181,17 @@ private: dc.SetTextForeground(*wxBLUE); dc.SetBackgroundMode(wxTRANSPARENT); - dc.DrawText("This is custom control with transparency", 0, 2); + dc.DrawText(m_message, 0, 2); + + // Draw some bitmap/icon to ensure transparent bitmaps are indeed + // transparent on transparent windows + wxBitmap bmp(wxArtProvider::GetBitmap(wxART_WARNING, wxART_MENU)); + wxIcon icon(wxArtProvider::GetIcon(wxART_GOTO_LAST, wxART_MENU)); + dc.DrawBitmap (bmp, GetSize().x - 1 - bmp.GetWidth(), 2); + dc.DrawIcon(icon, GetSize().x - 1 - bmp.GetWidth()-icon.GetWidth(), 2); } + + wxString m_message; }; // ---------------------------------------------------------------------------- @@ -329,7 +354,7 @@ MyCanvas::MyCanvas(wxFrame *parent) "right one drawn directly", wxPoint(150, 20)); - new ControlWithTransparency(this, wxPoint(65, 125), wxSize(300, 22)); + new ControlWithTransparency(this, wxPoint(65, 125), wxSize(350, 22)); SetFocusIgnoringChildren(); SetBackgroundColour(*wxCYAN); diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index 969ace7bae..614ee45993 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -1553,6 +1553,39 @@ wxColour wxWindowBase::GetForegroundColour() const return m_foregroundColour; } +bool wxWindowBase::SetBackgroundStyle(wxBackgroundStyle style) +{ + // The checks below shouldn't be triggered if we're not really changing the + // style. + if ( style == m_backgroundStyle ) + return true; + + // Transparent background style can be only set before creation because of + // wxGTK limitation. + wxCHECK_MSG( (style != wxBG_STYLE_TRANSPARENT) || !GetHandle(), + false, + "wxBG_STYLE_TRANSPARENT style can only be set before " + "Create()-ing the window." ); + + // And once it is set, wxBG_STYLE_TRANSPARENT can't be unset. + wxCHECK_MSG( (m_backgroundStyle != wxBG_STYLE_TRANSPARENT) || + (style == wxBG_STYLE_TRANSPARENT), + false, + "wxBG_STYLE_TRANSPARENT can't be unset once it was set." ); + + m_backgroundStyle = style; + + return true; +} + +bool wxWindowBase::IsTransparentBackgroundSupported(wxString *reason) const +{ + if ( reason ) + *reason = _("This platform does not support background transparency."); + + return false; +} + bool wxWindowBase::SetBackgroundColour( const wxColour &colour ) { if ( colour == m_backgroundColour ) diff --git a/src/gtk/dcclient.cpp b/src/gtk/dcclient.cpp index 2087ca70af..8e023ed656 100644 --- a/src/gtk/dcclient.cpp +++ b/src/gtk/dcclient.cpp @@ -129,7 +129,11 @@ enum wxPoolGCType wxTEXT_SCREEN, wxBG_SCREEN, wxPEN_SCREEN, - wxBRUSH_SCREEN + wxBRUSH_SCREEN, + wxTEXT_COLOUR_ALPHA, + wxBG_COLOUR_ALPHA, + wxPEN_COLOUR_ALPHA, + wxBRUSH_COLOUR_ALPHA }; struct wxGC @@ -375,6 +379,13 @@ void wxWindowDCImpl::SetUpDC( bool isMemDC ) m_textGC = wxGetPoolGC( m_gdkwindow, wxTEXT_SCREEN ); m_bgGC = wxGetPoolGC( m_gdkwindow, wxBG_SCREEN ); } + else if (m_cmap == gdk_screen_get_rgba_colormap(gdk_colormap_get_screen(m_cmap))) + { + m_penGC = wxGetPoolGC( m_gdkwindow, wxPEN_COLOUR_ALPHA ); + m_brushGC = wxGetPoolGC( m_gdkwindow, wxBRUSH_COLOUR_ALPHA ); + m_textGC = wxGetPoolGC( m_gdkwindow, wxTEXT_COLOUR_ALPHA ); + m_bgGC = wxGetPoolGC( m_gdkwindow, wxBG_COLOUR_ALPHA ); + } else { m_penGC = wxGetPoolGC( m_gdkwindow, wxPEN_COLOUR ); diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 7a8d6a445c..870b418451 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -48,6 +48,15 @@ using namespace wxGTKImpl; #include #endif +#if wxUSE_GRAPHICS_CONTEXT +#include "wx/graphics.h" +#include "wx/scopedptr.h" +#endif // wxUSE_GRAPHICS_CONTEXT + +// gdk_window_set_composited() is only supported since 2.12 +#define wxGTK_VERSION_REQUIRED_FOR_COMPOSITING 2,12,0 +#define wxGTK_HAS_COMPOSITING_SUPPORT GTK_CHECK_VERSION(2,12,0) + //----------------------------------------------------------------------------- // documentation on internals //----------------------------------------------------------------------------- @@ -1979,6 +1988,25 @@ void wxWindowGTK::GTKHandleRealized() ); } + // Use composited window if background is transparent, if supported. + if (m_backgroundStyle == wxBG_STYLE_TRANSPARENT) + { +#if wxGTK_HAS_COMPOSITING_SUPPORT + if (IsTransparentBackgroundSupported()) + { + GdkWindow* const window = GTKGetDrawingWindow(); + if (window) + gdk_window_set_composited(window, true); + } + else +#endif // wxGTK_HAS_COMPOSITING_SUPPORT + { + // We revert to erase mode if transparency is not supported + m_backgroundStyle = wxBG_STYLE_ERASE; + } + } + + // We cannot set colours and fonts before the widget // been realized, so we do this directly after realization // or otherwise in idle time @@ -2319,6 +2347,21 @@ void wxWindowGTK::PostCreation() { wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") ); +#if wxGTK_HAS_COMPOSITING_SUPPORT + // Set RGBA visual as soon as possible to minimize the possibility that + // somebody uses the wrong one. + if ( m_backgroundStyle == wxBG_STYLE_TRANSPARENT && + IsTransparentBackgroundSupported() ) + { + GdkScreen *screen = gtk_widget_get_screen (m_widget); + + GdkColormap *rgba_colormap = gdk_screen_get_rgba_colormap (screen); + + if (rgba_colormap) + gtk_widget_set_colormap(m_widget, rgba_colormap); + } +#endif // wxGTK_HAS_COMPOSITING_SUPPORT + if (m_wxwindow) { if (!m_noExpose) @@ -3692,6 +3735,24 @@ void wxWindowGTK::GtkSendPaintEvents() switch ( GetBackgroundStyle() ) { +#if wxUSE_GRAPHICS_CONTEXT + case wxBG_STYLE_TRANSPARENT: + { + // Set a transparent background, so that overlaying in parent + // might indeed let see through where this child did not + // explicitly paint. + // NB: it works also for top level windows (but this is the + // windows manager which then does the compositing job) + wxScopedPtr gc (wxGraphicsContext::Create( this )); + cairo_t *cairo_context = (cairo_t *)gc->GetNativeContext(); + + gc->Clip (m_nativeUpdateRegion); + cairo_set_operator (cairo_context, CAIRO_OPERATOR_CLEAR); + cairo_paint (cairo_context); + break; + } +#endif // wxUSE_GRAPHICS_CONTEXT + case wxBG_STYLE_ERASE: { wxWindowDC dc( (wxWindow*)this ); @@ -3768,6 +3829,39 @@ void wxWindowGTK::GtkSendPaintEvents() paint_event.SetEventObject( this ); HandleWindowEvent( paint_event ); +#if wxUSE_GRAPHICS_CONTEXT + { // now composite children which need it + wxScopedPtr gc (wxGraphicsContext::Create( this )); + cairo_t *cairo_context = (cairo_t *)gc->GetNativeContext(); + + // Overlay all our composite children on top of the painted area + wxWindowList::compatibility_iterator node; + for ( node = m_children.GetFirst(); node ; node = node->GetNext() ) + { + wxWindow *compositeChild = node->GetData(); + if (compositeChild->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT) + { + GtkWidget *child = compositeChild->m_wxwindow; + + // The source data is the (composited) child + gdk_cairo_set_source_pixmap (cairo_context, child->window, + child->allocation.x, + child->allocation.y); + + // Draw no more than our expose event intersects our child + gc->Clip (m_nativeUpdateRegion); + gc->Clip (child->allocation.x, child->allocation.y, + child->allocation.width, child->allocation.height); + + cairo_set_operator (cairo_context, CAIRO_OPERATOR_OVER); + cairo_paint (cairo_context); + + gc->ResetClip (); + } + } + } +#endif // wxUSE_GRAPHICS_CONTEXT + m_clipPaintRegion = false; m_updateRegion.Clear(); @@ -3971,21 +4065,24 @@ void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style) bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style) { - wxWindowBase::SetBackgroundStyle(style); + if (!wxWindowBase::SetBackgroundStyle(style)) + return false; - if ( style == wxBG_STYLE_PAINT ) + GdkWindow *window; + if ( m_wxwindow ) { - GdkWindow *window; - if ( m_wxwindow ) - { - window = GTKGetDrawingWindow(); - } - else - { - GtkWidget * const w = GetConnectWidget(); - window = w ? gtk_widget_get_window(w) : NULL; - } + window = GTKGetDrawingWindow(); + } + else + { + GtkWidget * const w = GetConnectWidget(); + window = w ? gtk_widget_get_window(w) : NULL; + } + + bool wantNoBackPixmap = style == wxBG_STYLE_PAINT || style == wxBG_STYLE_TRANSPARENT; + if ( wantNoBackPixmap ) + { if (window) { // Make sure GDK/X11 doesn't refresh the window @@ -4011,6 +4108,55 @@ bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style) return true; } +bool wxWindowGTK::IsTransparentBackgroundSupported(wxString* reason) const +{ +#if wxGTK_HAS_COMPOSITING_SUPPORT && wxUSE_GRAPHICS_CONTEXT + if (gtk_check_version(wxGTK_VERSION_REQUIRED_FOR_COMPOSITING) != NULL) + { + if (reason) + { + *reason = _("GTK+ installed on this machine is too old to " + "support screen compositing, please install " + "GTK+ 2.12 or later."); + } + + return false; + } + + // NB: We don't check here if the particular kind of widget supports + // transparency, we check only if it would be possible for a generic window + + wxCHECK_MSG ( m_widget, false, "Window must be created first" ); + + if (!gdk_screen_is_composited(gtk_widget_get_screen(m_widget))) + { + if (reason) + { + *reason = _("Compositing not supported by this system, " + "please enable it in your Window Manager."); + } + + return false; + } + + return true; +#elif !wxGTK_HAS_COMPOSITING_SUPPORT + if (reason) + { + *reason = _("This program was compiled with a too old version of GTK+, " + "please rebuild with GTK+ 2.12 or newer."); + } +#elif !wxUSE_GRAPHICS_CONTEXT + if (reason) + { + *reason = _("wxUSE_GRAPHICS_CONTEXT required for compositing window, " + "please rebuild wxWidgets with support for it."); + } +#endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT + + return false; +} + // ---------------------------------------------------------------------------- // Pop-up menu stuff // ---------------------------------------------------------------------------- -- 2.45.2