]> git.saurik.com Git - wxWidgets.git/commitdiff
Implement wxBG_STYLE_TRANSPARENT support for wxGTK.
authorVadim Zeitlin <vadim@wxwidgets.org>
Sat, 11 Feb 2012 16:26:52 +0000 (16:26 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sat, 11 Feb 2012 16:26:52 +0000 (16:26 +0000)
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
include/wx/gtk/window.h
include/wx/window.h
interface/wx/defs.h
interface/wx/window.h
samples/erase/erase.cpp
src/common/wincmn.cpp
src/gtk/dcclient.cpp
src/gtk/window.cpp

index 3191c2f838dad9aeb1a39ae70985a23f4f749ed9..7cf5a48302bb68c047664a2a1a4eed22d05139f6 100644 (file)
@@ -488,6 +488,7 @@ All (GUI):
 
 GTK:
 
+- Implement support for wxBG_STYLE_TRANSPARENT (Armel Asselin).
 - Fix wxNotebook best size calculation.
 
 MSW:
index 90b1a765a79ecd79e7b495716e165cb9b73a7fd1..55b034c795b61f31213725d6bae7cd7fcbef5825 100644 (file)
@@ -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);
 };
index b644b17f5518f4af836fd2bf677f41f5e71c1a6a..c4442b21414c9153e745871ca4b3e9e0fa0f86db 100644 (file)
@@ -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;
index 98906d323b001d746a0299af63ea3d29d50fb7b4..33b3e5af06c575ea612ff55de8e0af5ad0a1906f 100644 (file)
@@ -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,
 };
 
 
index 7228e0edcb394638b9bbe59531c899b1c590e60d..dce860350c33900944068be86889b86169d8d24f 100644 (file)
@@ -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,
index 6600a1125cce8b1f52e7922acc4e92999e1e0301..ee468651b6d5d68dec223d3cb67e48e088e3b879 100644 (file)
@@ -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);
index 969ace7bae41a319f58e6c75d393b7f86dd16339..614ee459932895ec0935982076f0e833cbdce811 100644 (file)
@@ -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 )
index 2087ca70af07d0d2bab94bd01003195b1c6e3db6..8e023ed656a1c2742054b61941c4d85394729d57 100644 (file)
@@ -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 );
index 7a8d6a445c1e900a3d57774043058ebf7c065e75..870b41845195be8aec0f508848c7d4b65d6290e8 100644 (file)
@@ -48,6 +48,15 @@ using namespace wxGTKImpl;
 #include <gdk/gdkkeysyms-compat.h>
 #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<wxGraphicsContext> 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<wxGraphicsContext> 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
 // ----------------------------------------------------------------------------