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
This commit is contained in:
Vadim Zeitlin
2012-02-11 16:26:52 +00:00
parent c3d3028a44
commit 1442168125
9 changed files with 297 additions and 24 deletions

View File

@@ -488,6 +488,7 @@ All (GUI):
GTK: GTK:
- Implement support for wxBG_STYLE_TRANSPARENT (Armel Asselin).
- Fix wxNotebook best size calculation. - Fix wxNotebook best size calculation.
MSW: MSW:

View File

@@ -81,6 +81,7 @@ public:
virtual bool SetFont( const wxFont &font ); virtual bool SetFont( const wxFont &font );
virtual bool SetBackgroundStyle(wxBackgroundStyle style) ; virtual bool SetBackgroundStyle(wxBackgroundStyle style) ;
virtual bool IsTransparentBackgroundSupported(wxString* reason = NULL) const;
virtual int GetCharHeight() const; virtual int GetCharHeight() const;
virtual int GetCharWidth() const; virtual int GetCharWidth() const;
@@ -399,7 +400,6 @@ private:
bool DoScrollByUnits(ScrollDir dir, ScrollUnit unit, int units); bool DoScrollByUnits(ScrollDir dir, ScrollUnit unit, int units);
virtual void AddChildGTK(wxWindowGTK* child); virtual void AddChildGTK(wxWindowGTK* child);
DECLARE_DYNAMIC_CLASS(wxWindowGTK) DECLARE_DYNAMIC_CLASS(wxWindowGTK)
wxDECLARE_NO_COPY_CLASS(wxWindowGTK); wxDECLARE_NO_COPY_CLASS(wxWindowGTK);
}; };

View File

@@ -1029,8 +1029,7 @@ public:
wxColour GetForegroundColour() const; wxColour GetForegroundColour() const;
// Set/get the background style. // Set/get the background style.
virtual bool SetBackgroundStyle(wxBackgroundStyle style) virtual bool SetBackgroundStyle(wxBackgroundStyle style);
{ m_backgroundStyle = style; return true; }
wxBackgroundStyle GetBackgroundStyle() const wxBackgroundStyle GetBackgroundStyle() const
{ return m_backgroundStyle; } { return m_backgroundStyle; }
@@ -1039,6 +1038,13 @@ public:
// from a parent window // from a parent window
virtual bool HasTransparentBackground() { return false; } 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 // set/retrieve the font for the window (SetFont() returns true if the
// font really changed) // font really changed)
virtual bool SetFont(const wxFont& font) = 0; virtual bool SetFont(const wxFont& font) = 0;

View File

@@ -546,9 +546,14 @@ enum wxBackgroundStyle
/* this style is deprecated and doesn't do anything, don't use */ /* this style is deprecated and doesn't do anything, don't use */
wxBG_STYLE_COLOUR, 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,
}; };

View File

@@ -1755,11 +1755,57 @@ public:
@c wxBG_STYLE_PAINT is a simpler and more efficient solution to the same @c wxBG_STYLE_PAINT is a simpler and more efficient solution to the same
problem. 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(), @see SetBackgroundColour(), GetForegroundColour(),
SetTransparent() SetTransparent(), IsTransparentBackgroundSupported()
*/ */
virtual bool SetBackgroundStyle(wxBackgroundStyle style); 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 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, parent window if you don't want its font to be inherited by its children,

View File

@@ -32,6 +32,7 @@
#include "wx/custombgwin.h" #include "wx/custombgwin.h"
#include "wx/dcbuffer.h" #include "wx/dcbuffer.h"
#include "wx/artprov.h"
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// resources // resources
@@ -146,13 +147,28 @@ public:
ControlWithTransparency(wxWindow *parent, ControlWithTransparency(wxWindow *parent,
const wxPoint& pos, const wxPoint& pos,
const wxSize& size) const wxSize& size)
: wxWindow(parent, wxID_ANY, pos, size, wxBORDER_NONE)
{ {
Connect(wxEVT_PAINT, wxString reason;
wxPaintEventHandler(ControlWithTransparency::OnPaint)); if ( parent->IsTransparentBackgroundSupported(&reason) )
{
SetBackgroundStyle (wxBG_STYLE_TRANSPARENT);
m_message = "This is custom control with transparency";
}
else
{
m_message = "Transparency not supported, check tooltip.";
} }
virtual bool HasTransparentBackground() { return true; } Create (parent, wxID_ANY, pos, size, wxBORDER_NONE);
Connect(wxEVT_PAINT,
wxPaintEventHandler(ControlWithTransparency::OnPaint));
if ( !reason.empty() )
{
// This can be only done now, after creating the window.
SetToolTip(reason);
}
}
private: private:
void OnPaint( wxPaintEvent& WXUNUSED(event) ) void OnPaint( wxPaintEvent& WXUNUSED(event) )
@@ -165,8 +181,17 @@ private:
dc.SetTextForeground(*wxBLUE); dc.SetTextForeground(*wxBLUE);
dc.SetBackgroundMode(wxTRANSPARENT); 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", "right one drawn directly",
wxPoint(150, 20)); wxPoint(150, 20));
new ControlWithTransparency(this, wxPoint(65, 125), wxSize(300, 22)); new ControlWithTransparency(this, wxPoint(65, 125), wxSize(350, 22));
SetFocusIgnoringChildren(); SetFocusIgnoringChildren();
SetBackgroundColour(*wxCYAN); SetBackgroundColour(*wxCYAN);

View File

@@ -1553,6 +1553,39 @@ wxColour wxWindowBase::GetForegroundColour() const
return m_foregroundColour; 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 ) bool wxWindowBase::SetBackgroundColour( const wxColour &colour )
{ {
if ( colour == m_backgroundColour ) if ( colour == m_backgroundColour )

View File

@@ -129,7 +129,11 @@ enum wxPoolGCType
wxTEXT_SCREEN, wxTEXT_SCREEN,
wxBG_SCREEN, wxBG_SCREEN,
wxPEN_SCREEN, wxPEN_SCREEN,
wxBRUSH_SCREEN wxBRUSH_SCREEN,
wxTEXT_COLOUR_ALPHA,
wxBG_COLOUR_ALPHA,
wxPEN_COLOUR_ALPHA,
wxBRUSH_COLOUR_ALPHA
}; };
struct wxGC struct wxGC
@@ -375,6 +379,13 @@ void wxWindowDCImpl::SetUpDC( bool isMemDC )
m_textGC = wxGetPoolGC( m_gdkwindow, wxTEXT_SCREEN ); m_textGC = wxGetPoolGC( m_gdkwindow, wxTEXT_SCREEN );
m_bgGC = wxGetPoolGC( m_gdkwindow, wxBG_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 else
{ {
m_penGC = wxGetPoolGC( m_gdkwindow, wxPEN_COLOUR ); m_penGC = wxGetPoolGC( m_gdkwindow, wxPEN_COLOUR );

View File

@@ -48,6 +48,15 @@ using namespace wxGTKImpl;
#include <gdk/gdkkeysyms-compat.h> #include <gdk/gdkkeysyms-compat.h>
#endif #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 // 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 // We cannot set colours and fonts before the widget
// been realized, so we do this directly after realization // been realized, so we do this directly after realization
// or otherwise in idle time // or otherwise in idle time
@@ -2319,6 +2347,21 @@ void wxWindowGTK::PostCreation()
{ {
wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") ); 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_wxwindow)
{ {
if (!m_noExpose) if (!m_noExpose)
@@ -3692,6 +3735,24 @@ void wxWindowGTK::GtkSendPaintEvents()
switch ( GetBackgroundStyle() ) 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: case wxBG_STYLE_ERASE:
{ {
wxWindowDC dc( (wxWindow*)this ); wxWindowDC dc( (wxWindow*)this );
@@ -3768,6 +3829,39 @@ void wxWindowGTK::GtkSendPaintEvents()
paint_event.SetEventObject( this ); paint_event.SetEventObject( this );
HandleWindowEvent( paint_event ); 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_clipPaintRegion = false;
m_updateRegion.Clear(); m_updateRegion.Clear();
@@ -3971,10 +4065,9 @@ void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style) bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
{ {
wxWindowBase::SetBackgroundStyle(style); if (!wxWindowBase::SetBackgroundStyle(style))
return false;
if ( style == wxBG_STYLE_PAINT )
{
GdkWindow *window; GdkWindow *window;
if ( m_wxwindow ) if ( m_wxwindow )
{ {
@@ -3986,6 +4079,10 @@ bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
window = w ? gtk_widget_get_window(w) : NULL; window = w ? gtk_widget_get_window(w) : NULL;
} }
bool wantNoBackPixmap = style == wxBG_STYLE_PAINT || style == wxBG_STYLE_TRANSPARENT;
if ( wantNoBackPixmap )
{
if (window) if (window)
{ {
// Make sure GDK/X11 doesn't refresh the window // Make sure GDK/X11 doesn't refresh the window
@@ -4011,6 +4108,55 @@ bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
return true; 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 // Pop-up menu stuff
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------