]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/nonownedwnd.cpp
Added wxNonOwnedWindow::SetShape(wxGraphicsPath).
[wxWidgets.git] / src / gtk / nonownedwnd.cpp
index b07cdd1afb7887d88c1b5a647aee49f88797b3e3..862ac8b32bb5bbd5f39fa66db883d8943b0b2fe4 100644 (file)
@@ -24,7 +24,6 @@
 #endif
 
 #ifndef WX_PRECOMP
-    #include "wx/frame.h"           // Just for wxFRAME_SHAPED.
     #include "wx/nonownedwnd.h"
     #include "wx/region.h"
 #endif // WX_PRECOMP
 
 #include <gdk/gdk.h>
 
-namespace
+#include "wx/graphics.h"
+
+// ----------------------------------------------------------------------------
+// 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() { }
+
+    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;
 
-// helper
-bool do_shape_combine_region(GdkWindow* window, const wxRegion& region)
+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_mask(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();
+
+        wxScopedPtr<wxGraphicsContext> context(wxGraphicsContext::Create(dc));
+        context->SetBrush(*wxWHITE);
+        context->FillPath(path);
+
+        return bmp;
+    }
+
+    virtual bool DoSetShape(GdkWindow *window)
+    {
+        GdkBitmap* bitmap = m_mask.GetBitmap();
+        if ( !bitmap )
+            return false;
+
+        gdk_window_shape_combine_mask(window, bitmap, 0, 0);
+
+        return true;
+    }
+
+    // Draw a shaped window border.
+    void OnPaint(wxPaintEvent& event)
+    {
+        event.Skip();
+
+        wxPaintDC dc(m_win);
+        wxScopedPtr<wxGraphicsContext> context(wxGraphicsContext::Create(dc));
+        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