Added wxNonOwnedWindow::SetShape(wxGraphicsPath).

TODO: Document.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@69462 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2011-10-18 21:56:52 +00:00
parent dd4eefcb29
commit 46ea442ca2
9 changed files with 508 additions and 71 deletions

View File

@ -39,7 +39,6 @@ class WXDLLIMPEXP_FWD_CORE wxToolBar;
#define wxFRAME_NO_TASKBAR 0x0002 // No taskbar button (MSW only)
#define wxFRAME_TOOL_WINDOW 0x0004 // No taskbar button, no system menu
#define wxFRAME_FLOAT_ON_PARENT 0x0008 // Always above its parent
#define wxFRAME_SHAPED 0x0010 // Create a window that is able to be shaped
// ----------------------------------------------------------------------------
// wxFrame is a top-level window with optional menubar, statusbar and toolbar

View File

@ -11,6 +11,8 @@
#ifndef _WX_GTK_NONOWNEDWND_H_
#define _WX_GTK_NONOWNEDWND_H_
class wxNonOwnedWindowShapeImpl;
// ----------------------------------------------------------------------------
// wxNonOwnedWindow contains code common to wx{Popup,TopLevel}Window in wxGTK.
// ----------------------------------------------------------------------------
@ -18,16 +20,23 @@
class WXDLLIMPEXP_CORE wxNonOwnedWindow : public wxNonOwnedWindowBase
{
public:
wxNonOwnedWindow() { }
virtual bool SetShape(const wxRegion& region);
wxNonOwnedWindow() { m_shapeImpl = NULL; }
virtual ~wxNonOwnedWindow();
// Overridden to actually set the shape when the window becomes realized.
virtual void GTKHandleRealized();
protected:
virtual bool DoClearShape();
virtual bool DoSetRegionShape(const wxRegion& region);
#if wxUSE_GRAPHICS_CONTEXT
virtual bool DoSetPathShape(const wxGraphicsPath& path);
#endif // wxUSE_GRAPHICS_CONTEXT
private:
// If valid, defines the custom shape of the window.
wxRegion m_shape;
// If non-NULL, contains information about custom window shape.
wxNonOwnedWindowShapeImpl* m_shapeImpl;
wxDECLARE_NO_COPY_CLASS(wxNonOwnedWindow);
};

View File

@ -11,6 +11,8 @@
#ifndef _WX_MSW_NONOWNEDWND_H_
#define _WX_MSW_NONOWNEDWND_H_
class wxNonOwnedWindowShapeImpl;
// ----------------------------------------------------------------------------
// wxNonOwnedWindow
// ----------------------------------------------------------------------------
@ -18,7 +20,20 @@
class WXDLLIMPEXP_CORE wxNonOwnedWindow : public wxNonOwnedWindowBase
{
public:
virtual bool SetShape(const wxRegion& region);
wxNonOwnedWindow();
virtual ~wxNonOwnedWindow();
protected:
virtual bool DoClearShape();
virtual bool DoSetRegionShape(const wxRegion& region);
#if wxUSE_GRAPHICS_CONTEXT
virtual bool DoSetPathShape(const wxGraphicsPath& path);
private:
wxNonOwnedWindowShapeImpl* m_shapeImpl;
#endif // wxUSE_GRAPHICS_CONTEXT
wxDECLARE_NO_COPY_CLASS(wxNonOwnedWindow);
};
#endif // _WX_MSW_NONOWNEDWND_H_

View File

@ -14,6 +14,11 @@
#include "wx/window.h"
// Styles that can be used with any wxNonOwnedWindow:
#define wxFRAME_SHAPED 0x0010 // Create a window that is able to be shaped
class WXDLLIMPEXP_FWD_CORE wxGraphicsPath;
// ----------------------------------------------------------------------------
// wxNonOwnedWindow: a window that is not a child window of another one.
// ----------------------------------------------------------------------------
@ -24,8 +29,52 @@ public:
// Set the shape of the window to the given region.
// Returns true if the platform supports this feature (and the
// operation is successful.)
virtual bool SetShape(const wxRegion& WXUNUSED(region)) { return false; }
bool SetShape(const wxRegion& region)
{
// This style is in fact only needed by wxOSX/Carbon so once we don't
// use this port any more, we could get rid of this requirement, but
// for now you must specify wxFRAME_SHAPED for SetShape() to work on
// all platforms.
wxCHECK_MSG
(
HasFlag(wxFRAME_SHAPED), false,
wxS("Shaped windows must be created with the wxFRAME_SHAPED style.")
);
return region.IsEmpty() ? DoClearShape() : DoSetRegionShape(region);
}
#if wxUSE_GRAPHICS_CONTEXT
// Set the shape using the specified path.
bool SetShape(const wxGraphicsPath& path)
{
wxCHECK_MSG
(
HasFlag(wxFRAME_SHAPED), false,
wxS("Shaped windows must be created with the wxFRAME_SHAPED style.")
);
return DoSetPathShape(path);
}
#endif // wxUSE_GRAPHICS_CONTEXT
protected:
virtual bool DoClearShape()
{
return false;
}
virtual bool DoSetRegionShape(const wxRegion& WXUNUSED(region))
{
return false;
}
#if wxUSE_GRAPHICS_CONTEXT
virtual bool DoSetPathShape(const wxGraphicsPath& WXUNUSED(path))
{
return false;
}
#endif // wxUSE_GRAPHICS_CONTEXT
};
#if defined(__WXDFB__)

View File

@ -14,6 +14,8 @@
#include "wx/window.h"
#include "wx/graphics.h"
#if wxUSE_SYSTEM_OPTIONS
#define wxMAC_WINDOW_PLAIN_TRANSITION wxT("mac.window-plain-transition")
#endif
@ -82,6 +84,10 @@ public:
virtual bool SetShape(const wxRegion& region);
const wxRegion& GetShape() const { return m_shape; }
#if wxUSE_GRAPHICS_CONTEXT
const wxGraphicsPath& GetShapePath() { return m_shapePath; }
#endif // wxUSE_GRAPHICS_CONTEXT
// activation hooks only necessary for MDI Implementation
static void MacDelayedDeactivation(long timestamp);
virtual void MacActivate( long timestamp , bool inIsActivating ) ;
@ -125,6 +131,12 @@ protected:
wxShowEffect effect,
unsigned timeout);
virtual bool DoClearShape();
virtual bool DoSetRegionShape(const wxRegion& region);
#if wxUSE_GRAPHICS_CONTEXT
virtual bool DoSetPathShape(const wxGraphicsPath& path);
#endif // wxUSE_GRAPHICS_CONTEXT
virtual void WillBeDestroyed();
wxNonOwnedWindowImpl* m_nowpeer ;
@ -135,6 +147,9 @@ protected:
private :
wxRegion m_shape;
#if wxUSE_GRAPHICS_CONTEXT
wxGraphicsPath m_shapePath;
#endif // wxUSE_GRAPHICS_CONTEXT
};
// list of all frames and modeless dialogs

View 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
@ -33,61 +32,275 @@
#include <gdk/gdk.h>
namespace
{
#include "wx/graphics.h"
// helper
bool do_shape_combine_region(GdkWindow* window, const wxRegion& region)
// ----------------------------------------------------------------------------
// 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
{
if (window)
public:
wxNonOwnedWindowShapeImpl(wxWindow* win) : m_win(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 ~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;
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
{
public:
wxNonOwnedWindowShapeImplNone(wxWindow* win) :
wxNonOwnedWindowShapeImpl(win)
{
}
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);
return do_shape_combine_region(gtk_widget_get_window(m_widget), region);
// Reset the existing shape immediately.
wxNonOwnedWindowShapeImplNone data(this);
data.SetShape();
}
else // not realized yet
{
// store the shape to set, it will be really set once we're realized
m_shape = region;
//else: just do nothing, deleting m_shapeImpl is enough to ensure that we
// don't set the custom shape later when we're realized.
// we don't know if we're going to succeed or fail, be optimistic by
// default
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 // Create an object that will set shape when we're realized.
{
m_shapeImpl = new wxNonOwnedWindowShapeImplRegion(this, region);
// 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

View File

@ -34,27 +34,27 @@
#include "wx/nonownedwnd.h"
#include "wx/msw/wrapgdip.h"
#include "wx/graphics.h"
#include "wx/scopedptr.h"
// ============================================================================
// wxNonOwnedWindow implementation
// ============================================================================
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."));
// The empty region signifies that the shape should be removed from the
// window.
if ( region.IsEmpty() )
if (::SetWindowRgn(GetHwnd(), NULL, TRUE) == 0)
{
if (::SetWindowRgn(GetHwnd(), NULL, TRUE) == 0)
{
wxLogLastError(wxT("SetWindowRgn"));
return false;
}
return true;
wxLogLastError(wxT("SetWindowRgn"));
return false;
}
return true;
}
bool wxNonOwnedWindow::DoSetRegionShape(const wxRegion& region)
{
// Windows takes ownership of the region, so
// we'll have to make a copy of the region to give to it.
DWORD noBytes = ::GetRegionData(GetHrgnOf(region), 0, NULL);
@ -77,4 +77,104 @@ bool wxNonOwnedWindow::SetShape(const wxRegion& region)
return true;
}
#if wxUSE_GRAPHICS_CONTEXT
#include "wx/msw/wrapgdip.h"
// This class contains data used only when SetPath(wxGraphicsPath) is called.
//
// Notice that it derives from wxEvtHandler solely to allow Connect()-ing its
// OnPaint() method to the window, we could get rid of this inheritance once
// Bind() can be used in wx sources.
class wxNonOwnedWindowShapeImpl : public wxEvtHandler
{
public:
wxNonOwnedWindowShapeImpl(wxNonOwnedWindow* win, const wxGraphicsPath& path) :
m_win(win),
m_path(path)
{
// Create the region corresponding to this path and set it as windows
// shape.
wxScopedPtr<wxGraphicsContext> context(wxGraphicsContext::Create(win));
Region gr(static_cast<GraphicsPath*>(m_path.GetNativePath()));
win->SetShape(
wxRegion(
gr.GetHRGN(static_cast<Graphics*>(context->GetNativeContext()))
)
);
// Connect to the paint event to draw the border.
//
// TODO: Do this only optionally?
m_win->Connect
(
wxEVT_PAINT,
wxPaintEventHandler(wxNonOwnedWindowShapeImpl::OnPaint),
NULL,
this
);
}
virtual ~wxNonOwnedWindowShapeImpl()
{
m_win->Disconnect
(
wxEVT_PAINT,
wxPaintEventHandler(wxNonOwnedWindowShapeImpl::OnPaint),
NULL,
this
);
}
private:
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);
}
wxNonOwnedWindow* const m_win;
wxGraphicsPath m_path;
wxDECLARE_NO_COPY_CLASS(wxNonOwnedWindowShapeImpl);
};
wxNonOwnedWindow::wxNonOwnedWindow()
{
m_shapeImpl = NULL;
}
wxNonOwnedWindow::~wxNonOwnedWindow()
{
delete m_shapeImpl;
}
bool wxNonOwnedWindow::DoSetPathShape(const wxGraphicsPath& path)
{
delete m_shapeImpl;
m_shapeImpl = new wxNonOwnedWindowShapeImpl(this, path);
return true;
}
#else // !wxUSE_GRAPHICS_CONTEXT
// Trivial ctor and dtor as we don't have anything to do when wxGraphicsContext
// is not used but still define them here to avoid adding even more #if checks
// to the header, it it doesn't do any harm even though it's not needed.
wxNonOwnedWindow::wxNonOwnedWindow()
{
}
wxNonOwnedWindow::~wxNonOwnedWindow()
{
}
#endif // wxUSE_GRAPHICS_CONTEXT/!wxUSE_GRAPHICS_CONTEXT
#endif // !__WXWINCE__

View File

@ -1288,6 +1288,19 @@ void wxWidgetCocoaImpl::drawRect(void* rect, WXWidget slf, void *WXUNUSED(_cmd))
dc.SetDeviceClippingRegion(clearRgn);
dc.Clear();
}
#if wxUSE_GRAPHICS_CONTEXT
// If the window shape is defined by a path, stroke the path to show
// the window border.
const wxGraphicsPath& path = tlwParent->GetShapePath();
if ( !path.IsNull() )
{
CGContextSetLineWidth(context, 1);
CGContextSetStrokeColorWithColor(context, wxLIGHT_GREY->GetCGColor());
CGContextAddPath(context, (CGPathRef) path.GetNativePath());
CGContextStrokePath(context);
}
#endif // wxUSE_GRAPHICS_CONTEXT
}
wxpeer->MacPaintChildrenBorders();

View File

@ -497,25 +497,49 @@ WXWindow wxNonOwnedWindow::GetWXWindow() const
// Shape implementation
// ---------------------------------------------------------------------------
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."));
m_shape.Clear();
m_shape = region;
// The empty region signifies that the shape
// should be removed from the window.
if ( region.IsEmpty() )
{
wxSize sz = GetClientSize();
wxRegion rgn(0, 0, sz.x, sz.y);
if ( rgn.IsEmpty() )
return false ;
else
return SetShape(rgn);
}
wxSize sz = GetClientSize();
wxRegion region(0, 0, sz.x, sz.y);
return m_nowpeer->SetShape(region);
}
bool wxNonOwnedWindow::DoSetRegionShape(const wxRegion& region)
{
m_shape = region;
return m_nowpeer->SetShape(region);
}
#if wxUSE_GRAPHICS_CONTEXT
#include "wx/scopedptr.h"
bool wxNonOwnedWindow::DoSetPathShape(const wxGraphicsPath& path)
{
m_shapePath = path;
// Convert the path to wxRegion by rendering the path on a window-sized
// bitmap, creating a mask from it and finally creating the region from
// this mask.
wxBitmap bmp(GetSize());
{
wxMemoryDC dc(bmp);
dc.SetBackground(*wxBLACK);
dc.Clear();
wxScopedPtr<wxGraphicsContext> context(wxGraphicsContext::Create(dc));
context->SetBrush(*wxWHITE);
context->FillPath(m_shapePath);
}
bmp.SetMask(new wxMask(bmp, *wxBLACK));
return DoSetRegionShape(wxRegion(bmp));
}
#endif // wxUSE_GRAPHICS_CONTEXT