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:
- Implement support for wxBG_STYLE_TRANSPARENT (Armel Asselin).
- Fix wxNotebook best size calculation.
MSW:

View 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);
};

View 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;

View 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,
};

View 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,

View 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);

View 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 )

View 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 );

View 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
// ----------------------------------------------------------------------------