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#include "QskIntervalF.h"
10
11#include <qvector.h>
12#include <qpair.h>
13#include <qmath.h>
14
15// the color of graduation ticks might different, when being on top of the filling
16QSK_SYSTEM_STATE( QskSliderSkinlet, Filled, QskAspect::FirstUserState >> 1 )
17
18using Q = QskSlider;
19
20static inline qreal qskSubcontrolExtent(
21 const QskSkinnable* skinnable, QskAspect::Subcontrol subControl )
22{
23 return skinnable->metric( subControl | QskAspect::Size, -1.0 );
24}
25
26static inline bool qskHasFilling( const QskSlider* slider )
27{
28 const auto policy = slider->flagHint< Qsk::Policy >(
29 Q::Fill | QskAspect::Option, Qsk::Always );
30
31 switch( policy )
32 {
33 case Qsk::Never:
34 return false;
35
36 case Qsk::Maybe:
37 return qskFuzzyCompare( slider->origin(), slider->minimum() );
38
39 default:
40 return true;
41 }
42}
43
44static QRectF qskInnerRect( const QskSlider* slider,
45 const QRectF& contentsRect, QskAspect::Subcontrol subControl )
46{
47 auto r = slider->subControlContentsRect( contentsRect, Q::Panel );
48
49 const qreal extent = qskSubcontrolExtent( slider, subControl );
50
51 if ( extent >= 0.0 )
52 {
53 if ( slider->orientation() == Qt::Horizontal )
54 {
55 if ( extent < r.height() )
56 {
57 r.setTop( r.center().y() - 0.5 * extent );
58 r.setHeight( extent );
59 }
60 }
61 else
62 {
63 if ( extent < r.width() )
64 {
65 r.setLeft( r.center().x() - 0.5 * extent );
66 r.setWidth( extent );
67 }
68 }
69 }
70
71 return r;
72}
73
74static inline QPair< qreal, qreal > qskTickSpan( qreal min, qreal max, qreal length )
75{
76 if ( length >= 0.0 )
77 {
78 // using the center of [min,max]
79 min += 0.5 * ( max - min - length );
80 max = min + length;
81 }
82
83 return { min, max };
84}
85
86
87QskSliderSkinlet::QskSliderSkinlet( QskSkin* skin )
88 : Inherited( skin )
89{
90 setNodeRoles( { PanelRole, GrooveRole, FillRole, TicksRole, HandleRole } );
91}
92
93QskSliderSkinlet::~QskSliderSkinlet()
94{
95}
96
97QRectF QskSliderSkinlet::subControlRect( const QskSkinnable* skinnable,
98 const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
99{
100 const auto slider = static_cast< const QskSlider* >( skinnable );
101
102 if ( subControl == Q::Panel )
103 return panelRect( slider, contentsRect );
104
105 if ( subControl == Q::Groove )
106 return qskInnerRect( slider, contentsRect, Q::Groove );
107
108 if ( subControl == Q::Fill )
109 return fillRect( slider, contentsRect );
110
111 if ( subControl == Q::Scale )
112 return subControlRect( skinnable, contentsRect, Q::Groove );
113
114 if ( subControl == Q::Handle )
115 return handleRect( slider, contentsRect );
116
117 return Inherited::subControlRect( skinnable, contentsRect, subControl );
118}
119
120QSGNode* QskSliderSkinlet::updateSubNode(
121 const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
122{
123 const auto slider = static_cast< const QskSlider* >( skinnable );
124
125 switch ( nodeRole )
126 {
127 case PanelRole:
128 return updateBoxNode( slider, node, Q::Panel );
129
130 case GrooveRole:
131 return updateBoxNode( slider, node, Q::Groove );
132
133 case FillRole:
134 return updateBoxNode( slider, node, Q::Fill );
135
136 case HandleRole:
137 return updateBoxNode( slider, node, Q::Handle );
138
139 case TicksRole:
140 return updateSeriesNode( slider, Q::Tick, node );
141 }
142
143 return Inherited::updateSubNode( skinnable, nodeRole, node );
144}
145
146int QskSliderSkinlet::sampleCount( const QskSkinnable* skinnable,
147 QskAspect::Subcontrol subControl ) const
148{
149 if ( subControl == Q::Tick )
150 {
151 const auto slider = static_cast< const QskSlider* >( skinnable );
152 return graduation( slider ).count();
153 }
154
155 return Inherited::sampleCount( skinnable, subControl );
156}
157
158QVariant QskSliderSkinlet::sampleAt( const QskSkinnable* skinnable,
159 QskAspect::Subcontrol subControl, int index ) const
160{
161 if ( subControl == Q::Tick )
162 {
163 const auto slider = static_cast< const QskSlider* >( skinnable );
164 return graduation( slider ).value( index );
165 }
166
167 return Inherited::sampleAt( skinnable, subControl, index );
168}
169
170QskAspect::States QskSliderSkinlet::sampleStates(
171 const QskSkinnable* skinnable, QskAspect::Subcontrol subControl, int index ) const
172{
173 auto states = Inherited::sampleStates( skinnable, subControl, index );
174
175 const auto slider = static_cast< const QskSlider* >( skinnable );
176
177 if ( subControl == Q::Tick && qskHasFilling( slider ) )
178 {
179 const auto tickValue = sampleAt( skinnable, subControl, index );
180 if ( tickValue.canConvert< qreal >() )
181 {
182 const auto intv = QskIntervalF::normalized(
183 slider->origin(), slider->value() );
184
185 if ( intv.contains( tickValue.value< qreal >() ) )
186 states |= Filled;
187 }
188 }
189
190 return states;
191}
192
193QRectF QskSliderSkinlet::sampleRect(
194 const QskSkinnable* skinnable, const QRectF& contentsRect,
195 QskAspect::Subcontrol subControl, int index ) const
196{
197 if ( subControl == Q::Tick )
198 {
199 const auto slider = static_cast< const QskSlider* >( skinnable );
200 return tickRect( slider, contentsRect, index );
201 }
202
203 return Inherited::sampleRect( skinnable, contentsRect, subControl, index );
204}
205
206QSGNode* QskSliderSkinlet::updateSampleNode( const QskSkinnable* skinnable,
207 QskAspect::Subcontrol subControl, int index, QSGNode* node ) const
208{
209 if ( subControl == Q::Tick )
210 {
211 const auto slider = static_cast< const QskSlider* >( skinnable );
212 const auto rect = sampleRect( slider, slider->contentsRect(), subControl, index );
213
214 return updateBoxNode( skinnable, node, rect, subControl );
215 }
216
217 return Inherited::updateSampleNode( skinnable, subControl, index, node );
218}
219
220QRectF QskSliderSkinlet::panelRect(
221 const QskSlider* slider, const QRectF& contentsRect ) const
222{
223 auto r = contentsRect;
224
225 const qreal extent = qskSubcontrolExtent( slider, Q::Panel );
226 if ( extent >= 0 && extent < r.height() )
227 {
228 const auto alignment = slider->alignmentHint( Q::Panel );
229
230 if ( slider->orientation() == Qt::Horizontal )
231 {
232 r = qskAlignedRectF( r, r.width(),
233 extent, alignment & Qt::AlignVertical_Mask );
234 }
235 else
236 {
237 r = qskAlignedRectF( r, extent, r.height(),
238 alignment & Qt::AlignHorizontal_Mask );
239 }
240 }
241
242 return r;
243}
244
245QRectF QskSliderSkinlet::fillRect(
246 const QskSlider* slider, const QRectF& contentsRect ) const
247{
248 if ( !qskHasFilling( slider ) )
249 return QRectF();
250
251 auto pos1 = slider->valueAsRatio( slider->origin() );
252 auto pos2 = qBound( 0.0, slider->positionHint( Q::Handle ), 1.0 );
253
254 if ( slider->isInverted() )
255 {
256 pos1 = 1.0 - pos1;
257 pos2 = 1.0 - pos2;
258 }
259
260 if ( pos1 > pos2 )
261 qSwap( pos1, pos2 );
262
263 auto r = qskInnerRect( slider, contentsRect, Q::Fill );
264
265 auto scaleRect = subControlRect( slider, contentsRect, Q::Scale );
266
267 if ( slider->orientation() == Qt::Horizontal )
268 {
269 if ( !qFuzzyIsNull( pos1 ) )
270 r.setLeft( scaleRect.left() + pos1 * scaleRect.width() );
271
272 r.setRight( scaleRect.left() + pos2 * scaleRect.width() );
273 }
274 else
275 {
276 if ( !qFuzzyIsNull( pos1 ) )
277 r.setBottom( scaleRect.bottom() - pos1 * scaleRect.height() );
278
279 r.setTop( scaleRect.bottom() - pos2 * scaleRect.height() );
280 }
281
282 return r;
283}
284
285QRectF QskSliderSkinlet::handleRect(
286 const QskSlider* slider, const QRectF& contentsRect ) const
287{
288 auto handleSize = slider->strutSizeHint( Q::Handle );
289 const auto pos = qBound( 0.0, slider->positionHint( Q::Handle ), 1.0 );
290
291 const auto r = subControlRect( slider, contentsRect, Q::Scale );
292 auto center = r.center();
293
294 if ( slider->orientation() == Qt::Horizontal )
295 {
296 if ( handleSize.height() < 0.0 )
297 handleSize.setHeight( r.height() );
298
299 if ( handleSize.width() < 0.0 )
300 handleSize.setWidth( handleSize.height() );
301
302 if ( slider->isInverted() )
303 center.setX( r.right() - pos * r.width() );
304 else
305 center.setX( r.left() + pos * r.width() );
306 }
307 else
308 {
309 if ( handleSize.width() < 0.0 )
310 handleSize.setWidth( r.width() );
311
312 if ( handleSize.height() < 0.0 )
313 handleSize.setHeight( handleSize.width() );
314
315 if ( slider->isInverted() )
316 center.setY( r.top() + pos * r.height() );
317 else
318 center.setY( r.bottom() - pos * r.height() );
319 }
320
321 QRectF handleRect( 0, 0, handleSize.width(), handleSize.height() );
322 handleRect.moveCenter( center );
323
324 return handleRect;
325}
326
327QRectF QskSliderSkinlet::tickRect( const QskSlider* slider,
328 const QRectF& contentsRect, int index ) const
329{
330 const auto tickValue = sampleAt( slider, Q::Tick, index );
331 if ( !tickValue.canConvert< qreal >() )
332 return QRectF();
333
334 auto tickPos = slider->valueAsRatio( tickValue.value< qreal >() );
335 if ( slider->isInverted() )
336 tickPos = 1.0 - tickPos;
337
338 const auto r = subControlRect( slider, contentsRect, Q::Scale );
339
340 const auto padding = slider->paddingHint( Q::Scale );
341 const auto size = slider->strutSizeHint( Q::Tick );
342
343 if( slider->orientation() == Qt::Horizontal )
344 {
345 const auto x = tickPos * r.width() - 0.5 * size.width();
346
347 const auto span = qskTickSpan(
348 padding.top(), r.height() - padding.bottom(), size.height() );
349
350 return QRectF( r.x() + x, r.y() + span.first,
351 size.width(), span.second - span.first );
352 }
353 else
354 {
355 const auto y = tickPos * r.height() + 0.5 * size.height();
356
357 const auto span = qskTickSpan(
358 padding.left(), r.width() - padding.right(), size.width() );
359
360 return QRectF( r.x() + span.first, r.bottom() - y,
361 span.second - span.first, size.height() );
362 }
363}
364
365QSizeF QskSliderSkinlet::sizeHint( const QskSkinnable* skinnable,
366 Qt::SizeHint which, const QSizeF& ) const
367{
368 if ( which != Qt::PreferredSize )
369 return QSizeF();
370
371 auto extent = qskSubcontrolExtent( skinnable, Q::Panel );
372 extent = qMax( extent, qskSubcontrolExtent( skinnable, Q::Groove ) );
373 extent = qMax( extent, qskSubcontrolExtent( skinnable, Q::Fill ) );
374
375 const auto slider = static_cast< const QskSlider* >( skinnable );
376
377 auto hint = skinnable->strutSizeHint( Q::Handle );
378
379 if ( slider->orientation() == Qt::Horizontal )
380 hint.setHeight( qMax( hint.height(), extent ) );
381 else
382 hint.setWidth( qMax( hint.width(), extent ) );
383
384 return hint;
385}
386
387bool QskSliderSkinlet::hasGraduation( const QskSlider* slider ) const
388{
389 if ( slider->stepSize() )
390 {
391 const auto policy = slider->flagHint< Qsk::Policy >(
392 Q::Tick | QskAspect::Option, Qsk::Never );
393
394 switch( policy )
395 {
396 case Qsk::Always:
397 return true;
398
399 case Qsk::Maybe:
400 return slider->isSnapping();
401
402 case Qsk::Never:
403 return false;
404 }
405 }
406
407 return false;
408}
409
410QVector< qreal > QskSliderSkinlet::graduation( const QskSlider* slider ) const
411{
412 QVector< qreal > graduation;
413
414 if ( hasGraduation( slider ) )
415 {
416 const auto from = slider->minimum();
417 const auto to = slider->maximum();
418
419 auto step = slider->stepSize();
420 if ( from > to )
421 step = -step;
422
423 const auto n = qCeil( ( to - from ) / step ) - 1;
424
425 graduation.reserve( n );
426
427 for ( int i = 1; i <= n; i++ )
428 graduation += from + i * step;
429 }
430
431 return graduation;
432}
433
434#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
QMarginsF paddingHint(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a padding hint.
QSizeF strutSizeHint(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a strut size hint.
T flagHint(QskAspect, T=T()) const
Retrieves a flag hint.
Qt::Alignment alignmentHint(QskAspect, Qt::Alignment=Qt::Alignment()) const
Retrieves an alignment hint.