QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskSegmentedBar.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskSegmentedBar.h"
7
8#include "QskLabelData.h"
9#include "QskTextOptions.h"
10#include "QskEvent.h"
11#include "QskSkinlet.h"
12#include "QskAspect.h"
13#include "QskAnimationHint.h"
14
15#include <qguiapplication.h>
16#include <qstylehints.h>
17#include <qfontmetrics.h>
18
19QSK_SUBCONTROL( QskSegmentedBar, Panel )
20QSK_SUBCONTROL( QskSegmentedBar, Splash )
21QSK_SUBCONTROL( QskSegmentedBar, Segment )
22QSK_SUBCONTROL( QskSegmentedBar, Separator )
23QSK_SUBCONTROL( QskSegmentedBar, Cursor )
24QSK_SUBCONTROL( QskSegmentedBar, Text )
25QSK_SUBCONTROL( QskSegmentedBar, Icon )
26
27QSK_SYSTEM_STATE( QskSegmentedBar, Selected, QskAspect::FirstSystemState << 1 )
28QSK_SYSTEM_STATE( QskSegmentedBar, Pressed, QskAspect::FirstSystemState << 2 )
29
30class QskSegmentedBar::PrivateData
31{
32 public:
33 PrivateData( Qt::Orientation orientation )
34 : orientation( orientation )
35 {
36 }
37
38 QVector< QskLabelData > options;
39 QVector< bool > enabled;
40
41 int selectedIndex = -1;
42 int currentIndex = -1;
43
44 Qt::Orientation orientation;
45 bool isPressed = false;
46};
47
48QskSegmentedBar::QskSegmentedBar( QQuickItem* parent )
49 : QskSegmentedBar( Qt::Horizontal, parent )
50{
51}
52
53QskSegmentedBar::QskSegmentedBar( Qt::Orientation orientation, QQuickItem* parent )
54 : Inherited( parent )
55 , m_data( new PrivateData( orientation ) )
56{
57 setAcceptHoverEvents( true );
58
59 if( orientation == Qt::Horizontal )
60 initSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::Fixed );
61 else
62 initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::MinimumExpanding );
63
64 setAcceptedMouseButtons( Qt::LeftButton );
65 setWheelEnabled( true );
66 setFocusPolicy( Qt::StrongFocus );
67
68 connect( this, &QskSegmentedBar::currentIndexChanged,
70}
71
72QskSegmentedBar::~QskSegmentedBar()
73{
74}
75
76void QskSegmentedBar::setOrientation( Qt::Orientation orientation )
77{
78 if( orientation != m_data->orientation )
79 {
80 setSizePolicy( sizePolicy( Qt::Vertical ), sizePolicy( Qt::Horizontal ) );
81 m_data->orientation = orientation;
82
84 update();
85 }
86}
87
88Qt::Orientation QskSegmentedBar::orientation() const
89{
90 return m_data->orientation;
91}
92
93void QskSegmentedBar::setTextOptions( const QskTextOptions& textOptions )
94{
95 setTextOptionsHint( Text, textOptions );
96}
97
98QskTextOptions QskSegmentedBar::textOptions() const
99{
100 return textOptionsHint( Text );
101}
102
103int QskSegmentedBar::addOption( const QUrl& graphicSource, const QString& text )
104{
105 return addOption( QskLabelData( text, graphicSource ) );
106}
107
108int QskSegmentedBar::addOption( const QskLabelData& option )
109{
110 m_data->options += option;
111 m_data->enabled += true;
112
114 update();
115
116 if ( isComponentComplete() )
117 Q_EMIT optionsChanged();
118
119 if ( count() == 1 )
120 setSelectedIndex( 0 );
121
122 return count() - 1;
123}
124
125QskLabelData QskSegmentedBar::optionAt( int index ) const
126{
127 return m_data->options.value( index );
128}
129
130void QskSegmentedBar::setOptions( const QStringList& options )
131{
132 setOptions( qskCreateLabelData( options ) );
133}
134
135void QskSegmentedBar::setOptions( const QVector< QskLabelData >& options )
136{
137 m_data->options = options;
138 m_data->enabled.fill( true, options.count() );
139
141 update();
142
143 // selectedIndex ???
144 Q_EMIT optionsChanged();
145}
146
148{
149 return static_cast< QskAspect::Variation >( m_data->orientation );
150}
151
152void QskSegmentedBar::mousePressEvent( QMouseEvent* event )
153{
154 using A = QskAspect;
155
156 const int index = indexAtPosition( qskMousePosition( event ) );
157
158 if( isSegmentEnabled( index ) )
159 {
160 m_data->isPressed = true;
161
162 if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
163 {
164 if( !QGuiApplication::styleHints()->setFocusOnTouchRelease() )
165 {
166 if( index != m_data->currentIndex )
167 setCurrentIndex( index );
168 }
169 }
170 }
171
172 const auto hint = animationHint( Splash | A::Color );
173
174 if( hint.isValid() )
175 {
176 setSkinHint( Splash | A::Metric | A::Position, qskMousePosition( event ) );
177 startTransition( Splash | A::Metric | A::Size, hint, 0.0, 1.0 );
178 }
179}
180
181void QskSegmentedBar::mouseUngrabEvent()
182{
183 m_data->isPressed = false;
184}
185
186void QskSegmentedBar::mouseReleaseEvent( QMouseEvent* event )
187{
188 int index = -1;
189
190 if( m_data->isPressed )
191 {
192 m_data->isPressed = false;
193 index = indexAtPosition( qskMousePosition( event ) );
194 }
195
196 if( isSegmentEnabled( index ) )
197 {
198 if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
199 {
200 if( QGuiApplication::styleHints()->setFocusOnTouchRelease() )
201 {
202 if( index != m_data->currentIndex )
203 setCurrentIndex( index );
204 }
205 }
206 }
207
208 setSelectedIndex( index );
209}
210
211void QskSegmentedBar::keyPressEvent( QKeyEvent* event )
212{
213 switch( event->key() )
214 {
215 case Qt::Key_Up:
216 case Qt::Key_Down:
217 case Qt::Key_Left:
218 case Qt::Key_Right:
219 {
220 bool forwards;
221
222 if ( m_data->orientation == Qt::Vertical )
223 forwards = ( event->key() == Qt::Key_Down );
224 else
225 forwards = ( event->key() == Qt::Key_Right );
226
227 const int index = nextIndex( m_data->selectedIndex, forwards );
228 if ( index != m_data->selectedIndex )
229 {
230 if ( index >= 0 && index < count() )
231 setSelectedIndex( index );
232 }
233
234 return;
235 }
236
237 case Qt::Key_Select:
238 case Qt::Key_Space:
239
240 // stop further processing
241 return;
242
243 default:
244 {
245 const int steps = qskFocusChainIncrement( event );
246
247 if( steps != 0 )
248 {
249 const int index = nextIndex( m_data->currentIndex, steps > 0 );
250
251 if( index != m_data->currentIndex )
252 setCurrentIndex( index );
253
254 if( index >= 0 )
255 return;
256 }
257 }
258 }
259
260 Inherited::keyPressEvent( event );
261}
262
263void QskSegmentedBar::keyReleaseEvent( QKeyEvent* event )
264{
265 if( event->key() == Qt::Key_Select || event->key() == Qt::Key_Space )
266 {
267 if( m_data->currentIndex >= 0 )
268 setSelectedIndex( m_data->currentIndex );
269
270 return;
271 }
272
273 Inherited::keyReleaseEvent( event );
274}
275
276void QskSegmentedBar::hoverEnterEvent( QHoverEvent* event )
277{
278 using A = QskAspect;
279
280 setSkinHint( Segment | Hovered | A::Metric | A::Position, qskHoverPosition( event ) );
281 update();
282}
283
284void QskSegmentedBar::hoverMoveEvent( QHoverEvent* event )
285{
286 using A = QskAspect;
287
288 setSkinHint( Segment | Hovered | A::Metric | A::Position, qskHoverPosition( event ) );
289 update();
290}
291
292void QskSegmentedBar::hoverLeaveEvent( QHoverEvent* )
293{
294 using A = QskAspect;
295
296 setSkinHint( Segment | Hovered | A::Metric | A::Position, QPointF() );
297 update();
298}
299
300void QskSegmentedBar::focusInEvent( QFocusEvent* event )
301{
302 int index = m_data->currentIndex;
303
304 switch( event->reason() )
305 {
306 case Qt::TabFocusReason:
307 {
308 index = nextIndex( -1, true );
309 break;
310 }
311
312 case Qt::BacktabFocusReason:
313 {
314 index = nextIndex( -1, false );
315 break;
316 }
317
318 default:
319 {
320 if( index < 0 || index >= count() )
321 index = nextIndex( -1, true );
322 }
323 }
324
325 if( index != m_data->currentIndex )
326 setCurrentIndex( index );
327
328 Inherited::focusInEvent( event );
329}
330
331void QskSegmentedBar::focusOutEvent( QFocusEvent* event )
332{
333 setCurrentIndex( -1 );
334 Inherited::focusOutEvent( event );
335}
336
337void QskSegmentedBar::clear()
338{
339 if( count() == 0 )
340 return;
341
342 m_data->options.clear();
343 Q_EMIT optionsChanged();
344
345 if( m_data->selectedIndex >= 0 )
346 {
347 m_data->selectedIndex = -1;
348 Q_EMIT selectedIndexChanged( m_data->selectedIndex );
349 }
350
351 if( m_data->currentIndex >= 0 )
352 {
353 m_data->currentIndex = -1;
354 Q_EMIT currentIndexChanged( m_data->currentIndex );
355 }
356
357 update();
358}
359
360void QskSegmentedBar::setCurrentIndex( int index )
361{
362 if ( !isSegmentEnabled( index ) )
363 index = -1;
364
365 if( index != m_data->currentIndex )
366 {
367 m_data->currentIndex = index;
368 setPositionHint( Segment | Focused, index );
369
370 Q_EMIT currentIndexChanged( index );
371 }
372}
373
374int QskSegmentedBar::currentIndex() const
375{
376 return m_data->currentIndex;
377}
378
379QString QskSegmentedBar::currentText() const
380{
381 if ( m_data->currentIndex >= 0 )
382 return optionAt( m_data->currentIndex ).text();
383
384 return QString();
385}
386
387void QskSegmentedBar::setSelectedIndex( int index )
388{
389 if ( !isSegmentEnabled( index ) )
390 index = -1;
391
392 if( index != m_data->selectedIndex )
393 {
394 const auto oldIndex = m_data->selectedIndex;
395 m_data->selectedIndex = index;
396
397 movePositionHint( Cursor, index );
398 update();
399
400 Q_EMIT selectedIndexChanged( index );
401
402 const auto states = skinStates();
403
404 if ( oldIndex >= 0 )
405 startHintTransitions( states | Selected, states, oldIndex );
406
407 if ( index >= 0 )
408 startHintTransitions( states, states | Selected, index );
409 }
410}
411
412int QskSegmentedBar::selectedIndex() const
413{
414 return m_data->selectedIndex;
415}
416
417int QskSegmentedBar::nextIndex( int index, bool forwards ) const
418{
419 const auto& options = m_data->options;
420 const int count = options.count();
421
422 if( forwards )
423 {
424 if( index < 0 || index >= count )
425 index = -1;
426
427 while( ++index < count )
428 {
429 if( isSegmentEnabled( index ) )
430 return index;
431 }
432 }
433 else
434 {
435 if( index < 0 || index >= count )
436 index = count;
437
438 while( --index >= 0 )
439 {
440 if( isSegmentEnabled( index ) )
441 return index;
442 }
443 }
444
445 return -1;
446}
447
448QVector< QskLabelData > QskSegmentedBar::options() const
449{
450 return m_data->options;
451}
452
453int QskSegmentedBar::count() const
454{
455 return m_data->options.count();
456}
457
458void QskSegmentedBar::setSegmentEnabled( int index, bool enabled )
459{
460 auto& bitVector = m_data->enabled;
461
462 if( ( index < 0 ) || ( index >= bitVector.count() )
463 || ( bitVector[ index ] == enabled ) )
464 {
465 return;
466 }
467
468 bitVector[ index ] = enabled;
469
470 if( !enabled )
471 {
472 if( m_data->currentIndex == index )
473 setCurrentIndex( -1 );
474 }
475
476 update();
477}
478
479bool QskSegmentedBar::isSegmentEnabled( int index ) const
480{
481 return m_data->enabled.value( index, false );
482}
483
484int QskSegmentedBar::indexAtPosition( const QPointF& pos ) const
485{
486 return effectiveSkinlet()->sampleIndexAt( this,
487 contentsRect(), QskSegmentedBar::Segment, pos );
488}
489
491{
492 if( m_data->currentIndex >= 0 )
493 {
494 return effectiveSkinlet()->sampleRect( this,
495 contentsRect(), QskSegmentedBar::Segment, m_data->currentIndex );
496 }
497
499}
500
501#include "moc_QskSegmentedBar.cpp"
Lookup key for a QskSkinHintTable.
Definition QskAspect.h:15
@ FirstSystemState
Definition QskAspect.h:115
Variation
Some sort of variation.
Definition QskAspect.h:82
void focusIndicatorRectChanged()
static const QskAspect::State Hovered
Definition QskControl.h:56
void setSizePolicy(QskSizePolicy)
virtual QRectF focusIndicatorRect() const
QskSizePolicy sizePolicy
Definition QskControl.h:43
QRectF contentsRect() const
static const QskAspect::State Focused
Definition QskControl.h:56
void resetImplicitSize()
Definition QskItem.cpp:721
QskAspect::Variation effectiveVariation() const override
QRectF focusIndicatorRect() const override final
bool setSkinHint(QskAspect, const QVariant &)
Insert a hint into the local hint table.
QskAnimationHint animationHint(QskAspect, QskSkinHintStatus *=nullptr) const
const QskSkinlet * effectiveSkinlet() const