QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskItemAnchors.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskItemAnchors.h"
7#include "QskMargins.h"
8
9QSK_QT_PRIVATE_BEGIN
10#include <private/qquickanchors_p.h>
11#include <private/qquickanchors_p_p.h>
12#include <private/qquickitem_p.h>
13QSK_QT_PRIVATE_END
14
15namespace
16{
17 struct AnchorLineOperators
18 {
19 QQuickAnchorLine ( QQuickAnchors::*line ) () const;
20 void ( QQuickAnchors::*setLine )( const QQuickAnchorLine& );
21 void ( QQuickAnchors::*resetLine )();
22 };
23
24 class UpdateBlocker
25 {
26 public:
27 UpdateBlocker( QQuickAnchors* anchors )
28 : m_anchors( anchors )
29 {
30 auto d = QQuickAnchorsPrivate::get( anchors );
31
32 m_wasComplete = d->componentComplete;
33 d->componentComplete = false;
34 }
35
36 ~UpdateBlocker()
37 {
38 reset();
39 }
40
41 void reset() const
42 {
43 auto d = QQuickAnchorsPrivate::get( m_anchors );
44 d->componentComplete = m_wasComplete;
45 }
46 private:
47 QQuickAnchors* m_anchors;
48 bool m_wasComplete;
49 };
50}
51
52static inline QQuickAnchors* qskGetOrCreateAnchors( QQuickItem* item )
53{
54 if ( item == nullptr )
55 return nullptr;
56
57 return QQuickItemPrivate::get( item )->anchors();
58}
59
60static inline QQuickAnchors* qskGetAnchors( QQuickItem* item )
61{
62 if ( item == nullptr )
63 return nullptr;
64
65 return QQuickItemPrivate::get( item )->_anchors;
66}
67
68static inline const QQuickAnchors* qskGetAnchors( const QQuickItem* item )
69{
70 return qskGetAnchors( const_cast< QQuickItem* >( item ) );
71}
72
73static inline QQuickAnchors::Anchor qskToQuickAnchor( Qt::AnchorPoint edge )
74{
75 using A = QQuickAnchors;
76
77 switch( edge )
78 {
79 case Qt::AnchorLeft: return A::LeftAnchor;
80 case Qt::AnchorHorizontalCenter: return A::HCenterAnchor;
81 case Qt::AnchorRight: return A::RightAnchor;
82 case Qt::AnchorTop: return A::TopAnchor;
83 case Qt::AnchorVerticalCenter: return A::VCenterAnchor;
84 case Qt::AnchorBottom: return A::BottomAnchor;
85 default: return A::InvalidAnchor;
86 }
87}
88
89static inline Qt::AnchorPoint qskToAnchorPoint( QQuickAnchors::Anchor anchor )
90{
91 using A = QQuickAnchors;
92
93 switch( anchor )
94 {
95 case A::LeftAnchor: return Qt::AnchorLeft;
96 case A::HCenterAnchor: return Qt::AnchorHorizontalCenter;
97 case A::RightAnchor: return Qt::AnchorRight;
98 case A::TopAnchor: return Qt::AnchorTop;
99 case A::VCenterAnchor: return Qt::AnchorVerticalCenter;
100 case A::BottomAnchor: return Qt::AnchorBottom;
101 case A::BaselineAnchor: return Qt::AnchorTop; // not supported
102 default: return Qt::AnchorLeft;
103 }
104}
105
106static inline bool qskIsCenterAnchorPoint( Qt::AnchorPoint edge )
107{
108 return ( edge == Qt::AnchorHorizontalCenter )
109 || ( edge == Qt::AnchorVerticalCenter );
110}
111
112static inline const AnchorLineOperators& qskAnchorLineOperators( Qt::AnchorPoint edge )
113{
114 using A = QQuickAnchors;
115
116 static constexpr AnchorLineOperators table[] =
117 {
118 // in order of Qt::AnchorPoint
119
120 { &A::left, &A::setLeft, &A::resetLeft },
121 { &A::horizontalCenter, &A::setHorizontalCenter, &A::resetHorizontalCenter },
122 { &A::right, &A::setRight, &A::resetRight },
123 { &A::top, &A::setTop, &A::resetTop },
124 { &A::verticalCenter, &A::setVerticalCenter, &A::resetVerticalCenter },
125 { &A::bottom, &A::setBottom, &A::resetBottom }
126 };
127
128 return table[edge];
129}
130
131QskItemAnchors::QskItemAnchors( QQuickItem* attachedItem )
132 : m_attachedItem( attachedItem )
133{
134}
135
136QskItemAnchors::~QskItemAnchors()
137{
138}
139
140bool QskItemAnchors::operator==( const QskItemAnchors& other ) const noexcept
141{
142 return m_attachedItem.data() == other.m_attachedItem.data();
143}
144
145QQuickItem* QskItemAnchors::attachedItem() const
146{
147 return m_attachedItem;
148}
149
150QMarginsF QskItemAnchors::margins() const
151{
152 const auto anchors = qskGetAnchors( m_attachedItem );
153 if ( anchors == nullptr )
154 return QMarginsF();
155
156 const auto d = QQuickAnchorsPrivate::get( anchors );
157
158 const auto left = d->leftMarginExplicit ? d->leftMargin : d->margins;
159 const auto right = d->rightMarginExplicit ? d->rightMargin : d->margins;
160 const auto top = d->rightMarginExplicit ? d->rightMargin : d->margins;
161 const auto bottom = d->bottomMarginExplicit ? d->bottomMargin : d->margins;
162
163 return QMarginsF( left, top, right, bottom );
164}
165
166void QskItemAnchors::setMargins( const QMarginsF& margins )
167{
168 auto anchors = qskGetOrCreateAnchors( m_attachedItem );
169 if ( anchors == nullptr )
170 return;
171
172 const auto oldMargins = this->margins();
173
174 Qt::Orientations changes;
175
176 if ( margins.left() != oldMargins.left() )
177 changes |= Qt::Horizontal;
178
179 if ( margins.right() != oldMargins.right() )
180 changes |= Qt::Horizontal;
181
182 if ( margins.top() != oldMargins.top() )
183 changes |= Qt::Vertical;
184
185 if ( margins.bottom() != oldMargins.bottom() )
186 changes |= Qt::Vertical;
187
188 if ( changes )
189 {
190 const auto left = margins.left();
191
192 UpdateBlocker blocker( anchors );
193
194 anchors->setLeftMargin( left + 1.0 );
195 anchors->setRightMargin( margins.right() );
196 anchors->setTopMargin( margins.top() );
197 anchors->setBottomMargin( margins.bottom() );
198
199 blocker.reset();
200
201 anchors->setLeftMargin( left ); // trigger final update
202 }
203}
204
205void QskItemAnchors::setCenterOffsets(
206 qreal horizontalOffset, qreal verticalOffset )
207{
208 if ( auto anchors = qskGetOrCreateAnchors( m_attachedItem ) )
209 {
210 anchors->setHorizontalCenterOffset( horizontalOffset );
211 anchors->setVerticalCenterOffset( verticalOffset );
212 }
213}
214
215void QskItemAnchors::setCenterOffset( Qt::Orientation orientation, qreal offset )
216{
217 auto anchors = qskGetOrCreateAnchors( m_attachedItem );
218 if ( anchors == nullptr )
219 return;
220
221 if ( orientation == Qt::Horizontal )
222 anchors->setHorizontalCenterOffset( offset );
223 else
224 anchors->setVerticalCenterOffset( offset );
225}
226
227qreal QskItemAnchors::centerOffset( Qt::Orientation orientation )
228{
229 if ( const auto anchors = qskGetAnchors( m_attachedItem ) )
230 {
231 if ( orientation == Qt::Horizontal )
232 return anchors->horizontalCenterOffset();
233 else
234 return anchors->verticalCenterOffset();
235 }
236
237 return 0.0;
238}
239
240QQuickItem* QskItemAnchors::settledItem( Qt::AnchorPoint edge ) const
241{
242 const auto anchors = qskGetAnchors( m_attachedItem );
243 if ( anchors == nullptr )
244 return nullptr;
245
246 if ( auto fill = anchors->fill() )
247 return !qskIsCenterAnchorPoint( edge ) ? fill : nullptr;
248
249 if ( auto centerIn = anchors->centerIn() )
250 return qskIsCenterAnchorPoint( edge ) ? centerIn : nullptr;
251
252 const auto& ops = qskAnchorLineOperators( edge );
253 return ( ( anchors->*ops.line ) () ).item;
254}
255
256void QskItemAnchors::addAnchors( Qt::Corner corner,
257 QQuickItem* settledItem, Qt::Corner settledItemCorner )
258{
259 auto anchorPoint =
260 []( Qt::Corner corner, Qt::Orientation orientation )
261 {
262 if ( orientation == Qt::Horizontal )
263 return ( corner & 0x1 ) ? Qt::AnchorRight : Qt::AnchorLeft;
264 else
265 return ( corner >= 0x2 ) ? Qt::AnchorBottom : Qt::AnchorTop;
266 };
267
268 addAnchor( anchorPoint( corner, Qt::Horizontal ),
269 settledItem, anchorPoint( settledItemCorner, Qt::Horizontal ) );
270
271 addAnchor( anchorPoint( corner, Qt::Vertical ),
272 settledItem, anchorPoint( settledItemCorner, Qt::Vertical ) );
273}
274
275void QskItemAnchors::addAnchor( Qt::AnchorPoint edge, QQuickItem* settledItem,
276 Qt::AnchorPoint settledItemEdge )
277{
278 if ( settledItem == nullptr )
279 return;
280
281 if ( const auto anchors = qskGetOrCreateAnchors( m_attachedItem ) )
282 {
283 const auto& ops = qskAnchorLineOperators( edge );
284 ( anchors->*ops.setLine )( { settledItem, qskToQuickAnchor( settledItemEdge ) } );
285 }
286}
287
288void QskItemAnchors::removeAnchor( Qt::AnchorPoint edge )
289{
290 const auto anchors = qskGetAnchors( m_attachedItem );
291 if ( anchors == nullptr )
292 return;
293
294 if ( auto fill = anchors->fill() )
295 {
296 if ( !qskIsCenterAnchorPoint( edge ) )
297 {
298 anchors->resetFill();
299
300 // setting the other borders as anchors
301 for ( auto anchorPoint : { Qt::AnchorLeft, Qt::AnchorRight,
302 Qt::AnchorTop, Qt::AnchorBottom } )
303 {
304 if ( edge != anchorPoint )
305 addAnchor( anchorPoint, fill, anchorPoint );
306 }
307 }
308
309 return;
310 }
311
312 if ( auto centerIn = anchors->centerIn() )
313 {
314 if ( qskIsCenterAnchorPoint( edge ) )
315 {
316 anchors->resetCenterIn();
317
318 // setting the other direction as anchor
319 const auto otherEdge = ( edge == Qt::AnchorHorizontalCenter )
320 ? Qt::AnchorVerticalCenter : Qt::AnchorHorizontalCenter;
321
322 addAnchor( otherEdge, centerIn, otherEdge );
323 }
324
325 return;
326 }
327
328 const auto& ops = qskAnchorLineOperators( edge );
329 ( anchors->*ops.resetLine ) ();
330}
331
332void QskItemAnchors::clearAnchors()
333{
334 const auto anchors = qskGetAnchors( m_attachedItem );
335 if ( anchors == nullptr )
336 return;
337
338 const UpdateBlocker blocker( anchors );
339
340 anchors->resetFill();
341 anchors->resetCenterIn();
342
343 for ( int i = 0; i < 6; i++ )
344 {
345 const auto& ops = qskAnchorLineOperators( static_cast< Qt::AnchorPoint >( i ) );
346 ( anchors->*ops.resetLine ) ();
347 }
348}
349
350void QskItemAnchors::setBorderAnchors(
351 QQuickItem* settledItem, Qt::Orientations orientations )
352{
353 if ( settledItem == nullptr || m_attachedItem == nullptr )
354 return;
355
356 auto anchors = qskGetOrCreateAnchors( m_attachedItem );
357
358 switch( orientations )
359 {
360 case Qt::Horizontal:
361 {
362 clearAnchors();
363
364 const UpdateBlocker blocker( anchors );
365 addAnchor( Qt::AnchorLeft, settledItem, Qt::AnchorLeft );
366 blocker.reset();
367
368 addAnchor( Qt::AnchorRight, settledItem, Qt::AnchorRight );
369
370
371 break;
372 }
373
374 case Qt::Vertical:
375 {
376 clearAnchors();
377
378 const UpdateBlocker blocker( anchors );
379 addAnchor( Qt::AnchorTop, settledItem, Qt::AnchorTop );
380 blocker.reset();
381
382 addAnchor( Qt::AnchorBottom, settledItem, Qt::AnchorBottom );
383
384 break;
385 }
386
387 case Qt::Horizontal | Qt::Vertical:
388 {
389 if ( settledItem != anchors->fill() )
390 {
391 clearAnchors();
392 anchors->setFill( settledItem );
393 }
394 break;
395 }
396 }
397}
398
399void QskItemAnchors::setCenterAnchors(
400 QQuickItem* settledItem, Qt::Orientations orientations )
401{
402 if ( settledItem == nullptr || m_attachedItem == nullptr )
403 return;
404
405 switch( orientations )
406 {
407 case Qt::Horizontal:
408 {
409 clearAnchors();
410 addAnchor( Qt::AnchorHorizontalCenter,
411 settledItem, Qt::AnchorHorizontalCenter );
412 break;
413 }
414
415 case Qt::Vertical:
416 {
417 clearAnchors();
418 addAnchor( Qt::AnchorVerticalCenter,
419 settledItem, Qt::AnchorVerticalCenter );
420 break;
421 }
422
423 case Qt::Horizontal | Qt::Vertical:
424 {
425 auto anchors = qskGetOrCreateAnchors( m_attachedItem );
426
427 if ( settledItem != anchors->centerIn() )
428 {
429 clearAnchors();
430 anchors->setCenterIn( settledItem );
431 }
432 break;
433 }
434 }
435}
436
437Qt::AnchorPoint QskItemAnchors::settledItemAnchorPoint( Qt::AnchorPoint edge ) const
438{
439 if ( const auto anchors = qskGetAnchors( m_attachedItem ) )
440 {
441 /*
442 Anchoring to the baseline of the attachedItem might have been
443 done in QML code. As Qt::AnchorPoint does not have a corresponding
444 value for it and QSkinny does not support the baseline concept at all
445 we are lying and report Qt::AnchorTop instead. Hm ...
446 */
447
448 const auto& ops = qskAnchorLineOperators( edge );
449 return qskToAnchorPoint( ( ( anchors->*ops.line ) () ).anchorLine );
450 }
451
452 return Qt::AnchorLeft; // something
453}