QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskScrollViewSkinlet.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskScrollViewSkinlet.h"
7#include "QskScrollView.h"
8
9#include "QskAspect.h"
10#include "QskQuick.h"
11#include "QskSGNode.h"
12#include "QskSkinStateChanger.h"
13
14#include <qsgnode.h>
15
16static void qskAlignedHandle( qreal start, qreal end,
17 qreal scrollBarLength, qreal minHandleLength,
18 qreal& handleStart, qreal& handleEnd )
19{
20 // no qBound: scrollBarLength might be < 4.0
21 minHandleLength = qMax( 4.0, minHandleLength );
22 minHandleLength = qMin( minHandleLength, scrollBarLength );
23
24 handleStart = start * scrollBarLength;
25 handleEnd = end * scrollBarLength;
26
27 const qreal handleLength = handleEnd - handleStart;
28 if ( handleLength < minHandleLength )
29 {
30 const qreal extra = minHandleLength - handleLength;
31
32 handleStart -= extra * handleStart / scrollBarLength;
33 handleEnd += extra * ( ( scrollBarLength - handleEnd ) / scrollBarLength );
34 }
35 else if ( handleLength > scrollBarLength )
36 {
37 const qreal extra = scrollBarLength - handleLength;
38
39 handleStart -= extra * handleStart / scrollBarLength;
40 handleEnd += extra * ( ( scrollBarLength - handleEnd ) / scrollBarLength );
41 }
42}
43
44static qreal qskScrollBarExtent(
45 const QskScrollView* scrollView, Qt::Orientation orientation )
46{
47 const auto subControl = ( orientation == Qt::Horizontal )
48 ? QskScrollView::HorizontalScrollBar : QskScrollView::VerticalScrollBar;
49
50 QskSkinStateChanger stateChanger( scrollView );
51 stateChanger.setStates( scrollView->scrollBarStates( subControl ) );
52
53 return scrollView->metric( subControl | QskAspect::Size );
54}
55
56QskScrollViewSkinlet::QskScrollViewSkinlet( QskSkin* skin )
57 : Inherited( skin )
58{
59 setNodeRoles( { PanelRole, ViewportRole, ContentsRootRole,
60 HorizontalScrollBarRole, HorizontalScrollHandleRole,
61 VerticalScrollBarRole, VerticalScrollHandleRole } );
62}
63
64QskScrollViewSkinlet::~QskScrollViewSkinlet() = default;
65
66QRectF QskScrollViewSkinlet::subControlRect( const QskSkinnable* skinnable,
67 const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
68{
69 using Q = QskScrollView;
70
71 const auto scrollView = static_cast< const QskScrollView* >( skinnable );
72
73 if ( subControl == Q::Panel )
74 return contentsRect;
75
76 if ( subControl == Q::Viewport )
77 return viewportRect( scrollView, contentsRect );
78
79 if ( subControl == Q::HorizontalScrollBar )
80 return scrollBarRect( scrollView, contentsRect, Qt::Horizontal );
81
82 if ( subControl == Q::HorizontalScrollHandle )
83 return scrollHandleRect( scrollView, contentsRect, Qt::Horizontal );
84
85 if ( subControl == Q::VerticalScrollBar )
86 return scrollBarRect( scrollView, contentsRect, Qt::Vertical );
87
88 if ( subControl == Q::VerticalScrollHandle )
89 return scrollHandleRect( scrollView, contentsRect, Qt::Vertical );
90
91 return Inherited::subControlRect( skinnable, contentsRect, subControl );
92}
93
94QSGNode* QskScrollViewSkinlet::updateSubNode(
95 const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
96{
97 using Q = QskScrollView;
98
99 const auto scrollView = static_cast< const QskScrollView* >( skinnable );
100
101 switch ( nodeRole )
102 {
103 case PanelRole:
104 return updateBoxNode( skinnable, node, Q::Panel );
105
106 case ViewportRole:
107 return updateBoxNode( skinnable, node, Q::Viewport );
108
109 case HorizontalScrollHandleRole:
110 return updateScrollBarNode( scrollView, Q::HorizontalScrollHandle, node );
111
112 case VerticalScrollHandleRole:
113 return updateScrollBarNode( scrollView, Q::VerticalScrollHandle, node );
114
115 case HorizontalScrollBarRole:
116 return updateScrollBarNode( scrollView, Q::HorizontalScrollBar, node );
117
118 case VerticalScrollBarRole:
119 return updateScrollBarNode( scrollView, Q::VerticalScrollBar, node );
120
121 case ContentsRootRole:
122 return updateContentsRootNode( scrollView, node );
123
124 }
125
126 return Inherited::updateSubNode( skinnable, nodeRole, node );
127}
128
129QSGNode* QskScrollViewSkinlet::updateScrollBarNode( const QskScrollView* scrollView,
130 QskAspect::Subcontrol subControl, QSGNode* node ) const
131{
132 const auto rect = subControlRect( scrollView,
133 scrollView->contentsRect(), subControl );
134
135 QskSkinStateChanger stateChanger( scrollView );
136 stateChanger.setStates( scrollView->scrollBarStates( subControl ) );
137
138 return updateBoxNode( scrollView, node, rect, subControl );
139}
140
141QSGNode* QskScrollViewSkinlet::updateContentsRootNode(
142 const QskScrollView* scrollView, QSGNode* node ) const
143{
144 auto* clipNode = updateBoxClipNode( scrollView, node, QskScrollView::Viewport );
145 if ( clipNode == nullptr )
146 return nullptr;
147
148 auto oldContentsNode = QskSGNode::findChildNode( clipNode, ContentsRootRole );
149 auto contentsNode = updateContentsNode( scrollView, oldContentsNode );
150
151 if ( contentsNode )
152 {
153 /*
154 Not all classes derived from QskScrollView create their content node
155 by updateContentsNode(). F.e QskScrollArea takes the itemNode from
156 another item and puts it below the clip node.
157 For those situations we need to set a node role, that we can decide
158 which child of the clip node needs to be replaced.
159 */
160 QskSGNode::setNodeRole( contentsNode, ContentsRootRole );
161
162 if ( contentsNode->parent() != clipNode )
163 clipNode->appendChildNode( contentsNode );
164 }
165
166 if ( oldContentsNode && oldContentsNode != contentsNode )
167 {
168 clipNode->removeChildNode( oldContentsNode );
169
170 if ( oldContentsNode->flags() & QSGNode::OwnedByParent )
171 delete oldContentsNode;
172 }
173
174 return clipNode;
175}
176
177QSGNode* QskScrollViewSkinlet::updateContentsNode(
178 const QskScrollView*, QSGNode* ) const
179{
180 return nullptr;
181}
182
183QRectF QskScrollViewSkinlet::viewportRect(
184 const QskScrollView* scrollView, const QRectF& contentsRect ) const
185{
186 using Q = QskScrollView;
187
188 const auto orientation = scrollView->scrollableOrientations();
189
190 auto vr = subControlRect( scrollView, contentsRect, Q::Panel );
191 const qreal spacing = scrollView->spacingHint( Q::Panel );
192
193 if ( orientation & Qt::Vertical )
194 {
195 const auto r = subControlRect(
196 scrollView, contentsRect, Q::VerticalScrollBar );
197
198 if ( r.width() > 0.0 )
199 vr.setWidth( vr.width() - r.width() - spacing );
200 }
201
202 if ( orientation & Qt::Horizontal )
203 {
204 const auto r = subControlRect(
205 scrollView, contentsRect, Q::HorizontalScrollBar );
206
207 if ( r.height() >= 0.0 )
208 vr.setHeight( vr.height() - r.height() - spacing );
209 }
210
211 if ( vr.width() < 0.0 )
212 vr.setWidth( 0.0 );
213
214 if ( vr.height() < 0.0 )
215 vr.setHeight( 0.0 );
216
217 return vr;
218}
219
220QRectF QskScrollViewSkinlet::scrollHandleRect( const QskScrollView* scrollView,
221 const QRectF& contentsRect, Qt::Orientation orientation ) const
222{
223 using Q = QskScrollView;
224
225 const auto scrollOrientations = scrollView->scrollableOrientations();
226 if ( !( orientation & scrollOrientations ) )
227 return QRectF();
228
229 const auto pos = scrollView->scrollPos();
230
231 const auto vRect = subControlRect(
232 scrollView, contentsRect, QskScrollView::Viewport );
233
234 const auto scrollableSize = scrollView->scrollableSize();
235
236 QRectF handleRect;
237
238 if ( orientation == Qt::Vertical )
239 {
240 const auto sbRect = subControlRect( scrollView, contentsRect, Q::VerticalScrollBar );
241
242 QskSkinStateChanger stateChanger( scrollView );
243 stateChanger.setStates( scrollView->scrollBarStates( Q::VerticalScrollBar ) );
244
245 const auto padding = scrollView->paddingHint( Q::VerticalScrollBar );
246 const auto strut = scrollView->strutSizeHint( Q::VerticalScrollHandle );
247
248 const qreal y1 = pos.y() / scrollableSize.height();
249 const qreal y2 = ( pos.y() + vRect.height() ) / scrollableSize.height();
250
251 qreal top, bottom;
252 qskAlignedHandle( y1, y2, sbRect.height(), strut.height(), top, bottom );
253
254 handleRect = sbRect;
255 handleRect.setTop( sbRect.y() + top );
256 handleRect.setBottom( sbRect.y() + bottom );
257 handleRect.adjust( padding.left(), 0, -padding.right(), 0 );
258 }
259 else
260 {
261 const auto sbRect = subControlRect( scrollView, contentsRect, Q::HorizontalScrollBar );
262
263 QskSkinStateChanger stateChanger( scrollView );
264 stateChanger.setStates( scrollView->scrollBarStates( Q::HorizontalScrollBar ) );
265
266 const auto padding = scrollView->paddingHint( Q::HorizontalScrollBar );
267
268 const qreal x1 = pos.x() / scrollableSize.width();
269 const qreal x2 = ( pos.x() + vRect.width() ) / scrollableSize.width();
270
271 const auto strut = scrollView->strutSizeHint( Q::HorizontalScrollHandle );
272
273 qreal left, right;
274 qskAlignedHandle( x1, x2, sbRect.width(), strut.width(), left, right );
275
276 handleRect = sbRect;
277 handleRect.setLeft( sbRect.x() + left );
278 handleRect.setRight( sbRect.x() + right );
279 handleRect.adjust( 0, padding.top(), 0, -padding.bottom() );
280 }
281
282 return handleRect;
283}
284
285QRectF QskScrollViewSkinlet::scrollBarRect( const QskScrollView* scrollView,
286 const QRectF& contentsRect, Qt::Orientation orientation ) const
287{
288 using Q = QskScrollView;
289
290 const auto scrollOrientations = scrollView->scrollableOrientations();
291 if ( !( orientation & scrollOrientations ) )
292 return QRectF();
293
294 auto r = subControlRect( scrollView, contentsRect, Q::Panel );
295
296 if ( orientation == Qt::Horizontal )
297 {
298 const qreal h = qskScrollBarExtent( scrollView, Qt::Horizontal );
299 r.setTop( r.bottom() - h );
300
301 if ( scrollOrientations & Qt::Vertical )
302 {
303 const qreal w = qskScrollBarExtent( scrollView, Qt::Vertical );
304 r.setRight( r.right() - w );
305 }
306 }
307 else
308 {
309 const qreal w = qskScrollBarExtent( scrollView, Qt::Vertical );
310 r.setLeft( r.right() - w );
311
312 if ( scrollOrientations & Qt::Horizontal )
313 {
314 const qreal h = qskScrollBarExtent( scrollView, Qt::Horizontal );
315 r.setBottom( r.bottom() - h );
316 }
317 }
318
319 return r;
320}
321
322#include "moc_QskScrollViewSkinlet.cpp"
Subcontrol
For use within the rendering or lay-outing of a specific QskSkinnable.
Definition QskAspect.h:104
QRectF contentsRect() const
qreal spacingHint(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a spacing hint.
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.