From dbe1fad7ec6e510b3e873628748a8aacf6d52526 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 15 May 2023 16:58:46 +0200 Subject: [PATCH] QskMenuButton introduced --- examples/gallery/main.cpp | 105 ++++++++-------------- src/CMakeLists.txt | 4 +- src/controls/QskMenu.h | 3 +- src/controls/QskMenuButton.cpp | 153 +++++++++++++++++++++++++++++++++ src/controls/QskMenuButton.h | 67 +++++++++++++++ support/SkinnyNamespace.cpp | 16 +++- support/SkinnyNamespace.h | 1 + 7 files changed, 274 insertions(+), 75 deletions(-) create mode 100644 src/controls/QskMenuButton.cpp create mode 100644 src/controls/QskMenuButton.h diff --git a/examples/gallery/main.cpp b/examples/gallery/main.cpp index be93afa7..48c227d7 100644 --- a/examples/gallery/main.cpp +++ b/examples/gallery/main.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -111,100 +112,64 @@ namespace } }; - class MenuButton : public QskPushButton - { - public: - MenuButton( const QString& text, QQuickItem* parent = nullptr ) - : QskPushButton( text, parent ) - { - connect( this, &QskPushButton::pressed, this, &MenuButton::openMenu ); - } - - private: - void openMenu() - { - auto menu = new QskMenu( window()->contentItem() ); - - populateMenu( menu ); - - menu->setOrigin( geometry().bottomLeft() ); - menu->open(); - } - - virtual void populateMenu( QskMenu* ) = 0; - }; - - class SkinButton final : public MenuButton + class SkinButton final : public QskMenuButton { public: SkinButton( const QString& text, QQuickItem* parent = nullptr ) - : MenuButton( text, parent ) - { - } - - private: - void populateMenu( QskMenu* menu ) override + : QskMenuButton( text, parent ) { const auto names = qskSkinManager->skinNames(); - for ( const auto& name : names ) - menu->addOption( QUrl(), name ); + setOptions( names ); if ( const auto index = names.indexOf( qskSetup->skinName() ) ) - menu->setCurrentIndex( index ); + setStartIndex( index ); - connect( menu, &QskMenu::triggered, this, &SkinButton::changeSkin ); + connect( this, &QskMenuButton::triggered, + this, &SkinButton::changeSkin ); + } + + void openMenu() override + { + const auto names = qskSkinManager->skinNames(); + setStartIndex( names.indexOf( qskSetup->skinName() ) ); + + QskMenuButton::openMenu(); } void changeSkin( int index ) { const auto names = qskSkinManager->skinNames(); - if ( index < 0 || index >= names.size() ) - return; - if ( index == names.indexOf( qskSetup->skinName() ) ) - return; - - auto oldSkin = qskSetup->skin(); - if ( oldSkin->parent() == qskSetup ) - oldSkin->setParent( nullptr ); // otherwise setSkin deletes it - - if ( auto newSkin = qskSetup->setSkin( names[ index ] ) ) + if ( ( index >= 0 ) && ( index < names.size() ) + && ( index != names.indexOf( qskSetup->skinName() ) ) ) { - QskSkinTransition transition; - - transition.setSourceSkin( oldSkin ); - transition.setTargetSkin( newSkin ); - transition.setAnimation( 500 ); - - transition.process(); - - if ( oldSkin->parent() == nullptr ) - delete oldSkin; + Skinny::setSkin( index, 500 ); } } }; - class FileButton final : public MenuButton + class FileButton final : public QskMenuButton { public: FileButton( const QString& text, QQuickItem* parent = nullptr ) - : MenuButton( text, parent ) + : QskMenuButton( text, parent ) { - } - - private: - void populateMenu( QskMenu* menu ) override - { - menu->addOption( "image://shapes/Rectangle/White", "Print" ); - menu->addOption( "image://shapes/Diamond/Yellow", "Save As" ); - menu->addOption( "image://shapes/Ellipse/Red", "Setup" ); - menu->addSeparator(); - menu->addOption( "image://shapes/Hexagon/PapayaWhip", "Quit" ); + addOption( "image://shapes/Rectangle/White", "Print" ); + addOption( "image://shapes/Diamond/Yellow", "Save As" ); + addOption( "image://shapes/Ellipse/Red", "Setup" ); + addSeparator(); + addOption( "image://shapes/Hexagon/PapayaWhip", "Quit" ); // see https://github.com/uwerat/qskinny/issues/192 - connect( menu, &QskMenu::triggered, - []( int index ) { if ( index == 3 ) qApp->quit(); } ); + connect( this, &QskMenuButton::triggered, + this, &FileButton::activate ); + } + private: + void activate( int index ) + { + if ( optionAt( index ).text() == "Quit" ) + qApp->quit(); } }; @@ -241,7 +206,7 @@ namespace drawer->setEdge( Qt::RightEdge ); auto burger = new QskPushButton( "≡", this ); - burger->setEmphasis( QskPushButton::LowEmphasis ); + burger->setEmphasis( QskPushButton::LowEmphasis ); connect( burger, &QskPushButton::clicked, drawer, &QskPopup::open ); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8d8cf4e6..dfd31a94 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -184,6 +184,7 @@ list(APPEND HEADERS controls/QskListViewSkinlet.h controls/QskMenu.h controls/QskMenuSkinlet.h + controls/QskMenuButton.h controls/QskObjectTree.h controls/QskPageIndicator.h controls/QskPageIndicatorSkinlet.h @@ -280,8 +281,9 @@ list(APPEND SOURCES controls/QskInputGrabber.cpp controls/QskListView.cpp controls/QskListViewSkinlet.cpp - controls/QskMenuSkinlet.cpp controls/QskMenu.cpp + controls/QskMenuSkinlet.cpp + controls/QskMenuButton.cpp controls/QskObjectTree.cpp controls/QskPageIndicator.cpp controls/QskPageIndicatorSkinlet.cpp diff --git a/src/controls/QskMenu.h b/src/controls/QskMenu.h index f0c98453..f01dd1a0 100644 --- a/src/controls/QskMenu.h +++ b/src/controls/QskMenu.h @@ -71,8 +71,6 @@ class QSK_EXPORT QskMenu : public QskPopup QVector< int > separators() const; QVector< int > actions() const; - void clear(); - int currentIndex() const; QString currentText() const; @@ -97,6 +95,7 @@ class QSK_EXPORT QskMenu : public QskPopup public Q_SLOTS: void setCurrentIndex( int ); + void clear(); protected: void keyPressEvent( QKeyEvent* ) override; diff --git a/src/controls/QskMenuButton.cpp b/src/controls/QskMenuButton.cpp new file mode 100644 index 00000000..30c07f41 --- /dev/null +++ b/src/controls/QskMenuButton.cpp @@ -0,0 +1,153 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskMenuButton.h" +#include "QskMenu.h" +#include "QskLabelData.h" + +#include +#include + +class QskMenuButton::PrivateData +{ + public: + int triggeredIndex = -1; + int startIndex = -1; + + QPointer< QskMenu > menu; + QVector< QskLabelData > options; +}; + +QskMenuButton::QskMenuButton( QQuickItem* parent ) + : QskMenuButton( QString(), parent ) +{ +} + +QskMenuButton::QskMenuButton( const QString& text, QQuickItem* parent ) + : QskPushButton( text, parent ) + , m_data( new PrivateData ) +{ + connect( this, &QskPushButton::pressed, + this, &QskMenuButton::openMenu ); +} + +QskMenuButton::~QskMenuButton() +{ +} + +int QskMenuButton::addOption( const QString& graphicSource, const QString& text ) +{ + return addOption( QskLabelData( text, graphicSource ) ); +} + +int QskMenuButton::addOption( const QUrl& graphicSource, const QString& text ) +{ + return addOption( QskLabelData( text, graphicSource ) ); +} + +int QskMenuButton::addOption( const QskLabelData& option ) +{ + const int index = m_data->options.count(); + m_data->options += option; + + if ( m_data->menu ) + m_data->menu->setOptions( m_data->options ); + + return index; +} + +void QskMenuButton::addSeparator() +{ + addOption( QskLabelData() ); +} + +void QskMenuButton::setOptions( const QStringList& options ) +{ + setOptions( qskCreateLabelData( options ) ); +} + +void QskMenuButton::setOptions( const QVector< QskLabelData >& options ) +{ + m_data->options = options; + + if ( m_data->menu ) + m_data->menu->setOptions( m_data->options ); +} + +void QskMenuButton::clear() +{ + m_data->options.clear(); + + if ( m_data->menu ) + m_data->menu->clear(); +} + +QVector< QskLabelData > QskMenuButton::options() const +{ + return m_data->options; +} + +QskLabelData QskMenuButton::optionAt( int index ) const +{ + return m_data->options.value( index ); +} + +int QskMenuButton::optionsCount() const +{ + return m_data->options.count(); +} + +void QskMenuButton::setStartIndex( int index ) +{ + m_data->startIndex = index; +} + +int QskMenuButton::triggeredIndex() const +{ + return m_data->triggeredIndex; +} + +QString QskMenuButton::triggeredText() const +{ + return optionAt( m_data->triggeredIndex ).text(); +} + +const QskMenu* QskMenuButton::menu() const +{ + return m_data->menu; +} + +void QskMenuButton::openMenu() +{ + if ( m_data->menu || window() == nullptr || m_data->options.isEmpty() ) + return; + + m_data->triggeredIndex = -1; + + auto menu = new QskMenu( window()->contentItem() ); + m_data->menu = menu; + + menu->setOptions( m_data->options ); + if ( m_data->startIndex >= 0 ) + menu->setCurrentIndex( m_data->startIndex ); + + menu->setOrigin( geometry().bottomLeft() ); + + connect( menu, &QskMenu::triggered, + this, &QskMenuButton::updateTriggeredIndex ); + + menu->open(); +} + +void QskMenuButton::updateTriggeredIndex( int index ) +{ + if ( m_data->triggeredIndex != index ) + { + m_data->triggeredIndex = index; + Q_EMIT triggered( index ); + } +} + +#include "moc_QskMenuButton.cpp" diff --git a/src/controls/QskMenuButton.h b/src/controls/QskMenuButton.h new file mode 100644 index 00000000..624b583f --- /dev/null +++ b/src/controls/QskMenuButton.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_MENU_BUTTON_H +#define QSK_MENU_BUTTON_H + +#include "QskPushButton.h" + +class QskMenu; +class QskLabelData; + +class QSK_EXPORT QskMenuButton : public QskPushButton +{ + Q_OBJECT + + Q_PROPERTY( QVector< QskLabelData > options READ options + WRITE setOptions NOTIFY optionsChanged ) + + Q_PROPERTY( int optionsCount READ optionsCount ) + Q_PROPERTY( int triggeredIndex READ triggeredIndex NOTIFY triggered ) + Q_PROPERTY( QString triggeredText READ triggeredText NOTIFY triggered ) + + public: + QskMenuButton( QQuickItem* parent = nullptr ); + QskMenuButton( const QString& text, QQuickItem* parent = nullptr ); + + ~QskMenuButton() override; + + int addOption( const QString&, const QString& ); + int addOption( const QUrl&, const QString& ); + int addOption( const QskLabelData& ); + void addSeparator(); + + void setOptions( const QVector< QskLabelData >& ); + void setOptions( const QStringList& ); + + QVector< QskLabelData > options() const; + QskLabelData optionAt( int ) const; + + int optionsCount() const; + + const QskMenu* menu() const; + + int triggeredIndex() const; + QString triggeredText() const; + + public Q_SLOTS: + void setStartIndex( int ); + void clear(); + + Q_SIGNALS: + void triggered( int index ); + void optionsChanged(); + + protected: + virtual void openMenu(); + + private: + void updateTriggeredIndex( int ); + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/support/SkinnyNamespace.cpp b/support/SkinnyNamespace.cpp index 0d103125..d86bc42b 100644 --- a/support/SkinnyNamespace.cpp +++ b/support/SkinnyNamespace.cpp @@ -135,13 +135,25 @@ static bool pluginPath = initPluginPath(); Q_COREAPP_STARTUP_FUNCTION( initFonts ) void Skinny::changeSkin( QskAnimationHint hint ) +{ + const auto names = qskSkinManager->skinNames(); + if ( names.size() > 1 ) + { + auto index = names.indexOf( qskSetup->skinName() ); + index = ( index + 1 ) % names.size(); + + setSkin( index, hint ); + } +} + +void Skinny::setSkin( int index, QskAnimationHint hint ) { const auto names = qskSkinManager->skinNames(); if ( names.size() <= 1 ) return; - int index = names.indexOf( qskSetup->skinName() ); - index = ( index + 1 ) % names.size(); + if ( index == names.indexOf( qskSetup->skinName() ) ) + return; auto oldSkin = qskSetup->skin(); if ( oldSkin->parent() == qskSetup ) diff --git a/support/SkinnyNamespace.h b/support/SkinnyNamespace.h index a2b30c94..5451a5e0 100644 --- a/support/SkinnyNamespace.h +++ b/support/SkinnyNamespace.h @@ -11,6 +11,7 @@ namespace Skinny { SKINNY_EXPORT void changeSkin( QskAnimationHint hint = 500 ); + SKINNY_EXPORT void setSkin( int index, QskAnimationHint hint = 500 ); SKINNY_EXPORT void changeFonts( int increment ); SKINNY_EXPORT void init(); }