QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskBoxMetrics.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskBoxMetrics.h"
7#include "QskBoxShapeMetrics.h"
8#include "QskBoxBorderMetrics.h"
9#include "QskVertexHelper.h"
10#include "QskFunctions.h"
11
12QskBoxMetrics::QskBoxMetrics( const QRectF& rect,
13 const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border )
14 : outerRect( rect )
15{
16 isOutsideRounded = !shape.isRectangle();
17
18 if ( !isOutsideRounded )
19 {
20 isInsideRounded = false;
21 isOutsideSymmetric = true;
22 stepSymmetries = Qt::Vertical | Qt::Horizontal;
23 preferredOrientation = Qt::Vertical;
24
25 const auto bw = border.widths();
26 hasBorder = bw.width() > 0.0 || bw.height() > 0.0;
27
28 innerRect = qskValidOrEmptyInnerRect( rect, bw );
29
30 return;
31 }
32
33 isOutsideSymmetric = shape.isRectellipse();
34
35 {
36 const auto tl = shape.topLeft();
37 const auto tr = shape.topRight();
38 const auto bl = shape.bottomLeft();
39 const auto br = shape.bottomRight();
40
41 if ( tl.isEmpty() || tr.isEmpty() || ( tl.height() == tr.height() ) )
42 {
43 if ( bl.isEmpty() || br.isEmpty() || ( bl.height() == br.height() ) )
44 stepSymmetries |= Qt::Vertical;
45 }
46
47 if ( tl.isEmpty() || bl.isEmpty() || ( tl.width() == bl.width() ) )
48 {
49 if ( tr.isEmpty() || br.isEmpty() || ( tr.width() == br.width() ) )
50 stepSymmetries |= Qt::Horizontal;
51 }
52 }
53
54 for ( int i = 0; i < 4; i++ )
55 {
56 auto& c = corners[ i ];
57
58 const auto radius = shape.radius( static_cast< Qt::Corner >( i ) );
59
60 c.radiusX = qBound( 0.0, radius.width(), 0.5 * outerRect.width() );
61 c.radiusY = qBound( 0.0, radius.height(), 0.5 * outerRect.height() );
62 c.stepCount = QskVertex::ArcIterator::segmentHint( qMax( c.radiusX, c.radiusY ) );
63
64 switch ( i )
65 {
66 case Qt::TopLeftCorner:
67 c.centerX = outerRect.left() + c.radiusX;
68 c.centerY = outerRect.top() + c.radiusY;
69 c.sx = -1.0;
70 c.sy = -1.0;
71 break;
72
73 case Qt::TopRightCorner:
74 c.centerX = outerRect.right() - c.radiusX;
75 c.centerY = outerRect.top() + c.radiusY;
76 c.sx = +1.0;
77 c.sy = -1.0;
78 break;
79
80 case Qt::BottomLeftCorner:
81 c.centerX = outerRect.left() + c.radiusX;
82 c.centerY = outerRect.bottom() - c.radiusY;
83 c.sx = -1.0;
84 c.sy = +1.0;
85 break;
86
87 case Qt::BottomRightCorner:
88 c.centerX = outerRect.right() - c.radiusX;
89 c.centerY = outerRect.bottom() - c.radiusY;
90 c.sx = +1.0;
91 c.sy = +1.0;
92 break;
93 }
94 }
95
96 {
97 const auto cleft = qMax( corners[ Qt::TopLeftCorner ].centerX,
98 corners[ Qt::BottomLeftCorner ].centerX );
99
100 const auto cright = qMin( corners[ Qt::TopRightCorner ].centerX,
101 corners[ Qt::BottomRightCorner ].centerX );
102
103 const auto ctop = qMax( corners[ Qt::TopLeftCorner ].centerY,
104 corners[ Qt::TopRightCorner ].centerY );
105
106 const auto cbottom = qMin( corners[ Qt::BottomLeftCorner ].centerY,
107 corners[ Qt::BottomRightCorner ].centerY );
108
109 // now the bounding rectangle of the fill area
110
111 const auto bw = border.widths();
112 hasBorder = bw.width() > 0.0 || bw.height() > 0.0;
113
114 qreal l = outerRect.left() + bw.left();
115 qreal t = outerRect.top() + bw.top();
116 qreal r = outerRect.right() - bw.right();
117 qreal b = outerRect.bottom() - bw.bottom();
118
119 l = qMin( l, cright );
120 r = qMax( r, cleft );
121 t = qMin( t, cbottom );
122 b = qMax( b, ctop );
123
124 if ( l > r )
125 l = r = r + 0.5 * ( l - r );
126
127 if ( t > b )
128 t = b = b + 0.5 * ( t - b );
129
130 innerRect.setCoords( l, t, r, b );
131 }
132
133 const QskMargins margins(
134 innerRect.left() - outerRect.left(),
135 innerRect.top() - outerRect.top(),
136 outerRect.right() - innerRect.right(),
137 outerRect.bottom() - innerRect.bottom() );
138
139 isBorderRegular = margins.isEquidistant();
140
141 isInsideRounded = false;
142
143 for ( int i = 0; i < 4; i++ )
144 {
145 auto& c = corners[ i ];
146
147 if ( c.sx < 0.0 )
148 c.radiusInnerX = c.radiusX - margins.left();
149 else
150 c.radiusInnerX = c.radiusX - margins.right();
151
152 if ( c.sy < 0.0 )
153 c.radiusInnerY = c.radiusY - margins.top();
154 else
155 c.radiusInnerY = c.radiusY - margins.bottom();
156
157 if ( c.radiusInnerX > 0.0 && c.radiusInnerY > 0.0 )
158 {
159 c.centerInnerX = c.centerX;
160 c.centerInnerY = c.centerY;
161
162 isInsideRounded = true;
163 }
164 else
165 {
166 /*
167 not enough space for a rounded border -> the inner side
168 becomes rectangular
169 */
170 c.radiusInnerX = c.radiusInnerY = 0.0;
171 c.centerInnerX = ( c.sx < 0.0 ) ? innerRect.left() : innerRect.right();
172 c.centerInnerY = ( c.sy < 0.0 ) ? innerRect.top() : innerRect.bottom();
173 }
174 }
175
176 {
177 const auto tl = corners[ Qt::TopLeftCorner ].innerStepCount();
178 const auto tr = corners[ Qt::TopRightCorner ].innerStepCount();
179 const auto bl = corners[ Qt::BottomLeftCorner ].innerStepCount();
180 const auto br = corners[ Qt::BottomRightCorner ].innerStepCount();
181
182 if ( qMax( tl, tr ) + qMax( bl, br ) >= qMax( tl, bl ) + qMax( tr, br ) )
183 preferredOrientation = Qt::Vertical;
184 else
185 preferredOrientation = Qt::Horizontal;
186 }
187}
188
189int QskBoxMetrics::outerStepCount() const
190{
191 return corners[0].stepCount + corners[1].stepCount
192 + corners[2].stepCount + corners[3].stepCount;
193}
194
195int QskBoxMetrics::innerStepCount() const
196{
197 return corners[0].innerStepCount() + corners[1].innerStepCount()
198 + corners[2].innerStepCount() + corners[3].innerStepCount();
199}