QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskSpinBoxSkinlet.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskSpinBoxSkinlet.h"
7#include "QskSpinBox.h"
8#include "QskFunctions.h"
9#include "QskSkinStateChanger.h"
10
11#include <qfontmetrics.h>
12#include <qcursor.h>
13
14static bool qskIsButtonHovered(
15 const QskSpinBox* spinBox, QskAspect::Subcontrol subControl )
16{
17 if ( spinBox->hasSkinState( QskControl::Hovered ) )
18 {
19 // disable Hovered to avoid recursive calls
20 QskSkinStateChanger stateChanger( spinBox );
21 stateChanger.setStates( spinBox->skinStates() & ~QskControl::Hovered );
22
23 const auto r = spinBox->subControlRect( subControl );
24 if ( !r.isEmpty() )
25 {
26 const auto pos = spinBox->mapFromGlobal( QCursor::pos() );
27 return r.contains( pos );
28 }
29 }
30
31 return false;
32}
33
34static inline QskAspect::States qskButtonStates(
35 const QskSkinnable* skinnable, QskAspect::Subcontrol subControl )
36{
37 using Q = QskSpinBox;
38
39 auto spinBox = static_cast< const QskSpinBox* >( skinnable );
40
41 auto states = spinBox->skinStates();
42
43 if ( spinBox->isEnabled() )
44 {
45 if ( subControl == Q::DownIndicator || subControl == Q::DownPanel )
46 {
47 if ( !spinBox->isWrapping() && spinBox->value() <= spinBox->minimum() )
48 states |= QskControl::Disabled;
49
50 if ( !qskIsButtonHovered( spinBox, Q::DownPanel ) )
51 states &= ~Q::Hovered;
52 }
53 else if ( subControl == Q::UpIndicator || subControl == Q::UpPanel )
54 {
55 if ( !spinBox->isWrapping() && spinBox->value() >= spinBox->maximum() )
56 states |= QskControl::Disabled;
57
58 if ( !qskIsButtonHovered( spinBox, Q::UpPanel ) )
59 states &= ~Q::Hovered;
60 }
61 }
62
63 return states;
64}
65
66QskSpinBoxSkinlet::QskSpinBoxSkinlet( QskSkin* )
67{
68 setNodeRoles( { PanelRole, UpPanelRole, DownPanelRole, TextPanelRole,
69 UpIndicatorRole, DownIndicatorRole, TextRole } );
70}
71
72QRectF QskSpinBoxSkinlet::subControlRect( const QskSkinnable* skinnable,
73 const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
74{
75 using Q = QskSpinBox;
76
77 QskSkinStateChanger stateChanger( skinnable );
78 stateChanger.setStates( qskButtonStates( skinnable, subControl ) );
79
80 if ( subControl == Q::Panel )
81 return contentsRect;
82
83 if ( subControl == Q::DownIndicator )
84 return skinnable->subControlContentsRect( contentsRect, Q::DownPanel );
85
86 if ( subControl == Q::UpIndicator )
87 return skinnable->subControlContentsRect( contentsRect, Q::UpPanel );
88
89 if ( subControl == Q::Text )
90 return skinnable->subControlContentsRect( contentsRect, Q::TextPanel );
91
92 if ( subControl == Q::DownPanel || subControl == Q::UpPanel )
93 return buttonRect( skinnable, contentsRect, subControl );
94
95 if ( subControl == Q::TextPanel )
96 return textPanelRect( skinnable, contentsRect );
97
98 return Inherited::subControlRect( skinnable, contentsRect, subControl );
99}
100
101QSGNode* QskSpinBoxSkinlet::updateSubNode(
102 const QskSkinnable* skinnable, const quint8 nodeRole, QSGNode* const node ) const
103{
104 using Q = QskSpinBox;
105
106 QskSkinStateChanger stateChanger( skinnable );
107
108 switch( nodeRole )
109 {
110 case PanelRole:
111 {
112 return updateBoxNode( skinnable, node, Q::Panel );
113 }
114
115 case UpPanelRole:
116 {
117 stateChanger.setStates( qskButtonStates( skinnable, Q::UpPanel ) );
118 return updateBoxNode( skinnable, node, Q::UpPanel );
119 }
120
121 case DownPanelRole:
122 {
123 stateChanger.setStates( qskButtonStates( skinnable, Q::DownPanel ) );
124 return updateBoxNode( skinnable, node, Q::DownPanel );
125 }
126
127 case UpIndicatorRole:
128 {
129 stateChanger.setStates( qskButtonStates( skinnable, Q::UpIndicator ) );
130 return updateSymbolNode( skinnable, node, Q::UpIndicator );
131 }
132
133 case DownIndicatorRole:
134 {
135 stateChanger.setStates( qskButtonStates( skinnable, Q::DownIndicator ) );
136 return updateSymbolNode( skinnable, node, Q::DownIndicator );
137 }
138
139 case TextPanelRole:
140 {
141 return updateBoxNode( skinnable, node, Q::TextPanel );
142 }
143
144 case TextRole:
145 {
146 auto spinBox = static_cast< const QskSpinBox* >( skinnable );
147 return updateTextNode( spinBox, node, spinBox->valueText(), Q::Text );
148 }
149 }
150
151 return Inherited::updateSubNode( skinnable, nodeRole, node );
152}
153
154QRectF QskSpinBoxSkinlet::textPanelRect(
155 const QskSkinnable* skinnable, const QRectF& rect ) const
156{
157 using Q = QskSpinBox;
158
159 auto spinBox = static_cast< const QskSpinBox* >( skinnable );
160
161 auto r = spinBox->innerBox( Q::Panel, rect );
162
163 const auto decoration = spinBox->decoration();
164 if ( decoration == Q::NoDecoration )
165 return r;
166
167 const auto spacing = spinBox->spacingHint( Q::Panel );
168
169 if ( decoration == Q::UpDownControl )
170 {
171 const auto w = subControlRect( skinnable, rect, Q::UpPanel ).width();
172 if ( w > 0.0 )
173 r.setRight( r.right() - spacing - w );
174 }
175 else if ( decoration == Q::ButtonsRight )
176 {
177 const auto w1 = subControlRect( skinnable, rect, Q::DownPanel ).width();
178 if ( w1 > 0.0 )
179 r.setRight( r.right() - w1 - spacing );
180
181 const auto w2 = subControlRect( skinnable, rect, Q::UpPanel ).width();
182 if ( w2 > 0.0 )
183 r.setRight( r.right() - w2 - spacing );
184 }
185 else
186 {
187 const auto w1 = subControlRect( skinnable, rect, Q::DownPanel ).width();
188 if ( w1 > 0.0 )
189 r.setLeft( r.left() + w1 + spacing );
190
191 const auto w2 = subControlRect( skinnable, rect, Q::UpPanel ).width();
192 if ( w2 > 0.0 )
193 r.setRight( r.right() - w2 - spacing );
194 }
195
196 return r;
197}
198
199QRectF QskSpinBoxSkinlet::buttonRect( const QskSkinnable* skinnable,
200 const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
201{
202 using Q = QskSpinBox;
203
204 const auto spinBox = static_cast< const QskSpinBox* >( skinnable );
205
206 const auto rect = spinBox->innerBox( Q::Panel, contentsRect );
207
208 if ( const auto decoration = spinBox->decoration() )
209 {
210 qreal x, y, w, h;
211
212 if ( decoration == Q::UpDownControl )
213 {
214 const auto hint1 = spinBox->strutSizeHint( Q::UpPanel );
215 const auto hint2 = spinBox->strutSizeHint( Q::DownPanel );
216
217 w = std::max( hint1.width(), hint2.width() );
218 if ( w <= 0 )
219 w = rect.height();
220
221 h = 0.5 * rect.height();
222
223 x = rect.right() - w;
224 y = ( subControl == Q::UpPanel ) ? rect.top() : rect.bottom() - h;
225 }
226 else if ( decoration == Q::ButtonsRight )
227 {
228 const auto hint = spinBox->strutSizeHint( subControl );
229
230 h = hint.height();
231 if ( h <= 0.0 )
232 h = rect.height();
233
234 w = hint.width();
235 if ( w <= 0.0 )
236 w = h;
237
238 if( subControl == Q::UpPanel )
239 {
240 const auto downRect = subControlRect( skinnable, contentsRect, Q::DownPanel );
241 x = downRect.left() - w;
242 }
243 else
244 {
245 x = rect.right() - w;
246 }
247
248 y = rect.top() + 0.5 * ( rect.height() - h );
249 }
250 else
251 {
252 const auto hint = spinBox->strutSizeHint( subControl );
253
254 h = hint.height();
255 if ( h <= 0.0 )
256 h = rect.height();
257
258 w = hint.width();
259 if ( w <= 0.0 )
260 w = h;
261
262 x = ( subControl == Q::UpPanel ) ? rect.right() - w : rect.left();
263 y = rect.top() + 0.5 * ( rect.height() - h );
264 }
265
266 return QRectF( x, y, w, h );
267 }
268
269 return QRectF();
270}
271
272QSizeF QskSpinBoxSkinlet::sizeHint( const QskSkinnable* skinnable,
273 Qt::SizeHint which, const QSizeF& ) const
274{
275 if ( which != Qt::PreferredSize )
276 return QSizeF();
277
278 using Q = QskSpinBox;
279
280 const auto spinBox = static_cast< const QskSpinBox* >( skinnable );
281
282 QSizeF hint;
283
284 {
285 const QFontMetricsF fm( spinBox->effectiveFont( Q::Text ) );
286
287 // 18: QAbstractSpinBox does this
288 const auto w1 = qskHorizontalAdvance( fm,
289 spinBox->textFromValue( spinBox->minimum() ).left( 18 ) );
290
291 const auto w2 = qskHorizontalAdvance( fm,
292 spinBox->textFromValue( spinBox->maximum() ).left( 18 ) );
293
294 hint.setWidth( std::max( w1, w2 ) );
295 hint.setHeight( fm.height() );
296
297 hint = hint.grownBy( spinBox->paddingHint( Q::TextPanel ) );
298 hint = hint.expandedTo( spinBox->strutSizeHint( Q::TextPanel ) );
299 }
300
301 if ( const auto decoration = spinBox->decoration() )
302 {
303 const auto spacing = spinBox->spacingHint( Q::Panel );
304 const auto hintUp = spinBox->strutSizeHint( Q::UpPanel );
305 const auto hintDown = spinBox->strutSizeHint( Q::DownPanel );
306
307 if ( decoration == QskSpinBox::UpDownControl )
308 {
309 qreal w = std::max( hintDown.width(), hintUp.width() );
310
311 qreal h = 0.0;
312 if ( hintDown.height() >= 0.0 )
313 h += hintDown.height();
314
315 if ( hintUp.height() >= 0.0 )
316 h += hintUp.height();
317
318 hint.rwidth() += ( w >= 0.0 ) ? w : hint.height();
319 hint.rwidth() += spacing;
320
321 hint.rheight() = std::max( h, hint.height() );
322 }
323 else
324 {
325 if ( hintDown.width() > 0.0 )
326 hint.rwidth() += hintDown.width() + spacing;
327
328 if ( hintUp.width() > 0.0 )
329 hint.rwidth() += hintUp.width() + spacing;
330
331 const auto h = std::max( hintUp.height(), hintDown.height() );
332 hint.rheight() = qMax( h, hint.height() );
333 }
334 }
335
336 hint = hint.grownBy( spinBox->paddingHint( Q::Panel ) );
337 hint = hint.expandedTo( spinBox->strutSizeHint( Q::Panel ) );
338
339 return hint;
340}
341
342#include "moc_QskSpinBoxSkinlet.cpp"
Subcontrol
For use within the rendering or lay-outing of a specific QskSkinnable.
Definition QskAspect.h:104
QRectF subControlRect(QskAspect::Subcontrol) const
static const QskAspect::State Disabled
Definition QskControl.h:56
static const QskAspect::State Hovered
Definition QskControl.h:56
qreal spacingHint(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a spacing hint.
QMarginsF paddingHint(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a padding hint.
QFont effectiveFont(QskAspect) const
QSizeF strutSizeHint(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a strut size hint.
QRectF subControlContentsRect(const QRectF &, QskAspect::Subcontrol) const
Calculate the inner rectangle for subControl.
QRectF innerBox(QskAspect, const QRectF &outerBox) const
Calculate the rectangle, whith paddings, indentations being subtracted.
A control to edit, increment and decrement number values.
Definition QskSpinBox.h:12