diff --git a/examples/qvgviewer/MainWindow.cpp b/examples/qvgviewer/MainWindow.cpp index bc379584..e3e66d24 100644 --- a/examples/qvgviewer/MainWindow.cpp +++ b/examples/qvgviewer/MainWindow.cpp @@ -20,6 +20,10 @@ #include #include +#ifdef CONTEXT_MENU + #include +#endif + #include #include @@ -41,6 +45,10 @@ class GraphicLabel : public QskGraphicLabel setBoxShapeHint( Panel, 8 ); setAlignment( Qt::AlignCenter ); setDarknessMode( false ); + +#ifdef CONTEXT_MENU + setAcceptedMouseButtons( Qt::LeftButton ); +#endif } void setDarknessMode( bool on ) @@ -71,6 +79,25 @@ class GraphicLabel : public QskGraphicLabel startTransition( QskGraphicLabel::Graphic | QskAspect::GraphicRole, duration, oldRole, graphicRole() ); } + +#ifdef CONTEXT_MENU + protected: + void mousePressEvent( QMouseEvent* event ) override + { + auto menu = new QskMenu( this ); + + menu->addItem( "image://shapes/Rectangle/White", "Launchpad" ); + menu->addItem( "image://shapes/Diamond/Yellow", "Manager" ); + menu->addItem( "image://shapes/Ellipse/Red", "Setup" ); + menu->addItem( "image://shapes/Hexagon/PapayaWhip", "Help" ); + + menu->setOrigin( event->localPos() ); + menu->open(); + + connect( menu, &QskMenu::triggered, + this, []( int index ) { qDebug() << index; } ); + } +#endif }; MainWindow::MainWindow() diff --git a/examples/qvgviewer/main.cpp b/examples/qvgviewer/main.cpp index 62f754db..c6a53784 100644 --- a/examples/qvgviewer/main.cpp +++ b/examples/qvgviewer/main.cpp @@ -8,6 +8,10 @@ #include #include +#ifdef CONTEXT_MENU + #include +#endif + #include #include @@ -19,6 +23,10 @@ int main( int argc, char* argv[] ) QskObjectCounter counter( true ); #endif +#ifdef CONTEXT_MENU + Qsk::addGraphicProvider( "shapes", new SkinnyShapeProvider() ); +#endif + QGuiApplication app( argc, argv ); SkinnyFont::init( &app ); diff --git a/examples/qvgviewer/qvgviewer.pro b/examples/qvgviewer/qvgviewer.pro index d2c05ec5..ece321d1 100644 --- a/examples/qvgviewer/qvgviewer.pro +++ b/examples/qvgviewer/qvgviewer.pro @@ -1,4 +1,5 @@ CONFIG += qskexample +DEFINES += CONTEXT_MENU HEADERS += \ MainWindow.h diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 0487b10d..7bb415fd 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -138,6 +139,7 @@ namespace void setupInputPredictionBar(); void setupVirtualKeyboard(); void setupListView(); + void setupMenu(); void setupPageIndicator(); void setupPopup(); void setupProgressBar(); @@ -256,6 +258,7 @@ void Editor::setup() setupInputPredictionBar(); setupVirtualKeyboard(); setupListView(); + setupMenu(); setupPageIndicator(); setupPopup(); setupProgressBar(); @@ -298,6 +301,38 @@ void Editor::setupPopup() setGradient( Q::Overlay, QColor( 220, 220, 220, 150 ) ); } +void Editor::setupMenu() +{ + using Q = QskMenu; + + const QColor c1( 78, 158, 38 ); + const QColor c2( 15, 103, 43 ); + + setBoxShape( Q::Panel, 4 ); + setVGradient( Q::Panel, c1, c2 ); + +#if 0 + setPadding( Q::Separator, QMarginsF( 10, 0, 10, 0 ) ); + setMetric( Q::Separator | QskAspect::Size, 3 ); + setVGradient( Q::Separator, c2, c1 ); +#endif + + // while fading out we are in Q::Closed state + for ( auto state : { QskAspect::NoState, Q::Closed } ) + setHGradient( Q::Cell | Q::Selected | state, c2, c2.lighter( 2 ) ); + + setPadding( Q::Cell, QskMargins( 2, 10, 2, 10 ) ); + setSpacing( Q::Cell, 5 ); + + setColor( Q::Text, QColor( 255, 255, 255 ) ); + setFontRole( Q::Text, QskSkin::SmallFont ); + + setMetric( Q::Panel | QskAspect::Position, 0 ); + setMetric( Q::Panel | QskAspect::Position | QskPopup::Closed, 1 ); + + setAnimation( Q::Panel | QskAspect::Metric, 200 ); +} + void Editor::setupTextLabel() { using Q = QskTextLabel; diff --git a/src/controls/QskMenu.cpp b/src/controls/QskMenu.cpp new file mode 100644 index 00000000..5a6dd5b5 --- /dev/null +++ b/src/controls/QskMenu.cpp @@ -0,0 +1,301 @@ +#include "QskMenu.h" + +#include +#include +#include +#include + +#include +#include + +QSK_SUBCONTROL( QskMenu, Panel ) +QSK_SUBCONTROL( QskMenu, Text ) +QSK_SUBCONTROL( QskMenu, Graphic ) +QSK_SUBCONTROL( QskMenu, Cell ) + +QSK_SYSTEM_STATE( QskMenu, Selected, QskAspect::FirstSystemState << 2 ) + +class QskMenu::PrivateData +{ + public: + QVector< Entry > entries; + QVector< QskGraphic > icons; + + QskTextOptions textOptions; + QPointF origin; + + int currentIndex = 0; + bool isPressed = false; +}; + +QskMenu::QskMenu( QQuickItem* parent ) + : Inherited( parent ) + , m_data( new PrivateData ) +{ + setModal( true ); + setOverlay( true ); + + setFaderAspect( QskMenu::Panel | QskAspect::Position | QskAspect::Metric ); + + setPopupFlag( QskPopup::CloseOnPressOutside, true ); + setPopupFlag( QskPopup::DeleteOnClose, true ); + + initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); +} + +QskMenu::~QskMenu() +{ +} + +void QskMenu::setOrigin( const QPointF& origin ) +{ + if ( origin != m_data->origin ) + { + m_data->origin = origin; + Q_EMIT originChanged( origin ); + } +} + +QPointF QskMenu::origin() const +{ + return m_data->origin; +} + +void QskMenu::addItem( const QUrl& graphicSource, const QString& text ) +{ + m_data->entries += { graphicSource, text }; + +#if 1 + { + const auto& entries = m_data->entries; + + m_data->icons.clear(); + m_data->icons.reserve( entries.count() ); + + for( const auto& entry : entries ) + { + QskGraphic graphic; + + if( !entry.iconSource.isEmpty() ) + graphic = Qsk::loadGraphic( entry.iconSource ); + + m_data->icons += graphic; + } + } +#endif + + resetImplicitSize(); + update(); +} + +void QskMenu::addItem( const QString& graphicSource, const QString& text ) +{ + addItem( QUrl( graphicSource ), text ); +} + +void QskMenu::addSeparator() +{ + // TODO ... +} + +void QskMenu::clear() +{ + m_data->icons.clear(); + m_data->entries.clear(); +} + +int QskMenu::count() const +{ + return m_data->entries.count(); +} + +QskMenu::Entry QskMenu::entryAt( int index ) const +{ + if( index >= 0 && index <= m_data->entries.count() ) + { + return m_data->entries[ index ]; + } + + return Entry(); +} + +QskGraphic QskMenu::graphicAt( int index ) const +{ + if( index >= 0 && index <= m_data->icons.count() ) + { + return m_data->icons[ index ]; + } + + return QskGraphic(); +} + +void QskMenu::setTextOptions( const QskTextOptions& textOptions ) +{ + if( textOptions != m_data->textOptions ) + { + m_data->textOptions = textOptions; + update(); + } +} + +QskTextOptions QskMenu::textOptions() const +{ + return m_data->textOptions; +} + +void QskMenu::setCurrentIndex( int index ) +{ + if( index >= 0 && index < count() + && ( index != m_data->currentIndex ) ) + { + m_data->currentIndex = index; + update(); + + Q_EMIT currentIndexChanged( index ); + } +} + +int QskMenu::currentIndex() const +{ + return m_data->currentIndex; +} + +void QskMenu::keyPressEvent( QKeyEvent* event ) +{ + if( m_data->currentIndex < 0 ) + { + return; + } + + int key = event->key(); + + switch( key ) + { + case Qt::Key_Up: + { + traverse( -1 ); + break; + } + + case Qt::Key_Down: + { + traverse( 1 ); + break; + } + + case Qt::Key_Select: + case Qt::Key_Space: + { + m_data->isPressed = true; + return; + } + + case Qt::Key_Escape: + case Qt::Key_Cancel: + { + setSelectedIndex( -1 ); + return; + } + + default: + { + return; + } + } +} + +void QskMenu::keyReleaseEvent( QKeyEvent* ) +{ + if( m_data->isPressed ) + { + m_data->isPressed = false; + setSelectedIndex( m_data->currentIndex ); + } +} + +void QskMenu::wheelEvent( QWheelEvent* event ) +{ +#if QT_VERSION < 0x050e00 + const int delta = event->delta(); +#else + const int delta = event->angleDelta().y(); +#endif + + // we ignore the step count and use its direction only + traverse( delta < 0 ? 1 : -1 ); +} + +void QskMenu::traverse( int steps ) +{ + auto& index = m_data->currentIndex; + + index = ( index + steps ) % count(); + if ( index < 0 ) + index += count(); + + update(); + Q_EMIT currentIndexChanged( index ); +} + +int QskMenu::indexAtPosition( const QPointF& pos ) const +{ + const auto r = contentsRect(); + + if( count() > 0 && r.contains( pos ) ) + return ( pos.y() - r.y() ) / ( r.height() / count() ); + + return -1; +} + +QskColorFilter QskMenu::graphicFilterAt( int index ) const +{ + Q_UNUSED( index ); + return QskColorFilter(); +} + +void QskMenu::mousePressEvent( QMouseEvent* event ) +{ + if ( event->button() == Qt::LeftButton ) + { + if ( contentsRect().contains( event->localPos() ) ) + m_data->isPressed = true; + } +} + +void QskMenu::mouseReleaseEvent( QMouseEvent* event ) +{ + if ( event->button() == Qt::LeftButton ) + { + if( m_data->isPressed ) + { + setSelectedIndex( indexAtPosition( event->localPos() ) ); + m_data->isPressed = false; + } + } +} + +void QskMenu::aboutToShow() +{ + setGeometry( QRectF( m_data->origin, sizeConstraint() ) ); + Inherited::aboutToShow(); +} + +QRectF QskMenu::focusIndicatorRect() const +{ + // highlighting the item is good enough + return QRectF(); +} + +void QskMenu::setSelectedIndex( int index ) +{ + if ( !isOpen() ) + return; + + if ( index >= 0 ) + setCurrentIndex( index ); + + Q_EMIT triggered( index ); + close(); +} + +#include "moc_QskMenu.cpp" diff --git a/src/controls/QskMenu.h b/src/controls/QskMenu.h new file mode 100644 index 00000000..73f0bf26 --- /dev/null +++ b/src/controls/QskMenu.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_MENU_H +#define QSK_MENU_H + +#include "QskPopup.h" + +#include +#include + +class QskTextOptions; +class QskGraphic; + +class QSK_EXPORT QskMenu : public QskPopup +{ + Q_OBJECT + + Q_PROPERTY( QPointF origin READ origin + WRITE setOrigin NOTIFY originChanged ) + + Q_PROPERTY( int count READ count ) + + Q_PROPERTY( int currentIndex READ currentIndex + WRITE setCurrentIndex NOTIFY currentIndexChanged ) + + using Inherited = QskPopup; + + public: + struct Entry + { + QUrl iconSource; + QString text; + }; + + QSK_SUBCONTROLS( Panel, Cell, Text, Graphic ) + QSK_STATES( Selected ) + + QskMenu( QQuickItem* parentItem = nullptr ); + ~QskMenu() override; + + void setOrigin( const QPointF& ); + QPointF origin() const; + + // insert, remove, functors, actions, QskGraphic ... + void addItem( const QUrl& graphicSource, const QString& text ); + void addItem( const QString& graphicSource, const QString& text ); + + void addSeparator(); + + int indexAtPosition( const QPointF& ) const; + + Entry entryAt( int index ) const; + QskGraphic graphicAt( int index ) const; + + void setTextOptions( const QskTextOptions& textOptions ); + QskTextOptions textOptions() const; + + int currentIndex() const; + int count() const; + + virtual QskColorFilter graphicFilterAt( int index ) const; + QRectF focusIndicatorRect() const override; + + Q_SIGNALS: + void triggered( int index ); + void currentIndexChanged( int index ); + void originChanged( const QPointF& ); + + public Q_SLOTS: + void setCurrentIndex( int index ); + void clear(); + + protected: + void keyPressEvent( QKeyEvent* ) override; + void keyReleaseEvent( QKeyEvent* ) override; + void wheelEvent( QWheelEvent* ) override; + + void mousePressEvent( QMouseEvent* ) override; + void mouseReleaseEvent( QMouseEvent* ) override; + + void aboutToShow() override; + + private: + void traverse( int steps ); + void setSelectedIndex( int index ); + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/controls/QskMenuSkinlet.cpp b/src/controls/QskMenuSkinlet.cpp new file mode 100644 index 00000000..0b653255 --- /dev/null +++ b/src/controls/QskMenuSkinlet.cpp @@ -0,0 +1,313 @@ +#include "QskMenuSkinlet.h" +#include "QskMenu.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + class StateChanger : public QskSkinStateChanger + { + public: + inline StateChanger( const QskSkinnable* skinnable, bool isSelected ) + : QskSkinStateChanger( skinnable, isSelected + ? QskMenu::Selected : QskAspect::NoState ) + { + } + }; +} + +static qreal qskMaxTextWidth( const QskMenu* menu ) +{ + const QFontMetricsF fm( menu->effectiveFont( QskMenu::Text ) ); + + auto maxWidth = 0.0; + + for ( int i = 0; i < menu->count(); i++ ) + { + const auto entry = menu->entryAt( i ); + if( !entry.text.isEmpty() ) + { + const auto w = qskHorizontalAdvance( fm, entry.text ); + if( w > maxWidth ) + maxWidth = w; + } + } + + return maxWidth; +} + +static QSizeF qskIconSize( const QskMenu* menu ) +{ + const auto hint = menu->strutSizeHint( QskMenu::Graphic ); + const qreal textHeight = QFontMetrics( + menu->effectiveFont( QskMenu::Text ) ).height(); + + const auto h = qMax( hint.height(), textHeight ); + + qreal maxW = 0.0; + for ( int i = 0; i < menu->count(); i++ ) + { + const auto w = menu->graphicAt( i ).widthForHeight( h ); + if( w > maxW ) + maxW = w; + } + + const auto w = qMax( hint.width(), maxW ); + + return QSizeF( w, h ); +} + +static QSizeF qskItemSize( const QskMenu* menu ) +{ + const auto spacing = menu->spacingHint( QskMenu::Cell ); + const QskMargins padding = menu->paddingHint( QskMenu::Cell ); + + const auto sz = qskIconSize( menu ); + + const qreal h = sz.height() + padding.height(); + const qreal w = sz.width() + spacing + qskMaxTextWidth( menu ) + padding.width(); + + return QSizeF( w, h ); +} + +static QSGNode* qskUpdateGraphicNode( const QskMenu* menu, + const QRectF& rect, const QskGraphic& graphic, QSGNode* node ) +{ + const auto alignment = menu->alignmentHint( QskMenu::Graphic, Qt::AlignCenter ); + const auto colorFilter = menu->effectiveGraphicFilter( QskMenu::Graphic ); + + return QskSkinlet::updateGraphicNode( + menu, node, graphic, colorFilter, rect, alignment ); +} + +static QSGNode* qskUpdateTextNode( const QskMenu* menu, + const QRectF& rect, const QString& text, QSGNode* node ) +{ + const auto alignment = menu->alignmentHint( + QskMenu::Text, Qt::AlignVCenter | Qt::AlignLeft ); + + return QskSkinlet::updateTextNode( menu, node, rect, alignment, + text, menu->textOptions(), QskMenu::Text ); +} + +static QSGNode* qskUpdateBackgroundNode( const QskMenu*, QSGNode* ) +{ + return nullptr; // TODO +} + +static QRectF qskCellRect( const QskMenu* menu, int index ) +{ + if ( index >= 0 ) + { + auto r = menu->subControlRect( QskMenu::Panel ); + r = menu->innerBox( QskMenu::Panel, r ); + + const auto sz = qskItemSize( menu ); + + const auto y = r.y() + index * sz.height(); + return QRectF( r.x(), y, r.width(), sz.height() ); + } + + return QRectF(); +} + +static QSGNode* qskUpdateCursorNode( const QskMenu* menu, QSGNode* node ) +{ + const auto r = qskCellRect( menu, menu->currentIndex() ); + if ( !r.isEmpty() ) + { + const StateChanger stateChanger( menu, true ); + + auto cursorNode = QskSGNode::ensureNode< QskBoxNode >( node ); + cursorNode->setBoxData( r, menu->gradientHint( QskMenu::Cell ) ); + + return cursorNode; + } + + return nullptr; +} + +static void qskUpdateItemNode( + const QskMenu* menu, const QRectF& graphicRect, const QskGraphic& graphic, + const QRectF& textRect, const QString& text, QSGNode* itemNode ) +{ + enum { GraphicRole, TextRole }; + static QVector< quint8 > roles = { GraphicRole, TextRole }; + + for ( const auto role : roles ) + { + auto oldNode = QskSGNode::findChildNode( itemNode, role ); + QSGNode* newNode = nullptr; + + if( role == GraphicRole ) + newNode = qskUpdateGraphicNode( menu, graphicRect, graphic, oldNode ); + else + newNode = qskUpdateTextNode( menu, textRect, text, oldNode ); + + QskSGNode::replaceChildNode( roles, role, itemNode, oldNode, newNode ); + } +} + +static QSGNode* qskUpdateItemsNode( const QskMenu* menu, QSGNode* rootNode ) +{ + const auto padding = menu->paddingHint( QskMenu::Cell ); + const auto spacing = menu->spacingHint( QskMenu::Cell ); + const auto iconSize = qskIconSize( menu ); + + auto boundingRect = menu->subControlRect( QskMenu::Panel ); + boundingRect = menu->innerBox( QskMenu::Panel, boundingRect ); + + auto itemSize = iconSize; + itemSize.rwidth() += spacing + qskMaxTextWidth( menu ); + +#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 ) + itemSize = itemSize.grownBy( padding ); +#else + itemSize.rwidth() += padding.left() + padding.right(); + itemSize.rheight() += padding.top() + padding.bottom(); +#endif + + itemSize = itemSize.expandedTo( menu->strutSizeHint( QskMenu::Graphic ) ); + + if ( rootNode == nullptr ) + rootNode = new QSGNode(); + + QSGNode* node = nullptr; + + qreal y = boundingRect.y(); + + for( int i = 0; i < menu->count(); i++ ) + { + if ( node == nullptr ) + node = rootNode->firstChild(); + else + node = node->nextSibling(); + + if ( node == nullptr ) + { + node = new QSGNode(); + rootNode->appendChildNode( node ); + } + + { + const StateChanger stateChanger( menu, menu->currentIndex() == i ); + + QRectF cellRect( boundingRect.x(), y, + boundingRect.width(), itemSize.height() ); + + cellRect = cellRect.marginsRemoved( padding ); + + auto graphicRect = cellRect; + graphicRect.setWidth( iconSize.width() ); + + auto textRect = cellRect; + textRect.setX( graphicRect.right() + spacing ); + + qskUpdateItemNode( menu, graphicRect, menu->graphicAt( i ), + textRect, menu->entryAt( i ).text, node ); + } + + y += itemSize.height(); + + } + + // Remove trailing nodes + QskSGNode::removeAllChildNodesAfter( rootNode, node ); + + return rootNode; +} + +QskMenuSkinlet::QskMenuSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ + appendNodeRoles( { PanelRole } ); +} + +QRectF QskMenuSkinlet::subControlRect( + const QskSkinnable* skinnable, const QRectF& contentsRect, + QskAspect::Subcontrol subControl ) const +{ + if( subControl == QskMenu::Panel ) + { + return contentsRect; + } + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); +} + +QSGNode* QskMenuSkinlet::updateContentsNode( + const QskPopup* popup, QSGNode* contentsNode ) const +{ + enum { Panel, Backgrounds, Cursor, Items }; + static QVector< quint8 > roles = { Panel, Backgrounds, Cursor, Items }; + + if ( contentsNode == nullptr ) + contentsNode = new QSGNode(); + + const auto menu = static_cast< const QskMenu* >( popup ); + + for ( const auto role : roles ) + { + auto oldNode = QskSGNode::findChildNode( contentsNode, role ); + + QSGNode* newNode; + + switch( role ) + { + case Panel: + { + newNode = updateBoxNode( menu, oldNode, QskMenu::Panel ); + break; + } + case Backgrounds: + { + newNode = qskUpdateBackgroundNode( menu, oldNode ); + break; + } + case Cursor: + { + newNode = qskUpdateCursorNode( menu, oldNode ); + break; + } + case Items: + { + newNode = qskUpdateItemsNode( menu, oldNode ); + break; + } + } + + QskSGNode::replaceChildNode( roles, role, contentsNode, oldNode, newNode ); + } + + return contentsNode; +} + +QSizeF QskMenuSkinlet::sizeHint( const QskSkinnable* skinnable, + Qt::SizeHint which, const QSizeF& ) const +{ + using Q = QskMenu; + + if ( which != Qt::PreferredSize ) + return QSizeF(); + + const auto menu = static_cast< const QskMenu* >( skinnable ); + + const auto itemSize = qskItemSize( menu ); + const auto count = menu->count(); + + const qreal h = count * itemSize.height(); + return menu->outerBoxSize( Q::Panel, QSizeF( itemSize.width(), h ) ); +} + +#include "moc_QskMenuSkinlet.cpp" diff --git a/src/controls/QskMenuSkinlet.h b/src/controls/QskMenuSkinlet.h new file mode 100644 index 00000000..f70f9dc2 --- /dev/null +++ b/src/controls/QskMenuSkinlet.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +class QskMenu; + +class QSK_EXPORT QskMenuSkinlet : public QskPopupSkinlet +{ + Q_GADGET + + using Inherited = QskPopupSkinlet; + + public: + enum NodeRole + { + PanelRole = QskPopupSkinlet::RoleCount, + RoleCount + }; + + Q_INVOKABLE QskMenuSkinlet( QskSkin* = nullptr ); + ~QskMenuSkinlet() = default; + + QRectF subControlRect( const QskSkinnable*, const QRectF&, + QskAspect::Subcontrol ) const override; + + QSizeF sizeHint( const QskSkinnable*, + Qt::SizeHint, const QSizeF& ) const override; + + private: + QSGNode* updateContentsNode( const QskPopup*, QSGNode* ) const override; +}; diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index 0b9469fc..429ac4cf 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -37,6 +37,9 @@ QSK_QT_PRIVATE_END #include "QskListView.h" #include "QskListViewSkinlet.h" +#include "QskMenu.h" +#include "QskMenuSkinlet.h" + #include "QskPopup.h" #include "QskPopupSkinlet.h" @@ -145,6 +148,7 @@ QskSkin::QskSkin( QObject* parent ) declareSkinlet< QskListView, QskListViewSkinlet >(); declareSkinlet< QskPageIndicator, QskPageIndicatorSkinlet >(); declareSkinlet< QskPopup, QskPopupSkinlet >(); + declareSkinlet< QskMenu, QskMenuSkinlet >(); declareSkinlet< QskPushButton, QskPushButtonSkinlet >(); declareSkinlet< QskScrollView, QskScrollViewSkinlet >(); declareSkinlet< QskSeparator, QskSeparatorSkinlet >(); diff --git a/src/src.pro b/src/src.pro index e59faa07..134e460c 100644 --- a/src/src.pro +++ b/src/src.pro @@ -152,6 +152,8 @@ HEADERS += \ controls/QskInputGrabber.h \ controls/QskListView.h \ controls/QskListViewSkinlet.h \ + controls/QskMenu.h \ + controls/QskMenuSkinlet.h \ controls/QskObjectTree.h \ controls/QskPageIndicator.h \ controls/QskPageIndicatorSkinlet.h \ @@ -229,6 +231,8 @@ SOURCES += \ controls/QskInputGrabber.cpp \ controls/QskListView.cpp \ controls/QskListViewSkinlet.cpp \ + controls/QskMenuSkinlet.cpp \ + controls/QskMenu.cpp \ controls/QskObjectTree.cpp \ controls/QskPageIndicator.cpp \ controls/QskPageIndicatorSkinlet.cpp \