X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a82afab363d472b412823d645091ced8a60b6c14..a3b7db872925df2921f36637a84085ec04cf977a:/src/gtk/nonownedwnd.cpp diff --git a/src/gtk/nonownedwnd.cpp b/src/gtk/nonownedwnd.cpp index 20f05258e3..e558b52984 100644 --- a/src/gtk/nonownedwnd.cpp +++ b/src/gtk/nonownedwnd.cpp @@ -3,7 +3,7 @@ // Purpose: wxGTK implementation of wxNonOwnedWindow. // Author: Vadim Zeitlin // Created: 2011-10-12 -// RCS-ID: $Id: wxhead.cpp,v 1.11 2010-04-22 12:44:51 zeitlin Exp $ +// RCS-ID: $Id$ // Copyright: (c) 2011 Vadim Zeitlin // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// @@ -24,68 +24,297 @@ #endif #ifndef WX_PRECOMP - #include "wx/string.h" + #include "wx/nonownedwnd.h" + #include "wx/dcclient.h" + #include "wx/dcmemory.h" + #include "wx/region.h" #endif // WX_PRECOMP -#include "wx/gtk/private.h" +#include "wx/graphics.h" -#include +#include +#include "wx/gtk/private/gtk2-compat.h" -namespace +// ---------------------------------------------------------------------------- +// wxNonOwnedWindowShapeImpl: base class for region and path-based classes. +// ---------------------------------------------------------------------------- + +// This class provides behaviour common to both region and path-based +// implementations and defines SetShape() method and virtual dtor that can be +// called by wxNonOwnedWindow when it's realized leaving just the +// implementation of DoSetShape() to the derived classes. +class wxNonOwnedWindowShapeImpl : public wxEvtHandler { +public: + wxNonOwnedWindowShapeImpl(wxWindow* win) : m_win(win) + { + } + + virtual ~wxNonOwnedWindowShapeImpl() { } -// helper -bool do_shape_combine_region(GdkWindow* window, const wxRegion& region) + bool SetShape() + { + if ( m_win->m_wxwindow ) + SetShapeIfNonNull(gtk_widget_get_window(m_win->m_wxwindow)); + + return SetShapeIfNonNull(gtk_widget_get_window(m_win->m_widget)); + } + + // Must be overridden to indicate if the data object must stay around or if + // it can be deleted once SetShape() was called. + virtual bool CanBeDeleted() const = 0; + +protected: + wxWindow* const m_win; + +private: + // SetShape to the given GDK window by calling DoSetShape() if it's non-NULL. + bool SetShapeIfNonNull(GdkWindow* window) + { + return window && DoSetShape(window); + } + + // SetShape the shape to the given GDK window which can be either the window + // of m_widget or m_wxwindow of the wxWindow we're used with. + virtual bool DoSetShape(GdkWindow* window) = 0; + + wxDECLARE_NO_COPY_CLASS(wxNonOwnedWindowShapeImpl); +}; + +// Version not using any custom shape. +class wxNonOwnedWindowShapeImplNone : public wxNonOwnedWindowShapeImpl { - if (window) +public: + wxNonOwnedWindowShapeImplNone(wxWindow* win) : + wxNonOwnedWindowShapeImpl(win) { - if (region.IsEmpty()) - { - gdk_window_shape_combine_mask(window, NULL, 0, 0); - } - else - { - gdk_window_shape_combine_region(window, region.GetRegion(), 0, 0); - return true; - } } - return false; -} -} // anonymous namespace + virtual bool CanBeDeleted() const { return true; } + +private: + virtual bool DoSetShape(GdkWindow* window) + { + gdk_window_shape_combine_region(window, NULL, 0, 0); + + return true; + } +}; + +// Version using simple wxRegion. +class wxNonOwnedWindowShapeImplRegion : public wxNonOwnedWindowShapeImpl +{ +public: + wxNonOwnedWindowShapeImplRegion(wxWindow* win, const wxRegion& region) : + wxNonOwnedWindowShapeImpl(win), + m_region(region) + { + } + + virtual bool CanBeDeleted() const { return true; } + +private: + virtual bool DoSetShape(GdkWindow* window) + { + gdk_window_shape_combine_region(window, m_region.GetRegion(), 0, 0); + + return true; + } + + wxRegion m_region; +}; + +#if wxUSE_GRAPHICS_CONTEXT + +// Version using more complex wxGraphicsPath. +class wxNonOwnedWindowShapeImplPath : public wxNonOwnedWindowShapeImpl +{ +public: + wxNonOwnedWindowShapeImplPath(wxWindow* win, const wxGraphicsPath& path) : + wxNonOwnedWindowShapeImpl(win), + m_path(path), + m_mask(CreateShapeBitmap(path), *wxBLACK) + { + + m_win->Connect + ( + wxEVT_PAINT, + wxPaintEventHandler(wxNonOwnedWindowShapeImplPath::OnPaint), + NULL, + this + ); + } + + virtual ~wxNonOwnedWindowShapeImplPath() + { + m_win->Disconnect + ( + wxEVT_PAINT, + wxPaintEventHandler(wxNonOwnedWindowShapeImplPath::OnPaint), + NULL, + this + ); + } + + // Currently we always return false from here, if drawing the border + // becomes optional, we could return true if we don't need to draw it. + virtual bool CanBeDeleted() const { return false; } + +private: + wxBitmap CreateShapeBitmap(const wxGraphicsPath& path) + { + // Draw the path on a bitmap to get the mask we need. + // + // Notice that using monochrome bitmap here doesn't work because of an + // apparent wxGraphicsContext bug in wxGTK, so use a bitmap of screen + // depth even if this is wasteful. + wxBitmap bmp(m_win->GetSize()); + wxMemoryDC dc(bmp); + + dc.SetBackground(*wxBLACK); + dc.Clear(); + +#ifdef __WXGTK3__ + wxGraphicsContext* context = dc.GetGraphicsContext(); +#else + wxScopedPtr context(wxGraphicsContext::Create(dc)); +#endif + context->SetBrush(*wxWHITE); + context->FillPath(path); + + return bmp; + } + + virtual bool DoSetShape(GdkWindow *window) + { + if (!m_mask) + return false; + +#ifdef __WXGTK3__ + cairo_region_t* region = gdk_cairo_region_create_from_surface(m_mask); + gdk_window_shape_combine_region(window, region, 0, 0); + cairo_region_destroy(region); +#else + gdk_window_shape_combine_mask(window, m_mask, 0, 0); +#endif + + return true; + } + + // Draw a shaped window border. + void OnPaint(wxPaintEvent& event) + { + event.Skip(); + + wxPaintDC dc(m_win); +#ifdef __WXGTK3__ + wxGraphicsContext* context = dc.GetGraphicsContext(); +#else + wxScopedPtr context(wxGraphicsContext::Create(dc)); +#endif + context->SetPen(wxPen(*wxLIGHT_GREY, 2)); + context->StrokePath(m_path); + } + + wxGraphicsPath m_path; + wxMask m_mask; +}; + +#endif // wxUSE_GRAPHICS_CONTEXT // ============================================================================ // wxNonOwnedWindow implementation // ============================================================================ +wxNonOwnedWindow::~wxNonOwnedWindow() +{ + delete m_shapeImpl; +} + void wxNonOwnedWindow::GTKHandleRealized() { wxNonOwnedWindowBase::GTKHandleRealized(); - if (HasFlag(wxFRAME_SHAPED)) - SetShape(m_shape); + if ( m_shapeImpl ) + { + m_shapeImpl->SetShape(); + + // We can destroy wxNonOwnedWindowShapeImplRegion immediately but need + // to keep wxNonOwnedWindowShapeImplPath around as it draws the border + // on every repaint. + if ( m_shapeImpl->CanBeDeleted() ) + { + delete m_shapeImpl; + m_shapeImpl = NULL; + } + } } -bool wxNonOwnedWindow::SetShape(const wxRegion& region) +bool wxNonOwnedWindow::DoClearShape() { - wxCHECK_MSG( HasFlag(wxFRAME_SHAPED), false, - wxT("Shaped windows must be created with the wxFRAME_SHAPED style.")); + if ( !m_shapeImpl ) + { + // Nothing to do, we don't have any custom shape. + return true; + } if ( gtk_widget_get_realized(m_widget) ) { - if ( m_wxwindow ) - do_shape_combine_region(gtk_widget_get_window(m_wxwindow), region); + // Reset the existing shape immediately. + wxNonOwnedWindowShapeImplNone data(this); + data.SetShape(); + } + //else: just do nothing, deleting m_shapeImpl is enough to ensure that we + // don't set the custom shape later when we're realized. - return do_shape_combine_region(gtk_widget_get_window(m_widget), region); + delete m_shapeImpl; + m_shapeImpl = NULL; + + return true; +} + +bool wxNonOwnedWindow::DoSetRegionShape(const wxRegion& region) +{ + // In any case get rid of the old data. + delete m_shapeImpl; + m_shapeImpl = NULL; + + if ( gtk_widget_get_realized(m_widget) ) + { + // We can avoid an unnecessary heap allocation and just set the shape + // immediately. + wxNonOwnedWindowShapeImplRegion data(this, region); + return data.SetShape(); } - else // not realized yet + else // Create an object that will set shape when we're realized. { - // store the shape to set, it will be really set once we're realized - m_shape = region; + m_shapeImpl = new wxNonOwnedWindowShapeImplRegion(this, region); - // we don't know if we're going to succeed or fail, be optimistic by - // default + // In general we don't know whether we are going to succeed or not, so + // be optimistic. return true; } } + +#if wxUSE_GRAPHICS_CONTEXT + +bool wxNonOwnedWindow::DoSetPathShape(const wxGraphicsPath& path) +{ + // The logic here is simpler than above because we always create + // wxNonOwnedWindowShapeImplPath on the heap as we must keep it around, + // even if we're already realized + + delete m_shapeImpl; + m_shapeImpl = new wxNonOwnedWindowShapeImplPath(this, path); + + if ( gtk_widget_get_realized(m_widget) ) + { + return m_shapeImpl->SetShape(); + } + //else: will be done later from GTKHandleRealized(). + + return true; +} + +#endif // wxUSE_GRAPHICS_CONTEXT