8#include "QskGraphicProvider.h"
9#include "QskLabelData.h"
10#include "QskTextOptions.h"
11#include "QskGraphic.h"
12#include "QskColorFilter.h"
13#include "QskSkinlet.h"
15#include "QskPlatform.h"
19#include <qeventloop.h>
22#include <private/qquickitem_p.h>
26QSK_SUBCONTROL(
QskMenu, Segment )
27QSK_SUBCONTROL(
QskMenu, Cursor )
30QSK_SUBCONTROL(
QskMenu, Separator )
35static inline
int qskActionIndex( const QVector<
int >& actions,
int index )
40 auto it = std::lower_bound( actions.constBegin(), actions.constEnd(), index );
41 return it - actions.constBegin();
44class QskMenu::PrivateData
49 QVector< QskLabelData > options;
51 QVector< int > separators;
52 QVector< int > actions;
54 int triggeredIndex = -1;
55 int currentIndex = -1;
58 bool isPressed =
false;
61QskMenu::QskMenu( QQuickItem* parent )
63 , m_data( new PrivateData )
74 setPopupFlag( QskPopup::CloseOnPressOutside,
true );
75 setPopupFlag( QskPopup::DeleteOnClose,
true );
78 setSubcontrolProxy( Inherited::Overlay, Overlay );
80 initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed );
83 connect(
this, &QskPopup::fadingChanged,
86 connect(
this, &QskPopup::fadingChanged,
87 this, &QQuickItem::setClip );
89 connect(
this, &QskPopup::opened,
this,
90 [
this]() { m_data->triggeredIndex = -1; } );
92 setAcceptHoverEvents(
true );
99QRectF QskMenu::clipRect()
const
103 constexpr qreal d = 1e6;
104 return QRectF( -d, m_data->origin.y() - y(), 2.0 * d, d );
107 return Inherited::clipRect();
110bool QskMenu::isWrapping()
const
112 return m_data->wrapping;
115void QskMenu::setWrapping(
bool on )
117 if ( m_data->wrapping != on )
119 m_data->wrapping = on;
120 Q_EMIT wrappingChanged( on );
127bool QskMenu::isCascading()
const
132void QskMenu::setCascading(
bool on )
135 Q_EMIT cascadingChanged( on );
138void QskMenu::resetCascading()
141 Q_EMIT cascadingChanged( isCascading() );
146void QskMenu::setOrigin(
const QPointF& origin )
148 if ( origin != m_data->origin )
150 m_data->origin = origin;
151 Q_EMIT originChanged( origin );
155QPointF QskMenu::origin()
const
157 return m_data->origin;
162 setTextOptionsHint( Text, textOptions );
167 return textOptionsHint( Text );
170int QskMenu::addOption(
const QString& graphicSource,
const QString& text )
172 return addOption(
QskLabelData( text, graphicSource ) );
175int QskMenu::addOption(
const QUrl& graphicSource,
const QString& text )
177 return addOption(
QskLabelData( text, graphicSource ) );
182 const int index = m_data->options.count();
184 m_data->options += option;
186 if ( option.isEmpty() )
187 m_data->separators += index;
189 m_data->actions += index;
194 if ( isComponentComplete() )
195 Q_EMIT optionsChanged();
200void QskMenu::setOptions(
const QStringList& options )
202 setOptions( qskCreateLabelData( options ) );
205void QskMenu::setOptions(
const QVector< QskLabelData >& options )
207 m_data->options = options;
209 for (
int i = 0; i < options.count(); i++ )
211 if ( options[i].isEmpty() )
212 m_data->separators += i;
214 m_data->actions += i;
217 if ( m_data->currentIndex >= 0 )
219 m_data->currentIndex = -1;
221 if ( isComponentComplete() )
222 Q_EMIT currentIndexChanged( m_data->currentIndex );
228 if ( isComponentComplete() )
229 Q_EMIT optionsChanged();
234 setOptions( QVector< QskLabelData >() );
237QVector< QskLabelData > QskMenu::options()
const
239 return m_data->options;
244 return m_data->options.value( index );
247void QskMenu::addSeparator()
252QVector< int > QskMenu::separators()
const
254 return m_data->separators;
257QVector< int > QskMenu::actions()
const
259 return m_data->actions;
262int QskMenu::currentIndex()
const
264 return m_data->currentIndex;
267void QskMenu::setCurrentIndex(
int index )
269 if( index < 0 || index >= m_data->options.count() )
275 if ( m_data->options[index].isEmpty() )
279 if( index != m_data->currentIndex )
281 setPositionHint( Cursor, index );
283 m_data->currentIndex = index;
286 Q_EMIT currentIndexChanged( index );
291QString QskMenu::currentText()
const
293 return optionAt( m_data->currentIndex ).text();
296int QskMenu::triggeredIndex()
const
298 return m_data->triggeredIndex;
301QString QskMenu::triggeredText()
const
303 return optionAt( m_data->triggeredIndex ).text();
306void QskMenu::updateResources()
310 dy = ( 1.0 - fadingFactor() ) * height();
312 setPosition( m_data->origin.x(), m_data->origin.y() - dy );
314 Inherited::updateResources();
319 if ( isFading() && clip() )
321 if (
auto clipNode = QQuickItemPrivate::get(
this )->clipNode() )
328 const auto r = clipRect();
329 if ( r != clipNode->rect() )
331 clipNode->setRect( r );
340void QskMenu::keyPressEvent( QKeyEvent* event )
342 if( m_data->currentIndex < 0 )
345 switch( event->key() )
364 m_data->isPressed =
true;
370 if (
const int steps = qskFocusChainIncrement( event ) )
378 return Inherited::keyPressEvent( event );
381void QskMenu::keyReleaseEvent( QKeyEvent* )
385 m_data->isPressed =
false;
387 if ( m_data->currentIndex >= 0 )
390 trigger( m_data->currentIndex );
395void QskMenu::hoverEnterEvent( QHoverEvent* event )
399 setSkinHint( Segment |
Hovered | A::Metric | A::Position, qskHoverPosition( event ) );
403void QskMenu::hoverMoveEvent( QHoverEvent* event )
407 setSkinHint( Segment |
Hovered | A::Metric | A::Position, qskHoverPosition( event ) );
411void QskMenu::hoverLeaveEvent( QHoverEvent* )
419#ifndef QT_NO_WHEELEVENT
421void QskMenu::wheelEvent( QWheelEvent* event )
423 const auto steps = qskWheelSteps( event );
429void QskMenu::traverse(
int steps )
431 const auto& actions = m_data->actions;
432 const auto count = actions.count();
435 if ( actions.isEmpty() || ( steps % count == 0 ) )
438 int action1 = qskActionIndex( actions, m_data->currentIndex );
439 int action2 = action1 + steps;
441 if ( !m_data->wrapping )
442 action2 = qBound( 0, action2, count - 1 );
448 index1 = m_data->options.count();
449 else if ( action2 >= count )
452 index1 = actions[ action1 ];
458 const auto index2 = actions[ action2 ];
460 movePositionHint( Cursor, index1, index2 );
461 setCurrentIndex( index2 );
464void QskMenu::mousePressEvent( QMouseEvent* event )
468 if ( event->button() == Qt::LeftButton )
470 const auto index = indexAtPosition( qskMousePosition( event ) );
473 setCurrentIndex( index );
474 m_data->isPressed =
true;
480 Inherited::mousePressEvent( event );
483void QskMenu::mouseUngrabEvent()
485 m_data->isPressed =
false;
489void QskMenu::mouseReleaseEvent( QMouseEvent* event )
491 if ( event->button() == Qt::LeftButton )
495 m_data->isPressed =
false;
497 const auto index = m_data->currentIndex;
500 && ( index == indexAtPosition( qskMousePosition( event ) ) ) )
503 trigger( m_data->currentIndex );
510 Inherited::mouseReleaseEvent( event );
517 if ( m_data->currentIndex < 0 )
519 if ( !m_data->actions.isEmpty() )
520 setCurrentIndex( m_data->actions.first() );
531 if( currentIndex() >= 0 )
533 auto actionIndex = qskActionIndex( m_data->actions, currentIndex() );
542QRectF QskMenu::cellRect(
int index )
const
544 const auto actionIndex = qskActionIndex( m_data->actions, index );
550int QskMenu::indexAtPosition(
const QPointF& pos )
const
555 return m_data->actions.value( index, -1 );
558bool QskMenu::isPressed()
const
560 return m_data->isPressed;
563void QskMenu::trigger(
int index )
565 if ( index >= 0 && index < m_data->options.count() )
567 m_data->triggeredIndex = index;
568 Q_EMIT triggered( index );
580 return m_data->triggeredIndex;
583#include "moc_QskMenu.cpp"
Lookup key for a QskSkinHintTable.
void focusIndicatorRectChanged()
static const QskAspect::State Hovered
virtual QRectF focusIndicatorRect() const
QRectF contentsRect() const
void mouseUngrabEvent() override
bool resetSkinHint(QskAspect)
Remove a hint from the local hint table.
bool setSkinHint(QskAspect, const QVariant &)
Insert a hint into the local hint table.
const QskSkinlet * effectiveSkinlet() const
bool setFlagHint(QskAspect, int flag)
Sets a flag hint.
virtual void updateNode(QSGNode *)
T flagHint(QskAspect, T=T()) const
Retrieves a flag hint.