QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskSliderSkinlet.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskSliderSkinlet.h"
7#include "QskSlider.h"
8#include "QskFunctions.h"
9
10#include <qvector.h>
11#include <qpair.h>
12#include <qmath.h>
13
14// the color of graduation ticks might different, when being on top of the filling
15QSK_SYSTEM_STATE( QskSliderSkinlet, Filled, QskAspect::FirstUserState >> 1 )
16
17using Q = QskSlider;
18
19static QRectF qskInnerRect( const QskSlider* slider,
20 const QRectF& contentsRect, QskAspect::Subcontrol subControl )
21{
22 auto r = slider->subControlContentsRect( contentsRect, Q::Panel );
23
24 QskSkinHintStatus status;
25
26 const qreal extent = slider->metric( subControl | QskAspect::Size, &status );
27
28 if ( slider->orientation() == Qt::Horizontal )
29 {
30 if ( status.isValid() && ( extent < r.height() ) )
31 {
32 r.setTop( r.center().y() - 0.5 * extent );
33 r.setHeight( extent );
34 }
35 }
36 else
37 {
38 if ( status.isValid() && ( extent < r.width() ) )
39 {
40 r.setLeft( r.center().x() - 0.5 * extent );
41 r.setWidth( extent );
42 }
43 }
44
45 return r;
46}
47
48static inline bool qskHasGraduation( const QskSlider* slider )
49{
50 if ( slider->stepSize() )
51 {
52 switch( slider->graduationPolicy() )
53 {
54 case Qsk::Always:
55 return true;
56
57 case Qsk::Maybe:
58 return slider->isSnapping();
59
60 case Qsk::Never:
61 return false;
62 }
63 }
64
65 return false;
66}
67
68static inline QPair< qreal, qreal > qskTickSpan( qreal min, qreal max, qreal length )
69{
70 if ( length >= 0.0 )
71 {
72 // using the center of [min,max]
73 min += 0.5 * ( max - min - length );
74 max = min + length;
75 }
76
77 return { min, max };
78}
79
80
81QskSliderSkinlet::QskSliderSkinlet( QskSkin* skin )
82 : Inherited( skin )
83{
84 setNodeRoles( { PanelRole, GrooveRole, FillRole, TicksRole, HandleRole } );
85}
86
87QskSliderSkinlet::~QskSliderSkinlet()
88{
89}
90
91QRectF QskSliderSkinlet::subControlRect( const QskSkinnable* skinnable,
92 const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
93{
94 const auto slider = static_cast< const QskSlider* >( skinnable );
95
96 if ( subControl == Q::Panel )
97 return panelRect( slider, contentsRect );
98
99 if ( subControl == Q::Groove )
100 return qskInnerRect( slider, contentsRect, Q::Groove );
101
102 if ( subControl == Q::Fill )
103 return fillRect( slider, contentsRect );
104
105 if ( subControl == Q::Scale )
106 return subControlRect( skinnable, contentsRect, Q::Groove );
107
108 if ( subControl == Q::Handle )
109 return handleRect( slider, contentsRect );
110
111 return Inherited::subControlRect( skinnable, contentsRect, subControl );
112}
113
114QSGNode* QskSliderSkinlet::updateSubNode(
115 const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
116{
117 const auto slider = static_cast< const QskSlider* >( skinnable );
118
119 switch ( nodeRole )
120 {
121 case PanelRole:
122 return updateBoxNode( slider, node, Q::Panel );
123
124 case GrooveRole:
125 return updateBoxNode( slider, node, Q::Groove );
126
127 case FillRole:
128 return updateBoxNode( slider, node, Q::Fill );
129
130 case HandleRole:
131 return updateBoxNode( slider, node, Q::Handle );
132
133 case TicksRole:
134 return updateSeriesNode( slider, Q::Tick, node );
135 }
136
137 return Inherited::updateSubNode( skinnable, nodeRole, node );
138}
139
140int QskSliderSkinlet::sampleCount( const QskSkinnable* skinnable,
141 QskAspect::Subcontrol subControl ) const
142{
143 if ( subControl == Q::Tick )
144 {
145 const auto slider = static_cast< const QskSlider* >( skinnable );
146 return graduation( slider ).count();
147 }
148
149 return Inherited::sampleCount( skinnable, subControl );
150}
151
152QVariant QskSliderSkinlet::sampleAt( const QskSkinnable* skinnable,
153 QskAspect::Subcontrol subControl, int index ) const
154{
155 if ( subControl == Q::Tick )
156 {
157 const auto slider = static_cast< const QskSlider* >( skinnable );
158 return graduation( slider ).value( index );
159 }
160
161 return Inherited::sampleAt( skinnable, subControl, index );
162}
163
164QskAspect::States QskSliderSkinlet::sampleStates(
165 const QskSkinnable* skinnable, QskAspect::Subcontrol subControl, int index ) const
166{
167 auto states = Inherited::sampleStates( skinnable, subControl, index );
168
169 if ( subControl == Q::Tick )
170 {
171 const auto tickValue = sampleAt( skinnable, subControl, index );
172 if ( tickValue.canConvert< qreal >() )
173 {
174 const auto slider = static_cast< const QskSlider* >( skinnable );
175
176 if ( tickValue.value< qreal >() <= slider->value() )
177 states |= Filled;
178 }
179 }
180
181 return states;
182}
183
184QRectF QskSliderSkinlet::sampleRect(
185 const QskSkinnable* skinnable, const QRectF& contentsRect,
186 QskAspect::Subcontrol subControl, int index ) const
187{
188 if ( subControl == Q::Tick )
189 {
190 const auto slider = static_cast< const QskSlider* >( skinnable );
191 return tickRect( slider, contentsRect, index );
192 }
193
194 return Inherited::sampleRect( skinnable, contentsRect, subControl, index );
195}
196
197QSGNode* QskSliderSkinlet::updateSampleNode( const QskSkinnable* skinnable,
198 QskAspect::Subcontrol subControl, int index, QSGNode* node ) const
199{
200 if ( subControl == Q::Tick )
201 {
202 const auto slider = static_cast< const QskSlider* >( skinnable );
203 const auto rect = sampleRect( slider, slider->contentsRect(), subControl, index );
204
205 return updateBoxNode( skinnable, node, rect, subControl );
206 }
207
208 return Inherited::updateSampleNode( skinnable, subControl, index, node );
209}
210
211QRectF QskSliderSkinlet::panelRect(
212 const QskSlider* slider, const QRectF& contentsRect ) const
213{
214 auto r = contentsRect;
215
216 const qreal size = slider->metric( Q::Panel | QskAspect::Size ); // 0: no hint
217 if ( size > 0 && size < r.height() )
218 {
219 const auto alignment = slider->alignmentHint( Q::Panel );
220
221 if ( slider->orientation() == Qt::Horizontal )
222 r = qskAlignedRectF( r, r.width(), size, alignment & Qt::AlignVertical_Mask );
223 else
224 r = qskAlignedRectF( r, size, r.height(), alignment & Qt::AlignHorizontal_Mask );
225 }
226
227 return r;
228}
229
230QRectF QskSliderSkinlet::fillRect(
231 const QskSlider* slider, const QRectF& contentsRect ) const
232{
233 const auto pos = qBound( 0.0, slider->handlePosition(), 1.0 );
234
235 auto r = qskInnerRect( slider, contentsRect, QskSlider::Fill );
236
237 auto scaleRect = subControlRect( slider, contentsRect, Q::Scale );
238
239 if ( slider->orientation() == Qt::Horizontal )
240 r.setRight( scaleRect.left() + pos * scaleRect.width() );
241 else
242 r.setTop( scaleRect.bottom() - pos * scaleRect.height() );
243
244 return r;
245}
246
247QRectF QskSliderSkinlet::handleRect(
248 const QskSlider* slider, const QRectF& contentsRect ) const
249{
250 auto handleSize = slider->strutSizeHint( Q::Handle );
251 const auto pos = qBound( 0.0, slider->handlePosition(), 1.0 );
252
253 const auto r = subControlRect( slider, contentsRect, Q::Scale );
254 auto center = r.center();
255
256 if ( slider->orientation() == Qt::Horizontal )
257 {
258 if ( handleSize.height() < 0.0 )
259 handleSize.setHeight( r.height() );
260
261 if ( handleSize.width() < 0.0 )
262 handleSize.setWidth( handleSize.height() );
263
264 center.setX( r.left() + pos * r.width() );
265 }
266 else
267 {
268 if ( handleSize.width() < 0.0 )
269 handleSize.setWidth( r.width() );
270
271 if ( handleSize.height() < 0.0 )
272 handleSize.setHeight( handleSize.width() );
273
274 center.setY( r.bottom() - pos * r.height() );
275 }
276
277 QRectF handleRect( 0, 0, handleSize.width(), handleSize.height() );
278 handleRect.moveCenter( center );
279
280 return handleRect;
281}
282
283QRectF QskSliderSkinlet::tickRect( const QskSlider* slider,
284 const QRectF& contentsRect, int index ) const
285{
286 const auto tickValue = sampleAt( slider, Q::Tick, index );
287 if ( !tickValue.canConvert< qreal >() )
288 return QRectF();
289
290 const auto tickPos = slider->valueAsRatio( tickValue.value< qreal >() );
291
292 const auto r = subControlRect( slider, contentsRect, Q::Scale );
293
294 const auto padding = slider->paddingHint( Q::Scale );
295 const auto size = slider->strutSizeHint( Q::Tick );
296
297 if( slider->orientation() == Qt::Horizontal )
298 {
299 const auto x = tickPos * r.width() - 0.5 * size.width();
300
301 const auto span = qskTickSpan(
302 padding.top(), r.height() - padding.bottom(), size.height() );
303
304 return QRectF( r.x() + x, r.y() + span.first,
305 size.width(), span.second - span.first );
306 }
307 else
308 {
309 const auto y = tickPos * r.height() + 0.5 * size.height();
310
311 const auto span = qskTickSpan(
312 padding.left(), r.width() - padding.right(), size.width() );
313
314 return QRectF( r.x() + span.first, r.bottom() - y,
315 span.second - span.first, size.height() );
316 }
317}
318
319QSizeF QskSliderSkinlet::sizeHint( const QskSkinnable* skinnable,
320 Qt::SizeHint which, const QSizeF& ) const
321{
322 if ( which != Qt::PreferredSize )
323 return QSizeF();
324
325 auto hint = skinnable->strutSizeHint( Q::Panel );
326 hint = hint.expandedTo( skinnable->strutSizeHint( Q::Groove ) );
327 hint = hint.expandedTo( skinnable->strutSizeHint( Q::Fill ) );
328 hint = hint.expandedTo( skinnable->strutSizeHint( Q::Handle ) );
329
330 return hint;
331}
332
333QVector< qreal > QskSliderSkinlet::graduation( const QskSlider* slider ) const
334{
335 QVector< qreal > graduation;
336
337 if ( qskHasGraduation( slider ) )
338 {
339 const auto from = slider->minimum();
340 const auto to = slider->maximum();
341
342 auto step = slider->stepSize();
343 if ( from > to )
344 step = -step;
345
346 const auto n = qCeil( ( to - from ) / step ) - 1;
347
348 graduation.reserve( n );
349
350 for ( int i = 1; i <= n; i++ )
351 graduation += from + i * step;
352 }
353
354 return graduation;
355}
356
357#include "moc_QskSliderSkinlet.cpp"
Lookup key for a QskSkinHintTable.
Definition QskAspect.h:15
@ FirstUserState
Definition QskAspect.h:116
Subcontrol
For use within the rendering or lay-outing of a specific QskSkinnable.
Definition QskAspect.h:104
QRectF subControlContentsRect(QskAspect::Subcontrol) const
QRectF contentsRect() const
bool isValid() const
QMarginsF paddingHint(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a padding hint.
QSizeF strutSizeHint(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a strut size hint.
qreal metric(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a metric hint.
Qt::Alignment alignmentHint(QskAspect, Qt::Alignment=Qt::Alignment()) const
Retrieves an alignment hint.