QskMenu added ( work in progress )
This commit is contained in:
parent
8d2ff3c3fe
commit
8010d70e4a
@ -20,6 +20,10 @@
|
|||||||
#include <QskTabView.h>
|
#include <QskTabView.h>
|
||||||
#include <QskBoxShapeMetrics.h>
|
#include <QskBoxShapeMetrics.h>
|
||||||
|
|
||||||
|
#ifdef CONTEXT_MENU
|
||||||
|
#include <QskMenu.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
@ -41,6 +45,10 @@ class GraphicLabel : public QskGraphicLabel
|
|||||||
setBoxShapeHint( Panel, 8 );
|
setBoxShapeHint( Panel, 8 );
|
||||||
setAlignment( Qt::AlignCenter );
|
setAlignment( Qt::AlignCenter );
|
||||||
setDarknessMode( false );
|
setDarknessMode( false );
|
||||||
|
|
||||||
|
#ifdef CONTEXT_MENU
|
||||||
|
setAcceptedMouseButtons( Qt::LeftButton );
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDarknessMode( bool on )
|
void setDarknessMode( bool on )
|
||||||
@ -71,6 +79,25 @@ class GraphicLabel : public QskGraphicLabel
|
|||||||
startTransition( QskGraphicLabel::Graphic | QskAspect::GraphicRole,
|
startTransition( QskGraphicLabel::Graphic | QskAspect::GraphicRole,
|
||||||
duration, oldRole, 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()
|
MainWindow::MainWindow()
|
||||||
|
@ -8,6 +8,10 @@
|
|||||||
#include <SkinnyFont.h>
|
#include <SkinnyFont.h>
|
||||||
#include <SkinnyShortcut.h>
|
#include <SkinnyShortcut.h>
|
||||||
|
|
||||||
|
#ifdef CONTEXT_MENU
|
||||||
|
#include <SkinnyShapeProvider.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QskFocusIndicator.h>
|
#include <QskFocusIndicator.h>
|
||||||
#include <QskObjectCounter.h>
|
#include <QskObjectCounter.h>
|
||||||
|
|
||||||
@ -19,6 +23,10 @@ int main( int argc, char* argv[] )
|
|||||||
QskObjectCounter counter( true );
|
QskObjectCounter counter( true );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONTEXT_MENU
|
||||||
|
Qsk::addGraphicProvider( "shapes", new SkinnyShapeProvider() );
|
||||||
|
#endif
|
||||||
|
|
||||||
QGuiApplication app( argc, argv );
|
QGuiApplication app( argc, argv );
|
||||||
|
|
||||||
SkinnyFont::init( &app );
|
SkinnyFont::init( &app );
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
CONFIG += qskexample
|
CONFIG += qskexample
|
||||||
|
DEFINES += CONTEXT_MENU
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
MainWindow.h
|
MainWindow.h
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <QskInputPanelBox.h>
|
#include <QskInputPanelBox.h>
|
||||||
#include <QskInputPredictionBar.h>
|
#include <QskInputPredictionBar.h>
|
||||||
#include <QskListView.h>
|
#include <QskListView.h>
|
||||||
|
#include <QskMenu.h>
|
||||||
#include <QskPageIndicator.h>
|
#include <QskPageIndicator.h>
|
||||||
#include <QskPopup.h>
|
#include <QskPopup.h>
|
||||||
#include <QskProgressBar.h>
|
#include <QskProgressBar.h>
|
||||||
@ -138,6 +139,7 @@ namespace
|
|||||||
void setupInputPredictionBar();
|
void setupInputPredictionBar();
|
||||||
void setupVirtualKeyboard();
|
void setupVirtualKeyboard();
|
||||||
void setupListView();
|
void setupListView();
|
||||||
|
void setupMenu();
|
||||||
void setupPageIndicator();
|
void setupPageIndicator();
|
||||||
void setupPopup();
|
void setupPopup();
|
||||||
void setupProgressBar();
|
void setupProgressBar();
|
||||||
@ -256,6 +258,7 @@ void Editor::setup()
|
|||||||
setupInputPredictionBar();
|
setupInputPredictionBar();
|
||||||
setupVirtualKeyboard();
|
setupVirtualKeyboard();
|
||||||
setupListView();
|
setupListView();
|
||||||
|
setupMenu();
|
||||||
setupPageIndicator();
|
setupPageIndicator();
|
||||||
setupPopup();
|
setupPopup();
|
||||||
setupProgressBar();
|
setupProgressBar();
|
||||||
@ -298,6 +301,38 @@ void Editor::setupPopup()
|
|||||||
setGradient( Q::Overlay, QColor( 220, 220, 220, 150 ) );
|
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()
|
void Editor::setupTextLabel()
|
||||||
{
|
{
|
||||||
using Q = QskTextLabel;
|
using Q = QskTextLabel;
|
||||||
|
301
src/controls/QskMenu.cpp
Normal file
301
src/controls/QskMenu.cpp
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
#include "QskMenu.h"
|
||||||
|
|
||||||
|
#include <QskGraphicProvider.h>
|
||||||
|
#include <QskTextOptions.h>
|
||||||
|
#include <QskGraphic.h>
|
||||||
|
#include <QskColorFilter.h>
|
||||||
|
|
||||||
|
#include <QFontMetricsF>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
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"
|
94
src/controls/QskMenu.h
Normal file
94
src/controls/QskMenu.h
Normal file
@ -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 <qurl.h>
|
||||||
|
#include <qstring.h>
|
||||||
|
|
||||||
|
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
|
313
src/controls/QskMenuSkinlet.cpp
Normal file
313
src/controls/QskMenuSkinlet.cpp
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
#include "QskMenuSkinlet.h"
|
||||||
|
#include "QskMenu.h"
|
||||||
|
|
||||||
|
#include <QskBoxNode.h>
|
||||||
|
#include <QskGraphic.h>
|
||||||
|
#include <QskColorFilter.h>
|
||||||
|
#include <QskGraphicNode.h>
|
||||||
|
#include <QskTextNode.h>
|
||||||
|
#include <QskTextOptions.h>
|
||||||
|
#include <QskSGNode.h>
|
||||||
|
#include <QskFunctions.h>
|
||||||
|
#include <QskSkinStateChanger.h>
|
||||||
|
#include <QskMargins.h>
|
||||||
|
|
||||||
|
#include <qfontmetrics.h>
|
||||||
|
|
||||||
|
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"
|
31
src/controls/QskMenuSkinlet.h
Normal file
31
src/controls/QskMenuSkinlet.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QskPopupSkinlet.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
@ -37,6 +37,9 @@ QSK_QT_PRIVATE_END
|
|||||||
#include "QskListView.h"
|
#include "QskListView.h"
|
||||||
#include "QskListViewSkinlet.h"
|
#include "QskListViewSkinlet.h"
|
||||||
|
|
||||||
|
#include "QskMenu.h"
|
||||||
|
#include "QskMenuSkinlet.h"
|
||||||
|
|
||||||
#include "QskPopup.h"
|
#include "QskPopup.h"
|
||||||
#include "QskPopupSkinlet.h"
|
#include "QskPopupSkinlet.h"
|
||||||
|
|
||||||
@ -145,6 +148,7 @@ QskSkin::QskSkin( QObject* parent )
|
|||||||
declareSkinlet< QskListView, QskListViewSkinlet >();
|
declareSkinlet< QskListView, QskListViewSkinlet >();
|
||||||
declareSkinlet< QskPageIndicator, QskPageIndicatorSkinlet >();
|
declareSkinlet< QskPageIndicator, QskPageIndicatorSkinlet >();
|
||||||
declareSkinlet< QskPopup, QskPopupSkinlet >();
|
declareSkinlet< QskPopup, QskPopupSkinlet >();
|
||||||
|
declareSkinlet< QskMenu, QskMenuSkinlet >();
|
||||||
declareSkinlet< QskPushButton, QskPushButtonSkinlet >();
|
declareSkinlet< QskPushButton, QskPushButtonSkinlet >();
|
||||||
declareSkinlet< QskScrollView, QskScrollViewSkinlet >();
|
declareSkinlet< QskScrollView, QskScrollViewSkinlet >();
|
||||||
declareSkinlet< QskSeparator, QskSeparatorSkinlet >();
|
declareSkinlet< QskSeparator, QskSeparatorSkinlet >();
|
||||||
|
@ -152,6 +152,8 @@ HEADERS += \
|
|||||||
controls/QskInputGrabber.h \
|
controls/QskInputGrabber.h \
|
||||||
controls/QskListView.h \
|
controls/QskListView.h \
|
||||||
controls/QskListViewSkinlet.h \
|
controls/QskListViewSkinlet.h \
|
||||||
|
controls/QskMenu.h \
|
||||||
|
controls/QskMenuSkinlet.h \
|
||||||
controls/QskObjectTree.h \
|
controls/QskObjectTree.h \
|
||||||
controls/QskPageIndicator.h \
|
controls/QskPageIndicator.h \
|
||||||
controls/QskPageIndicatorSkinlet.h \
|
controls/QskPageIndicatorSkinlet.h \
|
||||||
@ -229,6 +231,8 @@ SOURCES += \
|
|||||||
controls/QskInputGrabber.cpp \
|
controls/QskInputGrabber.cpp \
|
||||||
controls/QskListView.cpp \
|
controls/QskListView.cpp \
|
||||||
controls/QskListViewSkinlet.cpp \
|
controls/QskListViewSkinlet.cpp \
|
||||||
|
controls/QskMenuSkinlet.cpp \
|
||||||
|
controls/QskMenu.cpp \
|
||||||
controls/QskObjectTree.cpp \
|
controls/QskObjectTree.cpp \
|
||||||
controls/QskPageIndicator.cpp \
|
controls/QskPageIndicator.cpp \
|
||||||
controls/QskPageIndicatorSkinlet.cpp \
|
controls/QskPageIndicatorSkinlet.cpp \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user