From 5da92830dfb553a1ceded2093d8ddb3560e33c05 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 29 Oct 2018 20:11:48 +0100 Subject: [PATCH] QskSubWindow titleBar improvements --- examples/desktop/main.cpp | 13 ++- skins/material/QskMaterialSkin.cpp | 15 ++- skins/squiek/QskSquiekSkin.cpp | 20 +++- src/controls/QskSubWindow.cpp | 153 +++++++++++++++++++++------ src/controls/QskSubWindow.h | 53 +++++++--- src/controls/QskSubWindowSkinlet.cpp | 94 ++++++++++++++-- src/controls/QskSubWindowSkinlet.h | 7 +- src/dialogs/QskDialog.cpp | 2 +- 8 files changed, 292 insertions(+), 65 deletions(-) diff --git a/examples/desktop/main.cpp b/examples/desktop/main.cpp index de58af07..bec6c8c6 100644 --- a/examples/desktop/main.cpp +++ b/examples/desktop/main.cpp @@ -22,20 +22,25 @@ class SubWindow : public QskSubWindow { public: - SubWindow( const QString& graphicSource, QQuickItem* parent = nullptr ) + SubWindow( const QString& iconSource, QQuickItem* parent = nullptr ) : QskSubWindow( parent ) { - setObjectName( graphicSource ); + setObjectName( iconSource ); + + const QUrl url( iconSource ); + + setWindowTitle( url.fileName() ); + setWindowIconSource( url ); auto label = new QskGraphicLabel( this ); - label->setSource( graphicSource ); + label->setSource( iconSource ); label->setAlignment( Qt::AlignCenter ); setSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::MinimumExpanding ); QskShortcutMap::addShortcut( this, QKeySequence( Qt::Key_P ), true, - [ graphicSource ] { qDebug() << graphicSource; } ); + [ iconSource ] { qDebug() << iconSource; } ); } }; diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index 1f816229..5c5c71bc 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -655,8 +655,9 @@ void QskMaterialSkin::initSubWindowHints() const ColorPalette& pal = m_data->palette; - // panel + // Panel + setSkinHint( Q::Panel | Decoration, true ); setMargins( Q::Panel | Padding, 10 ); setBoxShape( Q::Panel, 0 ); setBoxBorderMetrics( Q::Panel, 2 ); @@ -668,11 +669,19 @@ void QskMaterialSkin::initSubWindowHints() setBoxBorderColors( Q::Panel, colors ); - // title bar + // TitleBar setGradient( Q::TitleBar, pal.darker200 ); setGradient( Q::TitleBar | Q::Focused, pal.accentColor ); - setFontRole( Q::TitleBar, QskSkin::TinyFont ); + // TitleBarText + setFontRole( Q::TitleBarText, QskSkin::SmallFont ); + + setSkinHint( Q::TitleBarText | Alignment, + static_cast( Qt::AlignLeft | Qt::AlignVCenter ) ); + + for ( auto subControl : { Q::Panel, Q::TitleBar, Q::TitleBarText } ) + setAnimation( subControl | Color, qskDuration ); + } #include "moc_QskMaterialSkin.cpp" diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index c6b6ba81..777d3031 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -739,11 +739,14 @@ void QskSquiekSkin::initSubWindowHints() const ColorPalette& pal = m_data->palette; + const qreal radius = 5.0; + // Panel + setSkinHint( Q::Panel | Decoration, true ); setMargins( Q::Panel | Padding, 10 ); setBoxBorderMetrics( Q::Panel, 2 ); - setBoxShape( Q::Panel, 0 ); + setBoxShape( Q::Panel, radius, radius, 0, 0, Qt::AbsoluteSize ); QskBoxBorderColors borderColors; borderColors.setColorsAt( Qt::TopEdge | Qt::LeftEdge, pal.lighter125 ); @@ -754,9 +757,22 @@ void QskSquiekSkin::initSubWindowHints() // TitleBar - setFontRole( Q::TitleBar, QskSkin::TinyFont ); setGradient( Q::TitleBar | Q::Focused, pal.highlighted ); setGradient( Q::TitleBar, pal.contrasted ); + setMetric( Q::TitleBar | Spacing, 5 ); + setMetric( Q::TitleBar | MinimumHeight, 20 ); + setBoxShape( Q::TitleBar, radius, radius, 0, 0, Qt::AbsoluteSize ); + + // TitleBarText + setFontRole( Q::TitleBarText, QskSkin::SmallFont ); + setColor( Q::TitleBarText | Q::Focused, pal.highlightedText ); + setColor( Q::TitleBarText, pal.themeForeground ); + + setSkinHint( Q::TitleBarText | Alignment, + static_cast( Qt::AlignLeft | Qt::AlignVCenter ) ); + + for ( auto subControl : { Q::Panel, Q::TitleBar, Q::TitleBarText } ) + setAnimation( subControl | Color, qskDuration ); } #include "moc_QskSquiekSkin.cpp" diff --git a/src/controls/QskSubWindow.cpp b/src/controls/QskSubWindow.cpp index d7fa3a8d..8c9b6775 100644 --- a/src/controls/QskSubWindow.cpp +++ b/src/controls/QskSubWindow.cpp @@ -6,24 +6,41 @@ #include "QskSubWindow.h" #include "QskAspect.h" #include "QskFunctions.h" +#include "QskGraphic.h" +#include "QskGraphicProvider.h" +#include "QskTextOptions.h" #include "QskQuick.h" +#include + QSK_SUBCONTROL( QskSubWindow, Panel ) QSK_SUBCONTROL( QskSubWindow, TitleBar ) +QSK_SUBCONTROL( QskSubWindow, TitleBarSymbol ) +QSK_SUBCONTROL( QskSubWindow, TitleBarText ) class QskSubWindow::PrivateData { public: PrivateData() + : isWindowIconSourceDirty( false ) { // should be available from the platform somehow. TODO ... windowButtons = QskSubWindow::WindowButtons( QskSubWindow::MinimizeButton | QskSubWindow::MaximizeButton | QskSubWindow::CloseButton ); + + windowTitleTextOptions.setElideMode( Qt::ElideRight ); } QskSubWindow::WindowButtons windowButtons; - QString title; + + QString windowTitle; + QskTextOptions windowTitleTextOptions; + + QUrl windowIconSource; + QskGraphic windowIcon; + + bool isWindowIconSourceDirty : 1; }; QskSubWindow::QskSubWindow( QQuickItem* parent ) @@ -45,7 +62,7 @@ void QskSubWindow::setDecorated( bool on ) if ( on == isDecorated() ) return; - const auto subControl = effectiveSubcontrol( QskSubWindow::TitleBar ); + const auto subControl = effectiveSubcontrol( QskSubWindow::Panel ); setFlagHint( subControl | QskAspect::Decoration, on ); resetImplicitSize(); // in case some parent wants to layout @@ -58,23 +75,93 @@ void QskSubWindow::setDecorated( bool on ) bool QskSubWindow::isDecorated() const { - return flagHint< bool >( TitleBar | QskAspect::Decoration, true ); + return flagHint< bool >( Panel | QskAspect::Decoration, true ); } -void QskSubWindow::setTitle( const QString& title ) +void QskSubWindow::setWindowTitle( const QString& title ) { - if ( m_data->title != title ) + if ( m_data->windowTitle != title ) { - m_data->title = title; - Q_EMIT titleChanged(); + m_data->windowTitle = title; + Q_EMIT windowTitleChanged(); } } -QString QskSubWindow::title() const +QString QskSubWindow::windowTitle() const { - return m_data->title; + return m_data->windowTitle; } +void QskSubWindow::setWindowTitleTextOptions( const QskTextOptions& options ) +{ + if ( options != m_data->windowTitleTextOptions ) + { + m_data->windowTitleTextOptions = options; + + update(); + Q_EMIT windowTitleTextOptionsChanged(); + } + +} + +QskTextOptions QskSubWindow::windowTitleTextOptions() const +{ + return m_data->windowTitleTextOptions; +} + +void QskSubWindow::setWindowIconSource( const QUrl& url ) +{ + if ( m_data->windowIconSource == url ) + return; + + m_data->windowIconSource = url; + m_data->windowIcon.reset(); + + m_data->isWindowIconSourceDirty = true; + + polish(); + update(); + + Q_EMIT windowIconSourceChanged(); +} + +QUrl QskSubWindow::windowIconSource() const +{ + return m_data->windowIconSource; +} + +void QskSubWindow::setWindowIcon( const QskGraphic& graphic ) +{ + if ( graphic != m_data->windowIcon ) + { + m_data->windowIcon = graphic; + + if ( !m_data->windowIconSource.isEmpty() ) + { + m_data->windowIconSource = QString(); + m_data->isWindowIconSourceDirty = false; + + Q_EMIT windowIconSourceChanged(); + } + + polish(); + update(); + + Q_EMIT windowIconChanged(); + } +} + +QskGraphic QskSubWindow::windowIcon() const +{ + return m_data->windowIcon; +} + +bool QskSubWindow::hasWindowIcon() const +{ + return !( windowIcon().isEmpty() && windowIconSource().isEmpty() ); +} + + void QskSubWindow::setWindowButtons( WindowButtons buttons ) { if ( buttons != m_data->windowButtons ) @@ -122,6 +209,22 @@ bool QskSubWindow::event( QEvent* event ) return Inherited::event( event ); } +void QskSubWindow::updateLayout() +{ + if ( m_data->isWindowIconSourceDirty ) + { + if ( !m_data->windowIconSource.isEmpty() ) + { + m_data->windowIcon = Qsk::loadGraphic( m_data->windowIconSource ); + Q_EMIT windowIconChanged(); + } + + m_data->isWindowIconSourceDirty = false; + } + + Inherited::updateLayout(); +} + QRectF QskSubWindow::layoutRect() const { QRectF rect = contentsRect(); @@ -135,36 +238,20 @@ QRectF QskSubWindow::layoutRect() const QSizeF QskSubWindow::contentsSizeHint() const { - qreal w = -1; - qreal h = -1; - - const auto children = childItems(); - for ( auto child : children ) - { - if ( qskIsTransparentForPositioner( child ) ) - continue; - - const QskControl* control = qobject_cast< QskControl* >( child ); - if ( control ) - { - const QSizeF sz = control->sizeHint(); - - w = qMax( w, sz.width() ); - h = qMax( h, sz.height() ); - } - } + // the size we get from the children + auto hint = Inherited::contentsSizeHint(); #if 1 // should be Minimum Width/Height from the hints - if ( w < 0 ) - w = qskDpiScaled( 100 ); + if ( hint.width() < 0 ) + hint.setWidth( qskDpiScaled( 100 ) ); - if ( h < 0 ) - h = qskDpiScaled( 80 ); + if ( hint.height() < 0 ) + hint.setHeight( qskDpiScaled( 80 ) ); #endif - QSizeF hint = outerBoxSize( Panel, QSizeF( w, h ) ); - hint.setHeight( hint.height() + titleBarRect().height() ); + hint = outerBoxSize( Panel, hint ); + hint.setHeight( hint.height() + subControlRect( TitleBar ).height() ); return hint; } diff --git a/src/controls/QskSubWindow.h b/src/controls/QskSubWindow.h index 1bcd4f25..9ac6d08d 100644 --- a/src/controls/QskSubWindow.h +++ b/src/controls/QskSubWindow.h @@ -8,6 +8,10 @@ #include "QskPopup.h" +class QskGraphic; +class QskTextOptions; +class QUrl; + class QSK_EXPORT QskSubWindow : public QskPopup { Q_OBJECT @@ -15,8 +19,17 @@ class QSK_EXPORT QskSubWindow : public QskPopup Q_PROPERTY( bool decorated READ isDecorated WRITE setDecorated NOTIFY decoratedChanged ) - Q_PROPERTY( QString title READ title - WRITE setTitle NOTIFY titleChanged ) + Q_PROPERTY( QString windowTitle READ windowTitle + WRITE setWindowTitle NOTIFY windowTitleChanged ) + + Q_PROPERTY( QskTextOptions windowTitleTextOptions READ windowTitleTextOptions + WRITE setWindowTitleTextOptions NOTIFY windowTitleTextOptionsChanged ) + + Q_PROPERTY( QUrl windowIconSource READ windowIconSource + WRITE setWindowIconSource NOTIFY windowIconSourceChanged ) + + Q_PROPERTY( QskGraphic windowIcon READ windowIcon + WRITE setWindowIcon NOTIFY windowIconChanged FINAL ) Q_PROPERTY( WindowButtons windowButtons READ windowButtons WRITE setWindowButtons NOTIFY windowButtonsChanged ) @@ -34,22 +47,33 @@ class QSK_EXPORT QskSubWindow : public QskPopup Q_ENUM( WindowButton ) Q_DECLARE_FLAGS( WindowButtons, WindowButton ) - QSK_SUBCONTROLS( Panel, TitleBar ) + QSK_SUBCONTROLS( Panel, TitleBar, TitleBarSymbol, TitleBarText ) QskSubWindow( QQuickItem* parent = nullptr ); ~QskSubWindow() override; - Q_INVOKABLE void setTitle( const QString& ); - Q_INVOKABLE QString title() const; + void setDecorated( bool ); + bool isDecorated() const; - Q_INVOKABLE void setDecorated( bool ); - Q_INVOKABLE bool isDecorated() const; + void setWindowTitleTextOptions( const QskTextOptions& ); + QskTextOptions windowTitleTextOptions() const; - Q_INVOKABLE void setWindowButtons( WindowButtons ); - Q_INVOKABLE WindowButtons windowButtons() const; + void setWindowTitle( const QString& ); + QString windowTitle() const; - Q_INVOKABLE void setWindowButton( WindowButton, bool on = true ); - Q_INVOKABLE bool testWindowButton( WindowButton ) const; + void setWindowIconSource( const QUrl& ); + QUrl windowIconSource() const; + + void setWindowIcon( const QskGraphic& ); + QskGraphic windowIcon() const; + + bool hasWindowIcon() const; + + void setWindowButtons( WindowButtons ); + WindowButtons windowButtons() const; + + void setWindowButton( WindowButton, bool on = true ); + bool testWindowButton( WindowButton ) const; QRectF titleBarRect() const; @@ -57,13 +81,18 @@ class QSK_EXPORT QskSubWindow : public QskPopup QRectF layoutRect() const override; Q_SIGNALS: - void titleChanged(); void decoratedChanged(); + void windowTitleChanged(); + void windowTitleTextOptionsChanged(); + void windowIconChanged(); + void windowIconSourceChanged(); void windowButtonsChanged(); protected: bool event( QEvent* ) override; + void updateLayout() override; + void itemChange( QQuickItem::ItemChange, const QQuickItem::ItemChangeData& ) override; diff --git a/src/controls/QskSubWindowSkinlet.cpp b/src/controls/QskSubWindowSkinlet.cpp index 66eae373..f574f00f 100644 --- a/src/controls/QskSubWindowSkinlet.cpp +++ b/src/controls/QskSubWindowSkinlet.cpp @@ -8,13 +8,15 @@ #include "QskAspect.h" #include "QskBoxBorderMetrics.h" +#include "QskGraphic.h" +#include "QskTextOptions.h" #include QskSubWindowSkinlet::QskSubWindowSkinlet( QskSkin* skin ) : Inherited( skin ) { - appendNodeRoles( { PanelRole, TitleBarRole } ); + appendNodeRoles( { PanelRole, TitleBarRole, SymbolRole, TitleRole } ); } QskSubWindowSkinlet::~QskSubWindowSkinlet() = default; @@ -24,15 +26,22 @@ QRectF QskSubWindowSkinlet::subControlRect( { const auto subWindow = static_cast< const QskSubWindow* >( skinnable ); - if ( subControl == QskSubWindow::TitleBar ) - { - return titleBarRect( subWindow ); - } - if ( subControl == QskSubWindow::Panel ) { return subWindow->contentsRect(); } + else if ( subControl == QskSubWindow::TitleBar ) + { + return titleBarRect( subWindow ); + } + else if ( subControl == QskSubWindow::TitleBarSymbol ) + { + return symbolRect( subWindow ); + } + else if ( subControl == QskSubWindow::TitleBarText ) + { + return titleRect( subWindow ); + } return Inherited::subControlRect( skinnable, subControl ); } @@ -45,10 +54,36 @@ QSGNode* QskSubWindowSkinlet::updateSubNode( switch ( nodeRole ) { case PanelRole: + { return updateBoxNode( subWindow, node, QskSubWindow::Panel ); - + } case TitleBarRole: - return updateBoxNode( subWindow, node, QskSubWindow::TitleBar ); + { + if ( subWindow->isDecorated() ) + return updateBoxNode( subWindow, node, QskSubWindow::TitleBar ); + + return nullptr; + } + case SymbolRole: + { + if ( subWindow->isDecorated() ) + { + return updateGraphicNode( subWindow, node, + subWindow->windowIcon(), QskSubWindow::TitleBarSymbol ); + } + + return nullptr; + } + case TitleRole: + { + if ( subWindow->isDecorated() ) + { + return updateTextNode( subWindow, node, subWindow->windowTitle(), + subWindow->windowTitleTextOptions(), QskSubWindow::TitleBarText ); + } + + return nullptr; + } } return Inherited::updateSubNode( skinnable, nodeRole, node ); @@ -71,8 +106,8 @@ qreal QskSubWindowSkinlet::titleBarHeight( const QskSubWindow* subWindow ) const if ( !subWindow->isDecorated() ) return 0; - const QFontMetricsF fm( subWindow->effectiveFont( QskSubWindow::TitleBar ) ); const QMarginsF margins = subWindow->marginsHint( QskSubWindow::TitleBar | Padding ); + const QFontMetricsF fm( subWindow->effectiveFont( QskSubWindow::TitleBarText ) ); const qreal height = fm.height() + margins.top() + margins.bottom(); const qreal minHeight = subWindow->metric( QskSubWindow::TitleBar | MinimumHeight ); @@ -80,4 +115,45 @@ qreal QskSubWindowSkinlet::titleBarHeight( const QskSubWindow* subWindow ) const return qMax( height, minHeight ); } +QRectF QskSubWindowSkinlet::symbolRect( const QskSubWindow* subWindow ) const +{ + auto rect = subControlRect( subWindow, QskSubWindow::TitleBar ); + rect = subWindow->innerBox( QskSubWindow::TitleBar, rect ); + + int w = 0; + + if ( !rect.isEmpty() ) + { + const auto symbol = subWindow->windowIcon(); + if ( !symbol.isNull() ) + w = symbol.widthForHeight( rect.height() ); + + rect.setWidth( w ); + } + + return rect; +} + +QRectF QskSubWindowSkinlet::titleRect( const QskSubWindow* subWindow ) const +{ + auto rect = subControlRect( subWindow, QskSubWindow::TitleBar ); + rect = subWindow->innerBox( QskSubWindow::TitleBar, rect ); + + if ( !rect.isEmpty() ) + { + const auto spacing = subWindow->metric( + QskSubWindow::TitleBar | QskAspect::Spacing ); + + const auto symbolRect = subControlRect( subWindow, QskSubWindow::TitleBarSymbol ); + rect.setX( rect.x() + symbolRect.right() + spacing ); + +#if 0 + const QFontMetricsF fm( subWindow->effectiveFont( QskSubWindow::TitleBarText ) ); + rect.setHeight( fm.height() ); // TitleBarText | Alignment +#endif + } + + return rect; +} + #include "moc_QskSubWindowSkinlet.cpp" diff --git a/src/controls/QskSubWindowSkinlet.h b/src/controls/QskSubWindowSkinlet.h index 51099209..302ec27f 100644 --- a/src/controls/QskSubWindowSkinlet.h +++ b/src/controls/QskSubWindowSkinlet.h @@ -20,7 +20,9 @@ class QSK_EXPORT QskSubWindowSkinlet : public QskPopupSkinlet enum NodeRole { PanelRole = QskPopupSkinlet::OverlayRole + 1, - TitleBarRole + TitleBarRole, + SymbolRole, + TitleRole }; Q_INVOKABLE QskSubWindowSkinlet( QskSkin* = nullptr ); @@ -35,7 +37,10 @@ class QSK_EXPORT QskSubWindowSkinlet : public QskPopupSkinlet private: qreal titleBarHeight( const QskSubWindow* ) const; + QRectF titleBarRect( const QskSubWindow* ) const; + QRectF symbolRect( const QskSubWindow* ) const; + QRectF titleRect( const QskSubWindow* ) const; }; #endif diff --git a/src/dialogs/QskDialog.cpp b/src/dialogs/QskDialog.cpp index 891f491d..51e675dc 100644 --- a/src/dialogs/QskDialog.cpp +++ b/src/dialogs/QskDialog.cpp @@ -53,7 +53,7 @@ static void qskSetupSubWindow( QskDialog::StandardButton defaultButton, QskInputSubWindow* subWindow ) { subWindow->setModal( true ); - subWindow->setTitle( title ); + subWindow->setWindowTitle( title ); subWindow->setStandardButtons( buttons ); if ( defaultButton == QskDialog::NoButton )