update position for widgets in native containers, fixes #15231
[wxWidgets.git] / src / gtk / nonownedwnd.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/nonownedwnd.cpp
3 // Purpose: wxGTK implementation of wxNonOwnedWindow.
4 // Author: Vadim Zeitlin
5 // Created: 2011-10-12
6 // RCS-ID: $Id$
7 // Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #include "wx/nonownedwnd.h"
28 #include "wx/dcclient.h"
29 #include "wx/dcmemory.h"
30 #include "wx/region.h"
31 #endif // WX_PRECOMP
32
33 #include "wx/graphics.h"
34
35 #include <gtk/gtk.h>
36 #include "wx/gtk/private/gtk2-compat.h"
37
38 // ----------------------------------------------------------------------------
39 // wxNonOwnedWindowShapeImpl: base class for region and path-based classes.
40 // ----------------------------------------------------------------------------
41
42 // This class provides behaviour common to both region and path-based
43 // implementations and defines SetShape() method and virtual dtor that can be
44 // called by wxNonOwnedWindow when it's realized leaving just the
45 // implementation of DoSetShape() to the derived classes.
46 class wxNonOwnedWindowShapeImpl : public wxEvtHandler
47 {
48 public:
49 wxNonOwnedWindowShapeImpl(wxWindow* win) : m_win(win)
50 {
51 }
52
53 virtual ~wxNonOwnedWindowShapeImpl() { }
54
55 bool SetShape()
56 {
57 if ( m_win->m_wxwindow )
58 SetShapeIfNonNull(gtk_widget_get_window(m_win->m_wxwindow));
59
60 return SetShapeIfNonNull(gtk_widget_get_window(m_win->m_widget));
61 }
62
63 // Must be overridden to indicate if the data object must stay around or if
64 // it can be deleted once SetShape() was called.
65 virtual bool CanBeDeleted() const = 0;
66
67 protected:
68 wxWindow* const m_win;
69
70 private:
71 // SetShape to the given GDK window by calling DoSetShape() if it's non-NULL.
72 bool SetShapeIfNonNull(GdkWindow* window)
73 {
74 return window && DoSetShape(window);
75 }
76
77 // SetShape the shape to the given GDK window which can be either the window
78 // of m_widget or m_wxwindow of the wxWindow we're used with.
79 virtual bool DoSetShape(GdkWindow* window) = 0;
80
81 wxDECLARE_NO_COPY_CLASS(wxNonOwnedWindowShapeImpl);
82 };
83
84 // Version not using any custom shape.
85 class wxNonOwnedWindowShapeImplNone : public wxNonOwnedWindowShapeImpl
86 {
87 public:
88 wxNonOwnedWindowShapeImplNone(wxWindow* win) :
89 wxNonOwnedWindowShapeImpl(win)
90 {
91 }
92
93 virtual bool CanBeDeleted() const { return true; }
94
95 private:
96 virtual bool DoSetShape(GdkWindow* window)
97 {
98 gdk_window_shape_combine_region(window, NULL, 0, 0);
99
100 return true;
101 }
102 };
103
104 // Version using simple wxRegion.
105 class wxNonOwnedWindowShapeImplRegion : public wxNonOwnedWindowShapeImpl
106 {
107 public:
108 wxNonOwnedWindowShapeImplRegion(wxWindow* win, const wxRegion& region) :
109 wxNonOwnedWindowShapeImpl(win),
110 m_region(region)
111 {
112 }
113
114 virtual bool CanBeDeleted() const { return true; }
115
116 private:
117 virtual bool DoSetShape(GdkWindow* window)
118 {
119 gdk_window_shape_combine_region(window, m_region.GetRegion(), 0, 0);
120
121 return true;
122 }
123
124 wxRegion m_region;
125 };
126
127 #if wxUSE_GRAPHICS_CONTEXT
128
129 // Version using more complex wxGraphicsPath.
130 class wxNonOwnedWindowShapeImplPath : public wxNonOwnedWindowShapeImpl
131 {
132 public:
133 wxNonOwnedWindowShapeImplPath(wxWindow* win, const wxGraphicsPath& path) :
134 wxNonOwnedWindowShapeImpl(win),
135 m_path(path),
136 m_mask(CreateShapeBitmap(path), *wxBLACK)
137 {
138
139 m_win->Connect
140 (
141 wxEVT_PAINT,
142 wxPaintEventHandler(wxNonOwnedWindowShapeImplPath::OnPaint),
143 NULL,
144 this
145 );
146 }
147
148 virtual ~wxNonOwnedWindowShapeImplPath()
149 {
150 m_win->Disconnect
151 (
152 wxEVT_PAINT,
153 wxPaintEventHandler(wxNonOwnedWindowShapeImplPath::OnPaint),
154 NULL,
155 this
156 );
157 }
158
159 // Currently we always return false from here, if drawing the border
160 // becomes optional, we could return true if we don't need to draw it.
161 virtual bool CanBeDeleted() const { return false; }
162
163 private:
164 wxBitmap CreateShapeBitmap(const wxGraphicsPath& path)
165 {
166 // Draw the path on a bitmap to get the mask we need.
167 //
168 // Notice that using monochrome bitmap here doesn't work because of an
169 // apparent wxGraphicsContext bug in wxGTK, so use a bitmap of screen
170 // depth even if this is wasteful.
171 wxBitmap bmp(m_win->GetSize());
172
173 wxMemoryDC dc(bmp);
174
175 dc.SetBackground(*wxBLACK);
176 dc.Clear();
177
178 #ifdef __WXGTK3__
179 wxGraphicsContext* context = dc.GetGraphicsContext();
180 #else
181 wxScopedPtr<wxGraphicsContext> context(wxGraphicsContext::Create(dc));
182 #endif
183 context->SetBrush(*wxWHITE);
184 context->FillPath(path);
185
186 return bmp;
187 }
188
189 virtual bool DoSetShape(GdkWindow *window)
190 {
191 if (!m_mask)
192 return false;
193
194 #ifdef __WXGTK3__
195 cairo_region_t* region = gdk_cairo_region_create_from_surface(m_mask);
196 gdk_window_shape_combine_region(window, region, 0, 0);
197 cairo_region_destroy(region);
198 #else
199 gdk_window_shape_combine_mask(window, m_mask, 0, 0);
200 #endif
201
202 return true;
203 }
204
205 // Draw a shaped window border.
206 void OnPaint(wxPaintEvent& event)
207 {
208 event.Skip();
209
210 wxPaintDC dc(m_win);
211 #ifdef __WXGTK3__
212 wxGraphicsContext* context = dc.GetGraphicsContext();
213 #else
214 wxScopedPtr<wxGraphicsContext> context(wxGraphicsContext::Create(dc));
215 #endif
216 context->SetPen(wxPen(*wxLIGHT_GREY, 2));
217 context->StrokePath(m_path);
218 }
219
220 wxGraphicsPath m_path;
221 wxMask m_mask;
222 };
223
224 #endif // wxUSE_GRAPHICS_CONTEXT
225
226 // ============================================================================
227 // wxNonOwnedWindow implementation
228 // ============================================================================
229
230 wxNonOwnedWindow::~wxNonOwnedWindow()
231 {
232 delete m_shapeImpl;
233 }
234
235 void wxNonOwnedWindow::GTKHandleRealized()
236 {
237 wxNonOwnedWindowBase::GTKHandleRealized();
238
239 if ( m_shapeImpl )
240 {
241 m_shapeImpl->SetShape();
242
243 // We can destroy wxNonOwnedWindowShapeImplRegion immediately but need
244 // to keep wxNonOwnedWindowShapeImplPath around as it draws the border
245 // on every repaint.
246 if ( m_shapeImpl->CanBeDeleted() )
247 {
248 delete m_shapeImpl;
249 m_shapeImpl = NULL;
250 }
251 }
252 }
253
254 bool wxNonOwnedWindow::DoClearShape()
255 {
256 if ( !m_shapeImpl )
257 {
258 // Nothing to do, we don't have any custom shape.
259 return true;
260 }
261
262 if ( gtk_widget_get_realized(m_widget) )
263 {
264 // Reset the existing shape immediately.
265 wxNonOwnedWindowShapeImplNone data(this);
266 data.SetShape();
267 }
268 //else: just do nothing, deleting m_shapeImpl is enough to ensure that we
269 // don't set the custom shape later when we're realized.
270
271 delete m_shapeImpl;
272 m_shapeImpl = NULL;
273
274 return true;
275 }
276
277 bool wxNonOwnedWindow::DoSetRegionShape(const wxRegion& region)
278 {
279 // In any case get rid of the old data.
280 delete m_shapeImpl;
281 m_shapeImpl = NULL;
282
283 if ( gtk_widget_get_realized(m_widget) )
284 {
285 // We can avoid an unnecessary heap allocation and just set the shape
286 // immediately.
287 wxNonOwnedWindowShapeImplRegion data(this, region);
288 return data.SetShape();
289 }
290 else // Create an object that will set shape when we're realized.
291 {
292 m_shapeImpl = new wxNonOwnedWindowShapeImplRegion(this, region);
293
294 // In general we don't know whether we are going to succeed or not, so
295 // be optimistic.
296 return true;
297 }
298 }
299
300 #if wxUSE_GRAPHICS_CONTEXT
301
302 bool wxNonOwnedWindow::DoSetPathShape(const wxGraphicsPath& path)
303 {
304 // The logic here is simpler than above because we always create
305 // wxNonOwnedWindowShapeImplPath on the heap as we must keep it around,
306 // even if we're already realized
307
308 delete m_shapeImpl;
309 m_shapeImpl = new wxNonOwnedWindowShapeImplPath(this, path);
310
311 if ( gtk_widget_get_realized(m_widget) )
312 {
313 return m_shapeImpl->SetShape();
314 }
315 //else: will be done later from GTKHandleRealized().
316
317 return true;
318 }
319
320 #endif // wxUSE_GRAPHICS_CONTEXT