diff --git a/examples/automotive/main.cpp b/examples/automotive/main.cpp index ed93e224..d4fd90f2 100644 --- a/examples/automotive/main.cpp +++ b/examples/automotive/main.cpp @@ -7,7 +7,6 @@ #include "SkinFactory.h" #include -#include #include #include @@ -33,7 +32,6 @@ int main( int argc, char** argv ) */ qskSetup->setItemUpdateFlag( QskQuickItem::PreferRasterForTextures, true ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); MainWindow window; diff --git a/examples/boxes/main.cpp b/examples/boxes/main.cpp index bfe8678d..95261fc2 100644 --- a/examples/boxes/main.cpp +++ b/examples/boxes/main.cpp @@ -5,7 +5,6 @@ #include "Box.h" -#include #include #include @@ -555,7 +554,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::Quit | SkinnyShortcut::DebugShortcuts ); auto* tabView = new TabView(); diff --git a/examples/buttons/TestButton.qml b/examples/buttons/TestButton.qml index 9d4d5137..65550e1c 100644 --- a/examples/buttons/TestButton.qml +++ b/examples/buttons/TestButton.qml @@ -5,10 +5,17 @@ Qsk.PushButton { sizePolicy { + // long texts, should not have an effect horizontalPolicy: Qsk.SizePolicy.Ignored verticalPolicy: Qsk.SizePolicy.Ignored } + minimumSize + { + width: 80 + height: 60 + } + shape { sizeMode: Qt.RelativeSize diff --git a/examples/buttons/buttons.qml b/examples/buttons/buttons.qml index 442f7729..e8296251 100644 --- a/examples/buttons/buttons.qml +++ b/examples/buttons/buttons.qml @@ -6,12 +6,20 @@ Qsk.Window { id: window visible: true - // visibility: QskWindow.Minimized width: 600 height: 600 color: "Beige" + Component.onCompleted: + { + // very much standard: we should find a better way + + var hint = sizeConstraint(); + setMinimumWidth( hint.width ) + setMinimumHeight( hint.height ) + } + Qsk.Shortcut { sequence : "Ctrl+X" diff --git a/examples/buttons/main.cpp b/examples/buttons/main.cpp index 26ee773f..373e695d 100644 --- a/examples/buttons/main.cpp +++ b/examples/buttons/main.cpp @@ -3,7 +3,6 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#include #include #include @@ -24,7 +23,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); QQmlApplicationEngine engine( QUrl( "qrc:/qml/buttons.qml" ) ); diff --git a/examples/colorswitch/Theme.cpp b/examples/colorswitch/Theme.cpp deleted file mode 100644 index a24a66bc..00000000 --- a/examples/colorswitch/Theme.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the 3-clause BSD License - *****************************************************************************/ - -#include "Theme.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - class SkinTransition final : public QskSkinTransition - { - public: - SkinTransition( const QColor& accent ) - : m_accent( accent ) - { - } - - protected: - void updateSkin( QskSkin*, QskSkin* newSkin ) override - { - newSkin->resetColors( m_accent ); - - /* - The current implementation of the skins is not that good - and we don't have support for customizing them by a minimal set - of attributes. So we do some manual extra work here - */ - - QskSkinHintTableEditor ed( &newSkin->hintTable() ); - - ed.setColor( QskListView::Cell | QskListView::Selected, m_accent.darker( 130 ) ); - ed.setBoxBorderColors( QskFocusIndicator::Panel, m_accent.darker( 150 ) ); - } - - private: - const QColor m_accent; - }; -} - -Theme::Theme( QObject* parent ) - : QObject( parent ) - , m_accent( Qt::blue ) -{ - connect( qskSetup, &QskSetup::skinChanged, - this, [ this ]( QskSkin* ) { updateColors(); } ); - - connect( qskSetup, &QskSetup::skinChanged, - this, [ this ]( QskSkin* ) { Q_EMIT skinChanged(); } ); -} - -void Theme::setAccent( QColor color ) -{ - if ( m_accent != color ) - { - m_accent = color; - updateColors(); - - Q_EMIT accentChanged(); - } -} - -QColor Theme::accent() const -{ - return m_accent; -} - -void Theme::setSkin( const QString& skinName ) -{ - if ( skinName == qskSetup->skinName() ) - return; - - auto oldSkin = qskSetup->skin(); - if ( oldSkin->parent() == qskSetup ) - oldSkin->setParent( nullptr ); // otherwise setSkin deletes it - - auto newSkin = qskSetup->setSkin( skinName ); - - SkinTransition transition( m_accent ); - - transition.setSourceSkin( oldSkin ); - transition.setTargetSkin( newSkin ); - transition.setAnimation( 500 ); - - transition.process(); - - if ( oldSkin->parent() == nullptr ) - delete oldSkin; -} - -QString Theme::skin() const -{ - return qskSetup->skinName(); -} - -void Theme::updateColors() -{ - SkinTransition transition( m_accent ); - - transition.setMask( SkinTransition::Color ); - transition.setSourceSkin( qskSetup->skin() ); - transition.setTargetSkin( qskSetup->skin() ); - transition.setAnimation( 500 ); - - transition.process(); -} - -#include "moc_Theme.cpp" diff --git a/examples/colorswitch/Theme.h b/examples/colorswitch/Theme.h deleted file mode 100644 index a01300f3..00000000 --- a/examples/colorswitch/Theme.h +++ /dev/null @@ -1,35 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the 3-clause BSD License - *****************************************************************************/ - -#pragma once - -#include -#include - -class Theme : public QObject -{ - Q_OBJECT - - Q_PROPERTY( QColor accent READ accent WRITE setAccent NOTIFY accentChanged ) - Q_PROPERTY( QString skin READ skin WRITE setSkin NOTIFY skinChanged ) - - public: - Theme( QObject* parent = nullptr ); - - void setAccent( QColor color ); - QColor accent() const; - - void setSkin( const QString& ); - QString skin() const; - - Q_SIGNALS: - void accentChanged(); - void skinChanged(); - - private: - void updateColors(); - - QColor m_accent; -}; diff --git a/examples/colorswitch/colorswitch.pro b/examples/colorswitch/colorswitch.pro deleted file mode 100644 index e6da7676..00000000 --- a/examples/colorswitch/colorswitch.pro +++ /dev/null @@ -1,11 +0,0 @@ -CONFIG += qskexample qskqmlexport - -RESOURCES += \ - colorswitch.qrc - -HEADERS += \ - Theme.h - -SOURCES += \ - Theme.cpp \ - main.cpp diff --git a/examples/colorswitch/colorswitch.qml b/examples/colorswitch/colorswitch.qml deleted file mode 100644 index 32732703..00000000 --- a/examples/colorswitch/colorswitch.qml +++ /dev/null @@ -1,187 +0,0 @@ -import QtQuick 2.5 -import Skinny 1.0 -import Theme 1.0 // things beyond supersimple are way more convenient with C++ - -Main -{ - id: main - - property var accentColors: [ "red", "lightgreen", "#66336699" ] - - Theme - { - accent: accentColors[ tabBar.currentIndex < 0 ? 0 : tabBar.currentIndex ] - skin: listBox.entries[ listBox.selectedRow ] - } - - Window - { - id: window - - visible: true - color: "Gainsboro" - - width: 600 - height: 600 - - Component.onCompleted: - { - // very much standard: we should find a better way - - var hint = sizeConstraint(); - setMinimumWidth( hint.width ) - setMinimumHeight( hint.height ) - } - - LinearBox - { - orientation: Qt.Horizontal - - spacing: 20 - //margins: 8 // so that we can see the focus frame - margins { left: 8; top: 8; right: 8; bottom: 8 } - - SimpleListBox - { - id: listBox - - preferredWidthFromColumns: true - sizePolicy.horizontalPolicy: SizePolicy.Fixed - - entries: main.skinList - selectedRow: 1 - } - - LinearBox - { - // margins: 10 - margins { left: 10; top: 10; right: 10; bottom: 10 } - orientation: Qt.Vertical - defaultAlignment: Qt.AlignCenter - extraSpacingAt: Qt.BottomEdge - - TabBar - { - id: tabBar - currentIndex: 1 - - Instantiator - { - onObjectAdded: { - tabBar.insertTab( index, object ); - } - - model: accentColors - - delegate: TabButton - { - id: tabButton1 - sizePolicy.horizontalPolicy: SizePolicy.MinimumExpanding - text: modelData - } - } - } - - LinearBox - { - orientation: Qt.Horizontal - dimension: 3 - - spacing: 20 - //margins: 20 - margins { left: 20; top: 20; right: 20; bottom: 20 } - - sizePolicy - { - horizontalPolicy: SizePolicy.Fixed - verticalPolicy: SizePolicy.Fixed - } - - PushButton - { - text: "normal" - focus: true - } - - PushButton - { - text: checked ? "checked" : "unchecked" - checked: true - checkable: true - } - - PushButton - { - text: "disabled" - enabled: false - } - - PushButton - { - text: "flat" - flat: true - } - - PushButton - { - text: ( checked ? "\u2714 " : "" ) + "flat" - flat: true - checkable: true - checked: true - } - - PushButton - { - text: "flat" - flat: true - enabled: false - } - } - - LinearBox - { - spacing: 5 - orientation: Qt.Horizontal - - sizePolicy - { - horizontalPolicy: SizePolicy.MinimumExpanding - verticalPolicy: SizePolicy.Fixed - } - - Slider - { - id: slider - - minimum: 0 - maximum: 100 - value: 42 - - snap: true - stepSize: 1 - } - - TextLabel - { - id: sliderValue - - FontMetrics - { - id: fontMetrics - } - - property rect textRect: fontMetrics.boundingRect("100") - preferredSize: Qt.size( textRect.width, textRect.height ) - sizePolicy: SizePolicy.Fixed - - text: slider.value - } - } - } - } - - FocusIndicator - { - } - } -} diff --git a/examples/colorswitch/colorswitch.qrc b/examples/colorswitch/colorswitch.qrc deleted file mode 100644 index 77f236a9..00000000 --- a/examples/colorswitch/colorswitch.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - colorswitch.qml - - diff --git a/examples/colorswitch/main.cpp b/examples/colorswitch/main.cpp deleted file mode 100644 index 59a1361a..00000000 --- a/examples/colorswitch/main.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the 3-clause BSD License - *****************************************************************************/ - -#include "Theme.h" - -#include -#include - -#include -#include - -#include -#include -#include - -int main( int argc, char* argv[] ) -{ -#ifdef ITEM_STATISTICS - QskObjectCounter counter( true ); -#endif - - qputenv( "QT_IM_MODULE", "skinny" ); - - QskQml::registerTypes(); - qmlRegisterType< Theme >( "Theme", 1, 0, "Theme" ); - - QGuiApplication app( argc, argv ); - - SkinnyFont::init( &app ); - SkinnyShortcut::enable( SkinnyShortcut::Quit | - SkinnyShortcut::ChangeFonts | SkinnyShortcut::DebugShortcuts ); - - QQmlApplicationEngine engine( QUrl( "qrc:/qml/colorswitch.qml" ) ); - - return app.exec(); -} diff --git a/examples/examples.pro b/examples/examples.pro index 0402f44c..a2698bd4 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -32,7 +32,6 @@ qtHaveModule(svg) { SUBDIRS += \ boxes \ buttons \ - colorswitch \ frames \ gbenchmark \ glabels \ diff --git a/examples/gallery/Page.cpp b/examples/gallery/Page.cpp index 0dbbfcf3..078d96b3 100644 --- a/examples/gallery/Page.cpp +++ b/examples/gallery/Page.cpp @@ -4,29 +4,11 @@ *****************************************************************************/ #include "Page.h" -#include Page::Page( Qt::Orientation orientation, QQuickItem* parent ) : QskLinearBox( orientation, parent ) - , m_gradient( QskRgb::GhostWhite ) { setMargins( 20 ); setPadding( 10 ); setSpacing( 10 ); } - -void Page::setGradient( const QskGradient& gradient ) -{ - if ( gradient != m_gradient ) - { - m_gradient = gradient; - - if ( parentItem() && isVisibleToParent() ) - parentItem()->update(); - } -} - -QskGradient Page::gradient() const -{ - return m_gradient; -} diff --git a/examples/gallery/Page.h b/examples/gallery/Page.h index f0404294..f8f8bf83 100644 --- a/examples/gallery/Page.h +++ b/examples/gallery/Page.h @@ -6,16 +6,9 @@ #pragma once #include -#include class Page : public QskLinearBox { public: Page( Qt::Orientation, QQuickItem* parent = nullptr ); - - void setGradient( const QskGradient& ); - QskGradient gradient() const; - - private: - QskGradient m_gradient; }; diff --git a/examples/gallery/button/ButtonPage.cpp b/examples/gallery/button/ButtonPage.cpp new file mode 100644 index 00000000..e6cb217d --- /dev/null +++ b/examples/gallery/button/ButtonPage.cpp @@ -0,0 +1,116 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#include "ButtonPage.h" + +#include +#include +#include +#include +#include + +namespace +{ + class ButtonBox : public QskLinearBox + { + public: + ButtonBox( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Horizontal, 4, parent ) + { + setSpacing( 20 ); + setExtraSpacingAt( Qt::BottomEdge ); + setDefaultAlignment( Qt::AlignCenter ); + + populate(); + } + + private: + void populate() + { + const char* texts[] = { "Press Me", "Check Me" }; + const char* graphics[] = { "diamond/khaki", "ellipse/sandybrown" }; + + for ( int i = 0; i < 6; i++ ) + { + const int index = i % 2; + + auto button = new QskPushButton( this ); + button->setCheckable( index != 0 ); + //button->setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); + + if ( i > 1 ) + { + auto src = QStringLiteral( "image://shapes/" ) + graphics[ index ]; + button->setGraphicSource( src ); + } + + if ( i < 2 || i > 3 ) + { + button->setText( texts[ index ] ); + } + } + } + }; + + class SwitchButtonBox : public QskLinearBox + { + public: + SwitchButtonBox( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Horizontal, parent ) + { + setSpacing( 20 ); + setExtraSpacingAt( Qt::LeftEdge | Qt::RightEdge | Qt::BottomEdge ); + + for ( auto orientation : { Qt::Vertical, Qt::Horizontal } ) + { + (void) new QskSwitchButton( orientation, this ); + + auto button = new QskSwitchButton( orientation, this ); + button->setChecked( true ); + + button = new QskSwitchButton( orientation, this ); + button->setInverted( true ); + + button = new QskSwitchButton( orientation, this ); + button->setInverted( true ); + button->setChecked( true ); + } + } + }; + + class CheckButtonBox : public QskLinearBox + { + public: + CheckButtonBox( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Horizontal, parent ) + { + setSpacing( 20 ); + setExtraSpacingAt( Qt::LeftEdge | Qt::RightEdge | Qt::BottomEdge ); + + for ( auto state : { Qt::Unchecked, Qt::PartiallyChecked, Qt::Checked } ) + { + auto button = new QskCheckBox( this ); + button->setTristate( true ); + button->setCheckState( state ); + } + } + }; +} + +ButtonPage::ButtonPage( QQuickItem* parent ) + : Page( Qt::Vertical, parent ) +{ + setSpacing( 40 ); + populate(); +} + +void ButtonPage::populate() +{ + new ButtonBox( this ); + new QskSeparator( Qt::Horizontal, this ); + new SwitchButtonBox( this ); + new QskSeparator( Qt::Horizontal, this ); + new CheckButtonBox( this ); +} diff --git a/examples/gallery/switchbutton/SwitchButtonPage.h b/examples/gallery/button/ButtonPage.h similarity index 80% rename from examples/gallery/switchbutton/SwitchButtonPage.h rename to examples/gallery/button/ButtonPage.h index 377092da..ad7cad86 100644 --- a/examples/gallery/switchbutton/SwitchButtonPage.h +++ b/examples/gallery/button/ButtonPage.h @@ -7,10 +7,10 @@ #include "Page.h" -class SwitchButtonPage : public Page +class ButtonPage : public Page { public: - SwitchButtonPage( QQuickItem* = nullptr ); + ButtonPage( QQuickItem* = nullptr ); private: void populate(); diff --git a/examples/gallery/gallery.pro b/examples/gallery/gallery.pro index 75cd1071..c21ad9a3 100644 --- a/examples/gallery/gallery.pro +++ b/examples/gallery/gallery.pro @@ -7,15 +7,9 @@ SOURCES += \ label/LabelPage.cpp \ HEADERS += \ - slider/CustomSlider.h \ - slider/CustomSliderSkinlet.h \ - slider/OtherSlider.h \ slider/SliderPage.h SOURCES += \ - slider/CustomSlider.cpp \ - slider/CustomSliderSkinlet.cpp \ - slider/OtherSlider.cpp \ slider/SliderPage.cpp HEADERS += \ @@ -25,10 +19,10 @@ SOURCES += \ progressbar/ProgressBarPage.cpp \ HEADERS += \ - switchbutton/SwitchButtonPage.h + button/ButtonPage.h SOURCES += \ - switchbutton/SwitchButtonPage.cpp \ + button/ButtonPage.cpp \ HEADERS += \ Page.h diff --git a/examples/gallery/label/LabelPage.cpp b/examples/gallery/label/LabelPage.cpp index 84f2c438..7b945ce8 100644 --- a/examples/gallery/label/LabelPage.cpp +++ b/examples/gallery/label/LabelPage.cpp @@ -8,29 +8,51 @@ #include #include #include +#include namespace { + class TextLabel : public QskTextLabel + { + public: + TextLabel( int role, QQuickItem* parent = nullptr ) + : QskTextLabel( parent ) + { + setText( textFromRole( role ) ); + setFontRole( role ); + + setSizePolicy( Qt::Horizontal, QskSizePolicy::Ignored ); + } + + private: + QString textFromRole( int role ) const + { + static QMetaEnum metaEnum; + + if ( !metaEnum.isValid() ) + { + const auto& mo = QskSkin::staticMetaObject; + metaEnum = mo.enumerator( mo.indexOfEnumerator( "SkinFontRole" ) ); + } + + QString s( metaEnum.valueToKey( role ) ); + s.remove( QStringLiteral( "Font" ) ); + + return s; + } + }; + class TextBox : public QskLinearBox { public: TextBox( QQuickItem* parent = nullptr ) - : QskLinearBox( Qt::Vertical, 3, parent ) + : QskLinearBox( Qt::Horizontal, 3, parent ) { setMargins( 10 ); - //setDefaultAlignment( Qt::AlignTop ); - setExtraSpacingAt( Qt::BottomEdge ); + setDefaultAlignment( Qt::AlignCenter ); - const QStringList texts = - { "Default", "Tiny", "Small", "Medium", "Large", "Huge" }; - - for ( int i = 0; i < texts.size(); i++ ) - { - auto label = new QskTextLabel( texts[ i ] + " Font", this ); - - //label->setPanel( true ); - label->setFontRole( i ); - } + for ( int i = 0; i <= QskSkin::HugeFont; i++ ) + ( void ) new TextLabel( i, this ); } }; @@ -67,7 +89,6 @@ namespace LabelPage::LabelPage( QQuickItem* parent ) : Page( Qt::Vertical, parent ) { - setGradient( QskRgb::AliceBlue ); setSpacing( 40 ); (void) new TextBox( this ); diff --git a/examples/gallery/main.cpp b/examples/gallery/main.cpp index 58e52f7a..5a466641 100644 --- a/examples/gallery/main.cpp +++ b/examples/gallery/main.cpp @@ -6,15 +6,19 @@ #include "label/LabelPage.h" #include "progressbar/ProgressBarPage.h" #include "slider/SliderPage.h" -#include "switchbutton/SwitchButtonPage.h" +#include "button/ButtonPage.h" -#include #include #include +#include #include #include #include +#include +#include +#include +#include #include #include @@ -30,27 +34,107 @@ namespace setMargins( 10 ); setTabPosition( Qsk::Left ); setAutoFitTabs( true ); - - connect( this, &QskTabView::currentIndexChanged, - this, &TabView::updateViewPanel ); } - protected: - void aboutToShow() override + void setTabsEnabled( bool on ) { - updateViewPanel(); + for ( int i = 0; i < count(); i++ ) + itemAt( i )->setEnabled( on ); + } + }; + + class MenuButton : public QskPushButton + { + public: + MenuButton( const QString& text, QQuickItem* parent = nullptr ) + : QskPushButton( text, parent ) + { + setFlat( true ); // until we have the section bit in QskAspect + + connect( this, &QskPushButton::pressed, this, &MenuButton::openMenu ); } private: - void updateViewPanel() + void openMenu() { - /* - We should have a better way to set individual colors - for each tab page background - */ + auto menu = new QskMenu( window()->contentItem() ); - if ( auto page = dynamic_cast< const ::Page* >( currentItem() ) ) - setGradientHint( QskTabView::Page, page->gradient() ); + 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", "Help" ); + + menu->setOrigin( geometry().bottomLeft() ); + menu->open(); + } + }; + + /* + Once QskApplicationView and friends are implemented we can replace + Header/ApplicationWindow with it. TODO ... + */ + class Header : public QskLinearBox + { + Q_OBJECT + + public: + Header( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Horizontal, parent ) + { + initSizePolicy( QskSizePolicy::Ignored, QskSizePolicy::Fixed ); + + setMargins( 10 ); + setBackgroundColor( Qt::lightGray ); + + { + auto button = new QskPushButton( "Skin", this ); +#if 1 + button->setFlat( true ); // until we have the section bit in QskAspect +#endif + + // transition leads to errors, when changing the tab before being completed. TODO ... + connect( button, &QskSwitchButton::clicked, + [] { Skinny::changeSkin( 500 ); } ); + } + + { + new MenuButton( "Menu", this ); + } + + addStretch( 10 ); + + { + new QskTextLabel( "Enabled", this ); + + auto button = new QskSwitchButton( this ); + button->setChecked( true ); + + connect( button, &QskSwitchButton::toggled, + this, &Header::enabledToggled ); + } + } + + Q_SIGNALS: + void enabledToggled( bool ); + }; + + class ApplicationView : public QskLinearBox + { + public: + ApplicationView( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Vertical, parent ) + { + auto header = new Header( this ); + + auto tabView = new TabView( this ); + tabView->addTab( "Labels", new LabelPage() ); + tabView->addTab( "Buttons", new ButtonPage() ); + tabView->addTab( "Sliders", new SliderPage() ); + tabView->addTab( "Progress\nBars", new ProgressBarPage() ); + + connect( header, &Header::enabledToggled, + tabView, &TabView::setTabsEnabled ); } }; } @@ -65,21 +149,15 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); - auto tabView = new TabView(); - - tabView->addTab( "Labels", new LabelPage() ); - tabView->addTab( "Sliders", new SliderPage() ); - tabView->addTab( "Progress\nBars", new ProgressBarPage() ); - tabView->addTab( "Switches", new SwitchButtonPage() ); + auto mainView = new ApplicationView(); QSize size( 800, 600 ); - size = size.expandedTo( tabView->sizeHint().toSize() ); + size = size.expandedTo( mainView->sizeHint().toSize() ); QskWindow window; - window.addItem( tabView ); + window.addItem( mainView ); window.addItem( new QskFocusIndicator() ); window.resize( size ); @@ -87,3 +165,5 @@ int main( int argc, char* argv[] ) return app.exec(); } + +#include "main.moc" diff --git a/examples/gallery/progressbar/ProgressBarPage.cpp b/examples/gallery/progressbar/ProgressBarPage.cpp index 75fad19e..18985ecf 100644 --- a/examples/gallery/progressbar/ProgressBarPage.cpp +++ b/examples/gallery/progressbar/ProgressBarPage.cpp @@ -45,9 +45,7 @@ namespace ProgressBarPage::ProgressBarPage( QQuickItem* parent ) : Page( Qt::Horizontal, parent ) { - setGradient( QskRgb::AliceBlue ); setSpacing( 40 ); - populate(); } diff --git a/examples/gallery/slider/SliderPage.cpp b/examples/gallery/slider/SliderPage.cpp index 4de7c647..733692d6 100644 --- a/examples/gallery/slider/SliderPage.cpp +++ b/examples/gallery/slider/SliderPage.cpp @@ -4,17 +4,28 @@ *****************************************************************************/ #include "SliderPage.h" -#include "CustomSlider.h" -#include "OtherSlider.h" +#include -#include -#include - -SliderPage::SliderPage( QQuickItem* parentItem ) - : Page( Qt::Vertical, parentItem ) +namespace { - setGradient( QskRgb::PeachPuff ); + class Slider : public QskSlider + { + public: + Slider( Qt::Orientation orientation, QQuickItem* parent = nullptr ) + : QskSlider( orientation, parent ) + { + setBoundaries( 0, 1000 ); + setPageSize( 10 ); + setStepSize( 10 ); + setSnap( true ); + } + }; +} + +SliderPage::SliderPage( QQuickItem* parent ) + : Page( Qt::Horizontal, parent ) +{ setMargins( 10 ); setSpacing( 20 ); @@ -37,54 +48,6 @@ SliderPage::SliderPage( QQuickItem* parentItem ) void SliderPage::populate() { - { - auto slider = new QskSlider( this ); - - slider->setMinimum( 0 ); - slider->setMaximum( 1000 ); - slider->setPageSize( 10 ); - slider->setStepSize( 10 ); - slider->setSnap( true ); - } - - { - auto slider = new OtherSlider( this ); - - slider->setMinimum( 0 ); - slider->setMaximum( 10 ); - slider->setStepSize( 1 ); - } - - - auto hBox = new QskLinearBox( Qt::Horizontal, this ); - - { - auto slider = new QskSlider( Qt::Vertical, hBox ); - - slider->setMinimum( 0 ); - slider->setMaximum( 1000 ); - slider->setPageSize( 10 ); - slider->setStepSize( 10 ); - slider->setSnap( true ); - } - - { - auto slider = new OtherSlider( hBox ); - slider->setOrientation( Qt::Vertical ); - - slider->setMinimum( 0 ); - slider->setMaximum( 10 ); - slider->setStepSize( 1 ); - } - - { - auto slider = new CustomSlider( this ); - - slider->setMargins( QskMargins( 0, 15 ) ); - slider->setSnap( true ); - slider->setMinimum( 0 ); - slider->setMaximum( 2000 ); - slider->setStepSize( 10 ); - slider->setPageSize( 10 ); - } + ( void ) new Slider( Qt::Horizontal, this ); + ( void ) new Slider( Qt::Vertical, this ); } diff --git a/examples/gallery/switchbutton/SwitchButtonPage.cpp b/examples/gallery/switchbutton/SwitchButtonPage.cpp deleted file mode 100644 index 169dac95..00000000 --- a/examples/gallery/switchbutton/SwitchButtonPage.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the 3-clause BSD License - *****************************************************************************/ - -#include "SwitchButtonPage.h" - -#include -#include -#include -#include - -#include - -SwitchButtonPage::SwitchButtonPage( QQuickItem* parent ) - : Page( Qt::Horizontal, parent ) -{ - setGradient( QskRgb::AliceBlue ); - setSpacing( 40 ); - - populate(); -} - -void SwitchButtonPage::populate() -{ - auto hbox1 = new QskLinearBox(); - hbox1->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed ); - hbox1->setExtraSpacingAt( Qt::LeftEdge ); - - auto label = new QskTextLabel( "Disable the switches:", hbox1 ); - label->setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - - auto button0 = new QskSwitchButton( hbox1 ); - - auto hbox2 = new QskLinearBox( Qt::Horizontal ); - hbox2->setDefaultAlignment( Qt::AlignHCenter | Qt::AlignTop ); - hbox2->setMargins( 30 ); - - (void) new QskSwitchButton( Qt::Vertical, hbox2 ); - (void) new QskSwitchButton( Qt::Horizontal, hbox2 ); - - auto button3 = new QskSwitchButton( Qt::Vertical, hbox2 ); - button3->setInverted( true ); - - auto button4 = new QskSwitchButton( Qt::Horizontal, hbox2 ); - button4->setInverted( true ); - - auto vbox = new QskLinearBox( Qt::Vertical, this ); - vbox->addItem( hbox1 ); - vbox->addItem( new QskSeparator() ); - vbox->addItem( hbox2 ); - vbox->setExtraSpacingAt( Qt::BottomEdge ); - - QObject::connect( button0, &QskSwitchButton::checkedChanged, - hbox2, &QskQuickItem::setDisabled ); -} diff --git a/examples/iotdashboard/main.cpp b/examples/iotdashboard/main.cpp index 80be72ba..a1457e0d 100644 --- a/examples/iotdashboard/main.cpp +++ b/examples/iotdashboard/main.cpp @@ -7,7 +7,6 @@ #include "GraphicProvider.h" #include "Skin.h" -#include #include #include @@ -61,8 +60,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); - Qsk::addGraphicProvider( QString(), new GraphicProvider() ); // disable default skins diff --git a/examples/layouts/main.cpp b/examples/layouts/main.cpp index f6d59be6..6c829fd5 100644 --- a/examples/layouts/main.cpp +++ b/examples/layouts/main.cpp @@ -10,7 +10,6 @@ #include "StackLayoutPage.h" #include "TestRectangle.h" -#include #include #include @@ -31,7 +30,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); auto tabView = new QskTabView(); diff --git a/examples/listbox/main.cpp b/examples/listbox/main.cpp index 487796aa..2c1e2099 100644 --- a/examples/listbox/main.cpp +++ b/examples/listbox/main.cpp @@ -3,7 +3,6 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#include #include #include @@ -65,7 +64,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); QskWindow window; diff --git a/examples/messagebox/main.cpp b/examples/messagebox/main.cpp index f5702319..f1b96068 100644 --- a/examples/messagebox/main.cpp +++ b/examples/messagebox/main.cpp @@ -3,7 +3,6 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#include #include #include @@ -126,7 +125,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); qskDialog->setPolicy( QskDialog::EmbeddedBox ); diff --git a/examples/messageboxQml/main.cpp b/examples/messageboxQml/main.cpp index 58d084a6..3e7a0694 100644 --- a/examples/messageboxQml/main.cpp +++ b/examples/messageboxQml/main.cpp @@ -3,7 +3,6 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#include #include #include @@ -22,7 +21,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); QQmlApplicationEngine engine( QUrl( "qrc:/qml/messagebox.qml" ) ); diff --git a/examples/mycontrols/main.cpp b/examples/mycontrols/main.cpp index 996bcd02..7102c1a7 100644 --- a/examples/mycontrols/main.cpp +++ b/examples/mycontrols/main.cpp @@ -6,7 +6,6 @@ #include "MySkin.h" #include "MyToggleButton.h" -#include #include #include @@ -136,7 +135,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::DebugBackground | SkinnyShortcut::DebugStatistics | SkinnyShortcut::Quit ); diff --git a/examples/qvgviewer/MainWindow.cpp b/examples/qvgviewer/MainWindow.cpp index e78a1a79..bc379584 100644 --- a/examples/qvgviewer/MainWindow.cpp +++ b/examples/qvgviewer/MainWindow.cpp @@ -20,11 +20,6 @@ #include #include -#ifdef CONTEXT_MENU - #include - #include -#endif - #include #include @@ -46,10 +41,6 @@ class GraphicLabel : public QskGraphicLabel setBoxShapeHint( Panel, 8 ); setAlignment( Qt::AlignCenter ); setDarknessMode( false ); - -#ifdef CONTEXT_MENU - setAcceptedMouseButtons( Qt::LeftButton ); -#endif } void setDarknessMode( bool on ) @@ -80,27 +71,6 @@ class GraphicLabel : public QskGraphicLabel startTransition( QskGraphicLabel::Graphic | QskAspect::GraphicRole, duration, oldRole, graphicRole() ); } - -#ifdef CONTEXT_MENU - protected: - void mousePressEvent( QMouseEvent* event ) override - { - QskMenu menu( this ); - menu.setPopupFlag( QskPopup::DeleteOnClose, false ); - - 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", "Help" ); - - menu.setOrigin( qskMousePosition( event ) ); - - const int result = menu.exec(); - if ( result >= 0 ) - qDebug() << "Selected:" << result; - } -#endif }; MainWindow::MainWindow() diff --git a/examples/qvgviewer/main.cpp b/examples/qvgviewer/main.cpp index c6a53784..458709e1 100644 --- a/examples/qvgviewer/main.cpp +++ b/examples/qvgviewer/main.cpp @@ -5,13 +5,8 @@ #include "MainWindow.h" -#include #include -#ifdef CONTEXT_MENU - #include -#endif - #include #include @@ -23,13 +18,8 @@ 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 ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); QskFocusIndicator* focusIndicator = new QskFocusIndicator(); diff --git a/examples/qvgviewer/qvgviewer.pro b/examples/qvgviewer/qvgviewer.pro index ece321d1..d2c05ec5 100644 --- a/examples/qvgviewer/qvgviewer.pro +++ b/examples/qvgviewer/qvgviewer.pro @@ -1,5 +1,4 @@ CONFIG += qskexample -DEFINES += CONTEXT_MENU HEADERS += \ MainWindow.h diff --git a/examples/gallery/slider/CustomSlider.cpp b/examples/tabview/CustomSlider.cpp similarity index 100% rename from examples/gallery/slider/CustomSlider.cpp rename to examples/tabview/CustomSlider.cpp diff --git a/examples/gallery/slider/CustomSlider.h b/examples/tabview/CustomSlider.h similarity index 100% rename from examples/gallery/slider/CustomSlider.h rename to examples/tabview/CustomSlider.h diff --git a/examples/gallery/slider/CustomSliderSkinlet.cpp b/examples/tabview/CustomSliderSkinlet.cpp similarity index 100% rename from examples/gallery/slider/CustomSliderSkinlet.cpp rename to examples/tabview/CustomSliderSkinlet.cpp diff --git a/examples/gallery/slider/CustomSliderSkinlet.h b/examples/tabview/CustomSliderSkinlet.h similarity index 100% rename from examples/gallery/slider/CustomSliderSkinlet.h rename to examples/tabview/CustomSliderSkinlet.h diff --git a/examples/gallery/slider/OtherSlider.cpp b/examples/tabview/OtherSlider.cpp similarity index 100% rename from examples/gallery/slider/OtherSlider.cpp rename to examples/tabview/OtherSlider.cpp diff --git a/examples/gallery/slider/OtherSlider.h b/examples/tabview/OtherSlider.h similarity index 100% rename from examples/gallery/slider/OtherSlider.h rename to examples/tabview/OtherSlider.h diff --git a/examples/tabview/main.cpp b/examples/tabview/main.cpp index 7afab28f..9c8bbda2 100644 --- a/examples/tabview/main.cpp +++ b/examples/tabview/main.cpp @@ -3,7 +3,9 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#include +#include "CustomSlider.h" +#include "OtherSlider.h" + #include #include @@ -34,6 +36,36 @@ class Label : public QskTextLabel } }; +class SliderBox : public QskLinearBox +{ + public: + SliderBox( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Vertical, parent ) + { + setMargins( 30 ); + setSpacing( 50 ); + setExtraSpacingAt( Qt::BottomEdge ); + + { + auto slider = new OtherSlider( this ); + + slider->setMinimum( 0 ); + slider->setMaximum( 10 ); + slider->setStepSize( 1 ); + } + + { + auto slider = new CustomSlider( this ); + + slider->setSnap( true ); + slider->setMinimum( 0 ); + slider->setMaximum( 2000 ); + slider->setStepSize( 10 ); + slider->setPageSize( 10 ); + } + } +}; + class TabView : public QskTabView { public: @@ -44,11 +76,20 @@ class TabView : public QskTabView { QString text; if ( i == 4 ) - text = QString( "Another Tab" ); + { + const auto text = QStringLiteral( "Another Tab" ); + addTab( text, new Label( text ) ); + } + else if ( i == 7 ) + { + addTab( "Sliders", new SliderBox() ); + } else - text = QString( "Tab %1" ).arg( i + 1 ); + { + const auto text = QString( "Tab %1" ).arg( i + 1 ); + addTab( text, new Label( text ) ); + } - addTab( text, new Label( text ) ); } buttonAt( 2 )->setEnabled( false ); @@ -90,7 +131,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); auto tabView = new TabView(); diff --git a/examples/tabview/tabview.pro b/examples/tabview/tabview.pro index 0a7f08c9..dbef219c 100644 --- a/examples/tabview/tabview.pro +++ b/examples/tabview/tabview.pro @@ -1,4 +1,12 @@ CONFIG += qskexample +HEADERS += \ + CustomSlider.h \ + CustomSliderSkinlet.h \ + OtherSlider.h \ + SOURCES += \ + CustomSlider.cpp \ + CustomSliderSkinlet.cpp \ + OtherSlider.cpp \ main.cpp diff --git a/playground/anchors/kiwi/LICENSE-Kiwi b/playground/anchors/kiwi/LICENSE-Kiwi new file mode 100644 index 00000000..c34aff71 --- /dev/null +++ b/playground/anchors/kiwi/LICENSE-Kiwi @@ -0,0 +1,71 @@ +========================= + The Kiwi licensing terms +========================= +Kiwi is licensed under the terms of the Modified BSD License (also known as +New or Revised BSD), as follows: + +Copyright (c) 2013, Nucleic Development Team + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the name of the Nucleic Development Team nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +About Kiwi +---------- +Chris Colbert began the Kiwi project in December 2013 in an effort to +create a blisteringly fast UI constraint solver. Chris is still the +project lead. + +The Nucleic Development Team is the set of all contributors to the Nucleic +project and its subprojects. + +The core team that coordinates development on GitHub can be found here: +http://github.com/nucleic. The current team consists of: + +* Chris Colbert + +Our Copyright Policy +-------------------- +Nucleic uses a shared copyright model. Each contributor maintains copyright +over their contributions to Nucleic. But, it is important to note that these +contributions are typically only changes to the repositories. Thus, the Nucleic +source code, in its entirety is not the copyright of any single person or +institution. Instead, it is the collective copyright of the entire Nucleic +Development Team. If individual contributors want to maintain a record of what +changes/contributions they have specific copyright on, they should indicate +their copyright in the commit message of the change, when they commit the +change to one of the Nucleic repositories. + +With this in mind, the following banner should be used in any source code file +to indicate the copyright and license terms: + +#------------------------------------------------------------------------------ +# Copyright (c) 2013, Nucleic Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file LICENSE, distributed with this software. +#------------------------------------------------------------------------------ diff --git a/playground/anchors/kiwi/README-Kiwi b/playground/anchors/kiwi/README-Kiwi new file mode 100644 index 00000000..a06b5488 --- /dev/null +++ b/playground/anchors/kiwi/README-Kiwi @@ -0,0 +1,17 @@ +This code is a stripped down version of the implementation of the Cassowary constraint +solving algorithm of the Kiwi project: + + https://github.com/nucleic/kiwi + +All credits go to the Nucleic Development Team ! + +For the QSkinny use case the following adjustments have been made: + + - implementations moved from *.h to *.cpp files + + - replacing AssocVector from the Loki Library by yet another stupid + implementation of a "flat map" + +I forgot what version of Kiwi had been used - a migration of the code +for a more recent official version will happen soon. + diff --git a/playground/anchors/main.cpp b/playground/anchors/main.cpp index 7f758ec6..a75c279d 100644 --- a/playground/anchors/main.cpp +++ b/playground/anchors/main.cpp @@ -5,7 +5,6 @@ #include "AnchorBox.h" -#include #include #include @@ -153,7 +152,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::Quit | SkinnyShortcut::DebugShortcuts ); auto box = new MyBox(); diff --git a/playground/dialogbuttons/main.cpp b/playground/dialogbuttons/main.cpp index 7393099d..b55461f9 100644 --- a/playground/dialogbuttons/main.cpp +++ b/playground/dialogbuttons/main.cpp @@ -5,7 +5,6 @@ #include "Window.h" -#include #include #include @@ -21,7 +20,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); Window window( Qt::Horizontal ); diff --git a/playground/grids/main.cpp b/playground/grids/main.cpp index 40dd8a8a..ea81b96a 100644 --- a/playground/grids/main.cpp +++ b/playground/grids/main.cpp @@ -5,8 +5,6 @@ #include "TestBox.h" -#include - #include #include @@ -389,15 +387,6 @@ int main( int argc, char** argv ) { QApplication app( argc, argv ); -#if 1 - /* - we don't need the fonts, but by calling something from - the support library initializations regarding the skins - are loaded. TODO ... - */ - SkinnyFont::init( &app ); -#endif - int testcase = 0; if ( argc == 2 ) testcase = atoi( argv[1] ); diff --git a/playground/images/images.qml b/playground/images/images.qml index 1ee7fb4f..b5132ff5 100644 --- a/playground/images/images.qml +++ b/playground/images/images.qml @@ -17,7 +17,8 @@ Qsk.Window orientation: Qt.Horizontal dimension: 3 - margins: 10 + //margins: 10 // only possible with Qt <= 6.1 + margins { left: 10; top: 10; right: 10; bottom: 10 } spacing: 10 Repeater diff --git a/playground/inputpanel/main.cpp b/playground/inputpanel/main.cpp index d26e3361..af6994cd 100644 --- a/playground/inputpanel/main.cpp +++ b/playground/inputpanel/main.cpp @@ -3,7 +3,6 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#include #include #include @@ -285,7 +284,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); #if 1 diff --git a/playground/webview/main.cpp b/playground/webview/main.cpp index 0a6de6ec..454e3639 100644 --- a/playground/webview/main.cpp +++ b/playground/webview/main.cpp @@ -3,7 +3,6 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#include #include #include @@ -43,7 +42,6 @@ int main( int argc, char* argv[] ) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app( argc, argv ); - SkinnyFont::init( &app ); SkinnyShortcut::enable( SkinnyShortcut::Quit | SkinnyShortcut::DebugShortcuts ); QskWindow window; diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index a096179e..03b170ba 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -8,11 +8,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -112,12 +114,14 @@ namespace void setupControl(); void setupBox(); + void setupCheckBox(); void setupDialogButtonBox(); void setupDialogButton(); void setupFocusIndicator(); void setupInputPanel(); void setupVirtualKeyboard(); void setupListView(); + void setupMenu(); void setupPageIndicator(); void setupPopup(); void setupProgressBar(); @@ -142,12 +146,14 @@ void Editor::setup() setupControl(); setupBox(); + setupCheckBox(); setupDialogButtonBox(); setupDialogButton(); setupFocusIndicator(); setupInputPanel(); setupVirtualKeyboard(); setupListView(); + setupMenu(); setupPageIndicator(); setupPopup(); setupProgressBar(); @@ -177,6 +183,27 @@ void Editor::setupControl() qskShadedColor( m_pal.textColor, 0.6 ) ); } +void Editor::setupCheckBox() +{ + using A = QskAspect; + using Q = QskCheckBox; + + const qreal size = qskDpiScaled( 18 ); + + setStrutSize( Q::Panel, size, size ); + setPadding( Q::Panel, 3 ); + + setBoxShape( Q::Panel, 2 ); + + setGradient( Q::Panel, m_pal.baseColor); + setGradient( Q::Panel | Q::Checked, m_pal.accentColor ); + setGradient( Q::Panel | Q::Checked | Q::Disabled, QskRgb::Grey ); + + setColor( Q::Indicator, m_pal.contrastColor ); + + setAnimation( Q::Panel | A::Color, qskDuration ); +} + void Editor::setupBox() { using Q = QskBox; @@ -199,6 +226,45 @@ void Editor::setupPopup() setGradient( Q::Overlay, gradient ); } +void Editor::setupMenu() +{ + using A = QskAspect; + using Q = QskMenu; + + setBoxShape( Q::Panel, qskDpiScaled( 4 ) ); + setBoxBorderMetrics( Q::Panel, qskDpiScaled( 1 ) ); + setBoxBorderColors( Q::Panel, m_pal.darker125 ); + + setGradient( Q::Panel, m_pal.baseColor ); + + const bool isCascading = qskMaybeDesktopPlatform(); + setFlagHint( Q::Panel | A::Style, isCascading ); + +#if 0 + setPadding( Q::Separator, QMarginsF( 10, 0, 10, 0 ) ); +#endif + setMetric( Q::Separator | A::Size, qskDpiScaled( 1 ) ); + setBoxShape( Q::Separator, 0 ); + setBoxBorderMetrics( Q::Separator, 0 ); + setGradient( Q::Separator, m_pal.darker125 ); + + setPadding( Q::Cell, QskMargins( 2, 10, 2, 10 ) ); + setSpacing( Q::Cell, 5 ); + setGradient( Q::Cell, Qt::transparent ); + + setGradient( Q::Cursor, m_pal.accentColor ); + + setColor( Q::Text, m_pal.textColor ); + setColor( Q::Text | Q::Selected, m_pal.contrastColor ); + setFontRole( Q::Text, QskSkin::SmallFont ); + + setPosition( Q::Panel, 0 ); + setPosition( Q::Panel | QskPopup::Closed, 1 ); + + setAnimation( Q::Panel | A::Metric, 150 ); + setAnimation( Q::Cursor | A::Position | A::Metric, 75, QEasingCurve::OutCubic ); +} + void Editor::setupTextLabel() { using Q = QskTextLabel; diff --git a/skins/material/QskMaterialSkinFactory.cpp b/skins/material/QskMaterialSkinFactory.cpp index 1b68a4aa..40683a6a 100644 --- a/skins/material/QskMaterialSkinFactory.cpp +++ b/skins/material/QskMaterialSkinFactory.cpp @@ -24,7 +24,7 @@ QStringList QskMaterialSkinFactory::skinNames() const QskSkin* QskMaterialSkinFactory::createSkin( const QString& skinName ) { - if ( skinName.toLower() == materialSkinName ) + if ( QString::compare( skinName, materialSkinName, Qt::CaseInsensitive ) == 0 ) return new QskMaterialSkin(); return nullptr; diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index f7031ae1..fd45d94a 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -132,6 +133,7 @@ namespace void setupControl(); void setupBox(); + void setupCheckBox(); void setupDialogButton(); void setupDialogButtonBox(); void setupFocusIndicator(); @@ -251,6 +253,7 @@ void Editor::setup() setupControl(); setupBox(); + setupCheckBox(); setupDialogButtonBox(); setupDialogButton(); setupFocusIndicator(); @@ -292,6 +295,28 @@ void Editor::setupBox() setPanel( QskBox::Panel, Plain ); } +void Editor::setupCheckBox() +{ + using A = QskAspect; + using Q = QskCheckBox; + + const qreal size = qskDpiScaled( 26 ); + + setStrutSize( Q::Panel, size, size ); + + setPadding( Q::Panel, qskDpiScaled( 5 ) ); + setBoxShape( Q::Panel, qskDpiScaled( 3 ) ); + setBoxBorderMetrics( Q::Panel, qskDpiScaled( 1 ) ); + + setBoxBorderColors( Q::Panel, m_pal.darker125 ); + setGradient( Q::Panel, m_pal.lighter135 ); + setGradient( Q::Panel | Q::Checked, m_pal.highlighted ); + + setColor( Q::Indicator, m_pal.lighter135 ); + + setAnimation( Q::Panel | A::Color, qskDuration ); +} + void Editor::setupPopup() { using A = QskAspect; @@ -306,11 +331,11 @@ void Editor::setupMenu() using A = QskAspect; using Q = QskMenu; - const QColor c1( 78, 158, 38 ); - const QColor c2( 15, 103, 43 ); + setBoxShape( Q::Panel, qskDpiScaled( 4 ) ); + setBoxBorderMetrics( Q::Panel, qskDpiScaled( 1 ) ); + setBoxBorderColors( Q::Panel, m_pal.darker125 ); - setBoxShape( Q::Panel, 4 ); - setVGradient( Q::Panel, c1, c2 ); + setGradient( Q::Panel, m_pal.lighter110 ); const bool isCascading = qskMaybeDesktopPlatform(); setFlagHint( Q::Panel | A::Style, isCascading ); @@ -318,16 +343,17 @@ void Editor::setupMenu() #if 0 setPadding( Q::Separator, QMarginsF( 10, 0, 10, 0 ) ); #endif - setMetric( Q::Separator | A::Size, 2 ); + setMetric( Q::Separator | A::Size, qskDpiScaled( 2 ) ); setSeparator( Q::Separator | A::Horizontal ); setPadding( Q::Cell, QskMargins( 2, 10, 2, 10 ) ); setSpacing( Q::Cell, 5 ); setGradient( Q::Cell, Qt::transparent ); - setHGradient( Q::Cursor, c2, c2.lighter( 2 ) ); + setGradient( Q::Cursor, m_pal.highlighted ); - setColor( Q::Text, QColor( 255, 255, 255 ) ); + setColor( Q::Text, m_pal.contrastedText ); + setColor( Q::Text | Q::Selected, m_pal.highlightedText ); setFontRole( Q::Text, QskSkin::SmallFont ); setPosition( Q::Panel, 0 ); @@ -892,7 +918,7 @@ void Editor::setupSwitchButton() using A = QskAspect; using Q = QskSwitchButton; - const qreal radius = qskDpiScaled( 18 ); + const qreal radius = qskDpiScaled( 12 ); const qreal handleSize = 2 * ( radius - 2 ); setBoxShape( Q::Groove, 100, Qt::RelativeSize ); diff --git a/skins/squiek/QskSquiekSkinFactory.cpp b/skins/squiek/QskSquiekSkinFactory.cpp index 16c27524..39074d24 100644 --- a/skins/squiek/QskSquiekSkinFactory.cpp +++ b/skins/squiek/QskSquiekSkinFactory.cpp @@ -24,7 +24,7 @@ QStringList QskSquiekSkinFactory::skinNames() const QskSkin* QskSquiekSkinFactory::createSkin( const QString& skinName ) { - if ( skinName.toLower() == squiekSkinName ) + if ( QString::compare( skinName, squiekSkinName, Qt::CaseInsensitive ) == 0 ) return new QskSquiekSkin(); return nullptr; diff --git a/src/common/QskArcMetrics.cpp b/src/common/QskArcMetrics.cpp index 991b075d..44d90fde 100644 --- a/src/common/QskArcMetrics.cpp +++ b/src/common/QskArcMetrics.cpp @@ -11,6 +11,10 @@ static void qskRegisterArcMetrics() { qRegisterMetaType< QskArcMetrics >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskArcMetrics >(); +#endif } Q_CONSTRUCTOR_FUNCTION( qskRegisterArcMetrics ) diff --git a/src/common/QskAspect.cpp b/src/common/QskAspect.cpp index a45febbb..6162df0e 100644 --- a/src/common/QskAspect.cpp +++ b/src/common/QskAspect.cpp @@ -144,7 +144,7 @@ QVector< QskAspect::Subcontrol > QskAspect::subControls( const QMetaObject* meta static QByteArray qskEnumString( const char* name, int value ) { - const QMetaObject& mo = QskAspect::staticMetaObject; + const auto& mo = QskAspect::staticMetaObject; const QMetaEnum metaEnum = mo.enumerator( mo.indexOfEnumerator( name ) ); const char* key = metaEnum.valueToKey( value ); diff --git a/src/common/QskAspect.h b/src/common/QskAspect.h index 94e5bd42..4b5f03bc 100644 --- a/src/common/QskAspect.h +++ b/src/common/QskAspect.h @@ -109,8 +109,18 @@ class QSK_EXPORT QskAspect constexpr QskAspect operator|( Type ) const noexcept; constexpr QskAspect operator|( Primitive ) const noexcept; constexpr QskAspect operator|( Placement ) const noexcept; + constexpr QskAspect operator|( State ) const noexcept; + QskAspect& operator|=( State ) noexcept; + + constexpr QskAspect operator&( State ) const noexcept; + QskAspect& operator&=( State ) noexcept; + constexpr QskAspect operator|( States ) const noexcept; + QskAspect& operator|=( States ) noexcept; + + constexpr QskAspect operator&( States ) const noexcept; + QskAspect& operator&=( States ) noexcept; constexpr QskAspect stateless() const noexcept; constexpr QskAspect trunk() const noexcept; @@ -283,12 +293,48 @@ inline constexpr QskAspect QskAspect::operator|( State state ) const noexcept m_bits.primitive, m_bits.placement, m_bits.states | state ); } +inline QskAspect& QskAspect::operator|=( State state ) noexcept +{ + m_bits.states |= state; + return *this; +} + +inline constexpr QskAspect QskAspect::operator&( State state ) const noexcept +{ + return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator, + m_bits.primitive, m_bits.placement, m_bits.states & state ); +} + +inline QskAspect& QskAspect::operator&=( State state ) noexcept +{ + m_bits.states &= state; + return *this; +} + inline constexpr QskAspect QskAspect::operator|( States states ) const noexcept { return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator, m_bits.primitive, m_bits.placement, m_bits.states | states ); } +inline QskAspect& QskAspect::operator|=( States states ) noexcept +{ + m_bits.states |= states; + return *this; +} + +inline constexpr QskAspect QskAspect::operator&( States states ) const noexcept +{ + return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator, + m_bits.primitive, m_bits.placement, m_bits.states & states ); +} + +inline QskAspect& QskAspect::operator&=( States states ) noexcept +{ + m_bits.states &= states; + return *this; +} + inline constexpr QskAspect QskAspect::stateless() const noexcept { return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator, @@ -538,6 +584,11 @@ namespace std }; } +inline QskHashValue qHash( const QskAspect aspect, QskHashValue seed = 0 ) noexcept +{ + return qHash( aspect.value(), seed ); +} + #ifndef QT_NO_DEBUG_STREAM class QDebug; diff --git a/src/common/QskBoxBorderColors.cpp b/src/common/QskBoxBorderColors.cpp index 054696dd..80493b20 100644 --- a/src/common/QskBoxBorderColors.cpp +++ b/src/common/QskBoxBorderColors.cpp @@ -13,6 +13,10 @@ static void qskRegisterBoxBorderColors() { qRegisterMetaType< QskBoxBorderColors >(); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskBoxBorderColors >(); +#endif + QMetaType::registerConverter< QColor, QskBoxBorderColors >( []( const QColor& color ) { return QskBoxBorderColors( color ); } ); @@ -110,6 +114,26 @@ void QskBoxBorderColors::setGradientAt( Qt::Edges edges, const QskGradient& grad m_gradients[ Qsk::Bottom ] = gradient; } +void QskBoxBorderColors::setLeft( const QskGradient& gradient ) +{ + m_gradients[ Qsk::Left ] = gradient; +} + +void QskBoxBorderColors::setTop( const QskGradient& gradient ) +{ + m_gradients[ Qsk::Top ] = gradient; +} + +void QskBoxBorderColors::setRight( const QskGradient& gradient ) +{ + m_gradients[ Qsk::Right ] = gradient; +} + +void QskBoxBorderColors::setBottom( const QskGradient& gradient ) +{ + m_gradients[ Qsk::Bottom ] = gradient; +} + const QskGradient& QskBoxBorderColors::gradientAt( Qt::Edge edge ) const { switch ( edge ) @@ -165,6 +189,14 @@ bool QskBoxBorderColors::isMonochrome() const && m_gradients[ 3 ].isMonochrome(); } +bool QskBoxBorderColors::isValid() const +{ + return m_gradients[ 0 ].isValid() + || m_gradients[ 1 ].isValid() + || m_gradients[ 2 ].isValid() + || m_gradients[ 3 ].isValid(); +} + QskBoxBorderColors QskBoxBorderColors::interpolated( const QskBoxBorderColors& to, qreal ratio ) const { @@ -172,8 +204,14 @@ QskBoxBorderColors QskBoxBorderColors::interpolated( for ( size_t i = 0; i < 4; i++ ) { - colors.m_gradients[ i ] = colors.m_gradients[ i ].interpolated( - to.m_gradients[ i ], ratio ); +#if 1 + /* + When one border has a width of 0 we would prefer to ignore + the color and use always use the other color. TODO ... + */ +#endif + auto& gradient = colors.m_gradients[ i ]; + gradient = gradient.interpolated( to.m_gradients[ i ], ratio ); } return colors; @@ -204,14 +242,44 @@ QDebug operator<<( QDebug debug, const QskBoxBorderColors& colors ) QDebugStateSaver saver( debug ); debug.nospace(); - debug << "BoxBorderColors" << '('; + debug << "BoxBorderColors"; - debug << " L" << colors.gradient( Qsk::Left ); - debug << ", T" << colors.gradient( Qsk::Top ); - debug << ", R" << colors.gradient( Qsk::Right ); - debug << ", B" << colors.gradient( Qsk::Bottom ); + if ( !colors.isValid() ) + { + debug << "()"; + } + else + { + debug << "( "; - debug << " )"; + if ( colors.isMonochrome() ) + { + const auto& gradient = colors.gradient( Qsk::Left ); + QskRgb::debugColor( debug, gradient.startColor() ); + } + else + { + const char prompts[] = { 'L', 'T', 'R', 'B' }; + + for ( int i = 0; i <= Qsk::Bottom; i++ ) + { + if ( i != 0 ) + debug << ", "; + + const auto& gradient = colors.gradient( + static_cast< Qsk::Position >( i ) ); + + debug << prompts[ i ] << ": "; + + if ( gradient.isValid() && gradient.isMonochrome() ) + QskRgb::debugColor( debug, gradient.startColor() ); + else + debug << gradient; + } + } + + debug << " )"; + } return debug; } diff --git a/src/common/QskBoxBorderColors.h b/src/common/QskBoxBorderColors.h index b6ca4b68..6011c2d9 100644 --- a/src/common/QskBoxBorderColors.h +++ b/src/common/QskBoxBorderColors.h @@ -12,10 +12,15 @@ #include #include -class QDebug; - class QSK_EXPORT QskBoxBorderColors { + Q_GADGET + + Q_PROPERTY( QskGradient left READ left WRITE setLeft ) + Q_PROPERTY( QskGradient top READ top WRITE setTop ) + Q_PROPERTY( QskGradient right READ right WRITE setRight ) + Q_PROPERTY( QskGradient bottom READ bottom WRITE setBottom ) + public: QskBoxBorderColors(); @@ -44,6 +49,18 @@ class QSK_EXPORT QskBoxBorderColors void setGradientAt( Qt::Edges, const QskGradient& ); const QskGradient& gradientAt( Qt::Edge ) const; + void setLeft( const QskGradient& ); + const QskGradient& left() const; + + void setTop( const QskGradient& ); + const QskGradient& top() const; + + void setRight( const QskGradient& ); + const QskGradient& right() const; + + void setBottom( const QskGradient& ); + const QskGradient& bottom() const; + QskBoxBorderColors interpolated( const QskBoxBorderColors&, qreal value ) const; static QVariant interpolate( const QskBoxBorderColors&, @@ -53,6 +70,7 @@ class QSK_EXPORT QskBoxBorderColors bool isMonochrome() const; bool isVisible() const; + bool isValid() const; private: QskGradient m_gradients[ 4 ]; @@ -78,8 +96,29 @@ inline const QskGradient& QskBoxBorderColors::gradient( Qsk::Position position ) return m_gradients[ position ]; } +inline const QskGradient& QskBoxBorderColors::left() const +{ + return m_gradients[ Qsk::Left ]; +} + +inline const QskGradient& QskBoxBorderColors::top() const +{ + return m_gradients[ Qsk::Top ]; +} + +inline const QskGradient& QskBoxBorderColors::right() const +{ + return m_gradients[ Qsk::Right ]; +} + +inline const QskGradient& QskBoxBorderColors::bottom() const +{ + return m_gradients[ Qsk::Bottom ]; +} + #ifndef QT_NO_DEBUG_STREAM +class QDebug; QSK_EXPORT QDebug operator<<( QDebug, const QskBoxBorderColors& ); #endif diff --git a/src/common/QskBoxBorderMetrics.cpp b/src/common/QskBoxBorderMetrics.cpp index 25de9fe6..18178062 100644 --- a/src/common/QskBoxBorderMetrics.cpp +++ b/src/common/QskBoxBorderMetrics.cpp @@ -12,6 +12,10 @@ static void qskRegisterBoxBorderMetrics() { qRegisterMetaType< QskBoxBorderMetrics >(); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskBoxBorderMetrics >(); +#endif + QMetaType::registerConverter< QskMargins, QskBoxBorderMetrics >( []( const QskMargins& margins ) { return QskBoxBorderMetrics( margins ); } ); @@ -109,9 +113,24 @@ QDebug operator<<( QDebug debug, const QskBoxBorderMetrics& metrics ) QDebugStateSaver saver( debug ); debug.nospace(); - debug << "BoxBorder" << '('; - debug << metrics.sizeMode() << ',' << metrics.widths(); - debug << ')'; + debug << "BoxBorder" << "( "; + + if ( metrics.sizeMode() != Qt::AbsoluteSize ) + debug << metrics.sizeMode() << ", "; + + const auto& w = metrics.widths(); + + if ( metrics.isEquidistant() ) + { + debug << w.left(); + } + else + { + const char s[] = ", "; + debug << w.left() << s << w.top() << s << w.right() << s << w.bottom(); + } + + debug << " )"; return debug; } diff --git a/src/common/QskBoxBorderMetrics.h b/src/common/QskBoxBorderMetrics.h index 10b7961c..802d2c75 100644 --- a/src/common/QskBoxBorderMetrics.h +++ b/src/common/QskBoxBorderMetrics.h @@ -60,6 +60,8 @@ class QSK_EXPORT QskBoxBorderMetrics static QVariant interpolate( const QskBoxBorderMetrics&, const QskBoxBorderMetrics&, qreal progress ); + constexpr bool isEquidistant() const noexcept; + private: QskMargins m_widths; Qt::SizeMode m_sizeMode; @@ -115,6 +117,11 @@ inline constexpr bool QskBoxBorderMetrics::isNull() const noexcept return m_widths.isNull(); } +inline constexpr bool QskBoxBorderMetrics::isEquidistant() const noexcept +{ + return m_widths.isEquidistant(); +} + inline constexpr const QskMargins& QskBoxBorderMetrics::widths() const noexcept { return m_widths; diff --git a/src/common/QskBoxShapeMetrics.cpp b/src/common/QskBoxShapeMetrics.cpp index 09e9cc70..17c21be1 100644 --- a/src/common/QskBoxShapeMetrics.cpp +++ b/src/common/QskBoxShapeMetrics.cpp @@ -14,6 +14,10 @@ static void qskRegisterBoxShapeMetrics() { qRegisterMetaType< QskBoxShapeMetrics >(); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskBoxShapeMetrics >(); +#endif + QMetaType::registerConverter< int, QskBoxShapeMetrics >( []( int radius ) { return QskBoxShapeMetrics( radius ); } ); diff --git a/src/common/QskGradient.cpp b/src/common/QskGradient.cpp index 101d4d21..889e9e5e 100644 --- a/src/common/QskGradient.cpp +++ b/src/common/QskGradient.cpp @@ -15,6 +15,10 @@ static void qskRegisterGradient() { qRegisterMetaType< QskGradient >(); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskGradient >(); +#endif + QMetaType::registerConverter< QColor, QskGradient >( []( const QColor& color ) { return QskGradient( color ); } ); } @@ -503,6 +507,14 @@ QskGradient QskGradient::interpolated( return QskGradient( gradient->orientation(), stops ); } + if ( isMonochrome() && to.isMonochrome() ) + { + const auto c = QskRgb::interpolated( + m_stops[ 0 ].color(), to.m_stops[ 0 ].color(), value ); + + return QskGradient( to.orientation(), c, c ); + } + if ( isMonochrome() ) { // we can ignore our stops @@ -628,7 +640,49 @@ void QskGradient::updateStatusBits() const QDebug operator<<( QDebug debug, const QskGradient& gradient ) { - debug << "GR:" << gradient.orientation() << gradient.stops().count(); + QDebugStateSaver saver( debug ); + debug.nospace(); + + debug << "Gradient"; + + if ( !gradient.isValid() ) + { + debug << "()"; + } + else + { + debug << "( "; + + if ( gradient.isMonochrome() ) + { + QskRgb::debugColor( debug, gradient.startColor() ); + } + else + { + const char o[] = { 'H', 'V', 'D' }; + debug << o[ gradient.orientation() ] << ", "; + + if ( gradient.stops().count() == 2 ) + { + QskRgb::debugColor( debug, gradient.startColor() ); + debug << ", "; + QskRgb::debugColor( debug, gradient.endColor() ); + } + else + { + const auto& s = gradient.stops(); + for ( int i = 0; i < s.count(); i++ ) + { + if ( i != 0 ) + debug << ", "; + + debug << s[i]; + } + } + } + debug << " )"; + } + return debug; } diff --git a/src/common/QskGradient.h b/src/common/QskGradient.h index c69c59fe..f92e2e74 100644 --- a/src/common/QskGradient.h +++ b/src/common/QskGradient.h @@ -49,7 +49,6 @@ class QSK_EXPORT QskGradient QskGradient( QRgb ); QskGradient( const QColor& ); - QskGradient( Qt::Orientation, const QVector< QskGradientStop >& ); QskGradient( Qt::Orientation, const QColor&, const QColor& ); diff --git a/src/common/QskGradientStop.cpp b/src/common/QskGradientStop.cpp index 530a2a1d..48dbdac2 100644 --- a/src/common/QskGradientStop.cpp +++ b/src/common/QskGradientStop.cpp @@ -14,6 +14,10 @@ static void qskRegisterGradientStop() { qRegisterMetaType< QskGradientStop >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskGradientStop >(); +#endif } Q_CONSTRUCTOR_FUNCTION( qskRegisterGradientStop ) @@ -78,7 +82,12 @@ QColor QskGradientStop::interpolated( QDebug operator<<( QDebug debug, const QskGradientStop& stop ) { - debug << stop.position() << ": " << stop.color(); + QDebugStateSaver saver( debug ); + debug.nospace(); + + debug << stop.position() << ": "; + QskRgb::debugColor( debug, stop.color() ); + return debug; } diff --git a/src/common/QskGradientStop.h b/src/common/QskGradientStop.h index c3ed9b2a..074444b7 100644 --- a/src/common/QskGradientStop.h +++ b/src/common/QskGradientStop.h @@ -47,7 +47,10 @@ class QSK_EXPORT QskGradientStop QColor m_color; // using RGBA instead ? }; -Q_DECLARE_TYPEINFO( QskGradientStop, Q_MOVABLE_TYPE ); +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + Q_DECLARE_TYPEINFO( QskGradientStop, Q_RELOCATABLE_TYPE ); +#endif + Q_DECLARE_METATYPE( QskGradientStop ) inline QskGradientStop::QskGradientStop() noexcept diff --git a/src/common/QskIntervalF.cpp b/src/common/QskIntervalF.cpp index a27a83c8..f4b19d38 100644 --- a/src/common/QskIntervalF.cpp +++ b/src/common/QskIntervalF.cpp @@ -12,6 +12,10 @@ static void qskRegisterIntervalF() { qRegisterMetaType< QskIntervalF >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskIntervalF >(); +#endif } Q_CONSTRUCTOR_FUNCTION( qskRegisterIntervalF ) diff --git a/src/common/QskMargins.h b/src/common/QskMargins.h index e90c9338..3a3bb962 100644 --- a/src/common/QskMargins.h +++ b/src/common/QskMargins.h @@ -61,6 +61,7 @@ class QSK_EXPORT QskMargins : public QMarginsF QskMargins interpolated( const QskMargins&, qreal progress ) const noexcept; constexpr bool isExpanding() const noexcept; + constexpr bool isEquidistant() const noexcept; static QVariant interpolate( const QskMargins&, const QskMargins&, qreal progress ) noexcept; @@ -181,6 +182,11 @@ constexpr inline qreal QskMargins::height() const noexcept return top() + bottom(); } +inline constexpr bool QskMargins::isEquidistant() const noexcept +{ + return ( left() == top() ) && ( left() == right() ) && ( left() == bottom() ); +} + Q_DECLARE_TYPEINFO( QskMargins, Q_MOVABLE_TYPE ); Q_DECLARE_METATYPE( QskMargins ) diff --git a/src/common/QskRgbValue.cpp b/src/common/QskRgbValue.cpp index 49a87f96..7b4c0b8c 100644 --- a/src/common/QskRgbValue.cpp +++ b/src/common/QskRgbValue.cpp @@ -172,3 +172,29 @@ QRgb QskRgb::darker( QRgb rgb, int factor ) noexcept return QColor::fromRgba( rgb ).darker( factor ).rgba(); } +#ifndef QT_NO_DEBUG_STREAM + +#include + +void QskRgb::debugColor( QDebug debug, const QColor& color ) +{ + debugColor( debug, color.rgba() ); +} + +void QskRgb::debugColor( QDebug debug, QRgb rgb ) +{ + QDebugStateSaver saver( debug ); + debug.nospace(); + + debug << '['; + + debug << qRed( rgb ) << "r," << qGreen( rgb ) << "g," + << qBlue( rgb ) << 'b'; + + if ( qAlpha( rgb ) != 255 ) + debug << ',' << qAlpha( rgb ) << 'a'; + + debug << ']'; +} + +#endif diff --git a/src/common/QskRgbValue.h b/src/common/QskRgbValue.h index 6fea8d95..8a6059de 100644 --- a/src/common/QskRgbValue.h +++ b/src/common/QskRgbValue.h @@ -461,23 +461,35 @@ namespace QskRgb return ( rgb & ColorMask ) | ( ( static_cast< uint >( alpha ) & 0xffu ) << 24 ); } - inline QColor toTransparentF( const QColor& color, qreal alpha ) + inline QColor toTransparentF( const QColor& color, qreal opacity ) { - return toTransparent( color, qRound( alpha * 255 ) ); + return toTransparent( color, qRound( opacity * 255 ) ); } - inline QColor toTransparentF( Qt::GlobalColor color, qreal alpha ) + inline QColor toTransparentF( Qt::GlobalColor color, qreal opacity ) { - return toTransparent( QColor( color ), qRound( alpha * 255 ) ); + return toTransparent( QColor( color ), qRound( opacity * 255 ) ); } - inline constexpr QRgb toTransparentF( QRgb rgb, qreal alpha ) noexcept + inline constexpr QRgb toTransparentF( QRgb rgb, qreal opacity ) noexcept { - return toTransparent( rgb, qRound( alpha * 255 ) ); + return toTransparent( rgb, qRound( opacity * 255 ) ); } QSK_EXPORT QRgb lighter( QRgb, int factor = 150 ) noexcept; QSK_EXPORT QRgb darker( QRgb, int factor = 200 ) noexcept; } +#ifndef QT_NO_DEBUG_STREAM + +class QDebug; + +namespace QskRgb +{ + QSK_EXPORT void debugColor( QDebug, const QColor& ); + QSK_EXPORT void debugColor( QDebug, QRgb ); +} + +#endif + #endif diff --git a/src/common/QskScaleTickmarks.cpp b/src/common/QskScaleTickmarks.cpp index 836fc474..65f67089 100644 --- a/src/common/QskScaleTickmarks.cpp +++ b/src/common/QskScaleTickmarks.cpp @@ -9,6 +9,10 @@ static void qskRegisterTickmarks() { qRegisterMetaType< QskScaleTickmarks >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskScaleTickmarks >(); +#endif } Q_CONSTRUCTOR_FUNCTION( qskRegisterTickmarks ) diff --git a/src/common/QskShadowMetrics.cpp b/src/common/QskShadowMetrics.cpp index 37b951b0..384104d3 100644 --- a/src/common/QskShadowMetrics.cpp +++ b/src/common/QskShadowMetrics.cpp @@ -12,6 +12,10 @@ static void qskRegisterShadowMetrics() { qRegisterMetaType< QskShadowMetrics >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskShadowMetrics >(); +#endif } Q_CONSTRUCTOR_FUNCTION( qskRegisterShadowMetrics ) diff --git a/src/common/QskStateCombination.cpp b/src/common/QskStateCombination.cpp deleted file mode 100644 index 07a7b481..00000000 --- a/src/common/QskStateCombination.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the QSkinny License, Version 1.0 - *****************************************************************************/ - -#include "QskStateCombination.h" - -static void qskRegisterStateCombination() -{ - qRegisterMetaType< QskStateCombination >(); -} - -Q_CONSTRUCTOR_FUNCTION( qskRegisterStateCombination ) - diff --git a/src/common/QskStateCombination.h b/src/common/QskStateCombination.h index 8393c90b..13d64e22 100644 --- a/src/common/QskStateCombination.h +++ b/src/common/QskStateCombination.h @@ -21,6 +21,9 @@ class QSK_EXPORT QskStateCombination constexpr QskStateCombination( QskAspect::States = QskAspect::States() ) noexcept; constexpr QskStateCombination( Type, QskAspect::States = QskAspect::States() ) noexcept; + constexpr bool operator==( QskStateCombination ) const noexcept; + constexpr bool operator!=( QskStateCombination ) const noexcept; + constexpr bool isNull() const noexcept; void setType( Type ) noexcept; @@ -36,7 +39,6 @@ class QSK_EXPORT QskStateCombination }; Q_DECLARE_TYPEINFO( QskStateCombination, Q_MOVABLE_TYPE ); -Q_DECLARE_METATYPE( QskStateCombination ) constexpr inline QskStateCombination::QskStateCombination( QskAspect::State state ) noexcept @@ -90,4 +92,14 @@ constexpr inline QskAspect::States QskStateCombination::states() const noexcept return m_states; } +constexpr bool QskStateCombination::operator==( QskStateCombination other ) const noexcept +{ + return ( m_type == other.m_type ) && ( m_states == other.m_states ); +} + +constexpr bool QskStateCombination::operator!=( QskStateCombination other ) const noexcept +{ + return !( *this == other ); +} + #endif diff --git a/src/controls/QskCheckBox.cpp b/src/controls/QskCheckBox.cpp new file mode 100644 index 00000000..69cdb7bd --- /dev/null +++ b/src/controls/QskCheckBox.cpp @@ -0,0 +1,184 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskCheckBox.h" +#include "QskAspect.h" + +#include + +QSK_SUBCONTROL( QskCheckBox, Panel ) +QSK_SUBCONTROL( QskCheckBox, Indicator ) + +QSK_SYSTEM_STATE( QskCheckBox, PartiallyChecked, QskAspect::LastUserState << 2 ) + +class QskCheckBox::PrivateData +{ + public: + PrivateData() + : checkState( Qt::Unchecked ) + , checkStateChanging( false ) + , toggleChanging( false ) + , tristate( false ) + { + } + + QSet< QskAbstractButton* > group; + + int groupItemsChecked = 0; + + Qt::CheckState checkState : 2; + bool checkStateChanging : 1; + bool toggleChanging : 1; + bool tristate : 1; +}; + +QskCheckBox::QskCheckBox( QQuickItem* parent ) + : Inherited( parent ) + , m_data( new PrivateData() ) +{ + setAcceptHoverEvents( true ); + initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); + + connect( this, &QskCheckBox::checkedChanged, this, + [ this ]( bool on ) { setCheckStateInternal( on ? Qt::Checked : Qt::Unchecked ); } ); +} + +QskCheckBox::~QskCheckBox() +{ + Q_EMIT removeFromAllGroupsRequested(); +} + +bool QskCheckBox::isCheckable() const +{ + return true; +} + +Qt::CheckState QskCheckBox::checkState() const +{ + return m_data->checkState; +} + +void QskCheckBox::setCheckStateInternal( Qt::CheckState checkState ) +{ + if( m_data->checkStateChanging ) + return; + + setSkinStateFlag( PartiallyChecked, checkState == Qt::PartiallyChecked ); + + m_data->checkState = checkState; + Q_EMIT checkStateChanged( checkState ); +} + +void QskCheckBox::setCheckState( Qt::CheckState checkState ) +{ + if( checkState == m_data->checkState ) + return; + + m_data->checkStateChanging = true; + + if( checkState == Qt::PartiallyChecked ) + { + setChecked( true ); + setTristate( true ); + } + else + { + setChecked( checkState == Qt::Checked ); + } + + m_data->checkStateChanging = false; + + setCheckStateInternal( checkState ); +} + +bool QskCheckBox::isTristate() const +{ + return m_data->tristate; +} + +void QskCheckBox::setTristate( bool tristate ) +{ + if( m_data->tristate != tristate ) + { + m_data->tristate = tristate; + Q_EMIT tristateChanged( tristate ); + } +} + +void QskCheckBox::updated() +{ + if( m_data->toggleChanging ) + return; + + const auto& groupItemsChecked = m_data->groupItemsChecked; + + if( groupItemsChecked == m_data->group.size() ) + { + setCheckState( Qt::Checked ); + } + else if ( groupItemsChecked == 0 ) + { + setCheckState( Qt::Unchecked ); + } + else + { + setCheckState( Qt::PartiallyChecked ); + } +} + +void QskCheckBox::addToGroup( QskCheckBox* groupItem ) +{ + if( m_data->group.contains( groupItem ) ) + return; + + m_data->group.insert( groupItem ); + + if( groupItem->checkState() == Qt::Checked ) + m_data->groupItemsChecked++; + + updated(); + + connect( this, &QskCheckBox::checkStateChanged, + groupItem, [ this, groupItem ]( Qt::CheckState checkState ) + { + if( checkState == Qt::Checked ) + { + m_data->toggleChanging = true; + groupItem->setChecked( true ); + m_data->groupItemsChecked = m_data->group.size(); + m_data->toggleChanging = false; + } + else if ( checkState == Qt::Unchecked ) + { + m_data->toggleChanging = true; + groupItem->setChecked( false ); + m_data->groupItemsChecked = 0; + m_data->toggleChanging = false; + } + } ); + + connect( groupItem, &QskAbstractButton::toggled, + this, [ this, groupItem ]( bool toggled ) + { + m_data->groupItemsChecked += toggled ? 1 : -1; + updated(); + } ); + + connect( groupItem, &QskCheckBox::removeFromAllGroupsRequested, + this, [ this, groupItem ]( ) { removeFromGroup( groupItem ); } ); +} + +void QskCheckBox::removeFromGroup( QskCheckBox* groupItem ) +{ + if( !m_data->group.remove( groupItem ) ) + return; + + if( groupItem->checkState() == Qt::Checked ) + m_data->groupItemsChecked--; + + updated(); +} + +#include "moc_QskCheckBox.cpp" diff --git a/src/controls/QskCheckBox.h b/src/controls/QskCheckBox.h new file mode 100644 index 00000000..9a2fb36e --- /dev/null +++ b/src/controls/QskCheckBox.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_CHECK_BOX_H +#define QSK_CHECK_BOX_H + +#include "QskAbstractButton.h" + +class QSK_EXPORT QskCheckBox : public QskAbstractButton +{ + Q_OBJECT + + Q_PROPERTY( Qt::CheckState checkState READ checkState + WRITE setCheckState NOTIFY checkStateChanged FINAL ) + + Q_PROPERTY( bool tristate READ isTristate + WRITE setTristate NOTIFY tristateChanged FINAL ) + + using Inherited = QskAbstractButton; + + public: + QSK_SUBCONTROLS( Panel, Indicator ) + QSK_STATES( PartiallyChecked ) + + QskCheckBox( QQuickItem* parent = nullptr ); + ~QskCheckBox() override; + + Qt::CheckState checkState() const; + bool isTristate() const; + bool isCheckable() const override final; + + void addToGroup( QskCheckBox* ); + void removeFromGroup( QskCheckBox* ); + + public Q_SLOTS: + void setCheckState( Qt::CheckState ); + void setTristate( bool triState = true ); + + Q_SIGNALS: + void checkStateChanged( Qt::CheckState ); + void tristateChanged( bool ); + void removeFromAllGroupsRequested(); + + private: + void setCheckStateInternal( Qt::CheckState ); + void updated(); + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/controls/QskCheckBoxSkinlet.cpp b/src/controls/QskCheckBoxSkinlet.cpp new file mode 100644 index 00000000..28946f6e --- /dev/null +++ b/src/controls/QskCheckBoxSkinlet.cpp @@ -0,0 +1,137 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskCheckBoxSkinlet.h" +#include "QskCheckBox.h" +#include "QskSGNode.h" + +#include +#include + +namespace +{ + class IndicatorNode : public QSGGeometryNode + { + public: + IndicatorNode() + : m_geometry( QSGGeometry::defaultAttributes_Point2D(), 3 ) + { +#if QT_VERSION >= QT_VERSION_CHECK( 5, 8, 0 ) + m_geometry.setDrawingMode( QSGGeometry::DrawLineStrip ); +#else + m_geometry.setDrawingMode( GL_LINE_STRIP ); +#endif + m_geometry.setLineWidth( 2 ); + setGeometry( &m_geometry ); + + setMaterial( &m_material ); + } + + void update( bool isPartially, const QRectF& rect, const QColor& color ) + { + if ( color != m_material.color() ) + { + m_material.setColor( color ); + markDirty( QSGNode::DirtyMaterial ); + } + + if ( rect != m_rect || isPartially != m_isPartially ) + { + m_rect = rect; + m_isPartially = isPartially; + + const auto x = rect.x(); + const auto y = rect.y(); + const auto w = rect.width(); + const auto h = rect.height(); + + auto points = m_geometry.vertexDataAsPoint2D(); + + if ( isPartially ) + { + points[0].set( x, y + h / 2 ); + points[1] = points[0]; + points[2].set( x + w, y + h / 2 ); + } + else + { + points[0].set( x, y + h / 2 ); + points[1].set( x + w / 3, y + h ); + points[2].set( x + w, y ); + } + + markDirty( QSGNode::DirtyGeometry ); + } + } + + private: + QSGFlatColorMaterial m_material; + QSGGeometry m_geometry; + + QRectF m_rect; + bool m_isPartially; + }; +} + +QskCheckBoxSkinlet::QskCheckBoxSkinlet( QskSkin* skin ) + : QskSkinlet( skin ) +{ + setNodeRoles( { PanelRole, IndicatorRole } ); +} + +QskCheckBoxSkinlet::~QskCheckBoxSkinlet() +{ +} + +QRectF QskCheckBoxSkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const +{ + if ( subControl == QskCheckBox::Indicator ) + return skinnable->innerBox( QskCheckBox::Panel, contentsRect ); + + return contentsRect; +} + +QSGNode* QskCheckBoxSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + auto checkBox = static_cast< const QskCheckBox* >( skinnable ); + + switch( nodeRole ) + { + case PanelRole: + return updateBoxNode( skinnable, node, QskCheckBox::Panel ); + + case IndicatorRole: + return updateIndicatorNode( checkBox, node ); + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} + +QSGNode* QskCheckBoxSkinlet::updateIndicatorNode( + const QskCheckBox* checkBox, QSGNode* node ) const +{ + using Q = QskCheckBox; + + const auto state = checkBox->checkState(); + if ( state == Qt::Unchecked ) + return nullptr; + + const auto rect = checkBox->subControlRect( Q::Indicator ); + if ( rect.isEmpty() ) + return nullptr; + + auto indicatorNode = QskSGNode::ensureNode< IndicatorNode >( node ); + indicatorNode->update( state != Qt::Checked, rect, checkBox->color( Q::Indicator ) ); + + return indicatorNode; +} + +QSizeF QskCheckBoxSkinlet::sizeHint( const QskSkinnable* skinnable, + Qt::SizeHint, const QSizeF& ) const +{ + return skinnable->strutSizeHint( QskCheckBox::Panel ); +} diff --git a/src/controls/QskCheckBoxSkinlet.h b/src/controls/QskCheckBoxSkinlet.h new file mode 100644 index 00000000..094c5807 --- /dev/null +++ b/src/controls/QskCheckBoxSkinlet.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_CHECK_BOX_SKINLET_H +#define QSK_CHECK_BOX_SKINLET_H + +#include "QskSkinlet.h" + +class QskCheckBox; + +class QSK_EXPORT QskCheckBoxSkinlet : public QskSkinlet +{ + Q_GADGET + + using Inherited = QskSkinlet; + + public: + enum NodeRole + { + PanelRole, + IndicatorRole, + }; + + Q_INVOKABLE QskCheckBoxSkinlet( QskSkin* = nullptr ); + ~QskCheckBoxSkinlet() override; + + QRectF subControlRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol ) const override; + + QSizeF sizeHint( const QskSkinnable*, + Qt::SizeHint, const QSizeF& ) const override; + + protected: + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; + + protected: + virtual QSGNode* updateIndicatorNode( const QskCheckBox*, QSGNode* ) const; +}; + +#endif diff --git a/src/controls/QskEvent.h b/src/controls/QskEvent.h index bd5b8e90..83dd0b5f 100644 --- a/src/controls/QskEvent.h +++ b/src/controls/QskEvent.h @@ -23,6 +23,12 @@ class QWheelEvent; class QHoverEvent; class QKeyEvent; +#define QSK_EVENT_DISABLE_COPY(Class) \ + Class(const Class &) = default; \ + Class(Class &&) = delete; \ + Class &operator=(const Class &other) = default; \ + Class &operator=(Class &&) = delete; + class QSK_EXPORT QskEvent : public QEvent { public: @@ -53,6 +59,8 @@ class QSK_EXPORT QskEvent : public QEvent virtual QskEvent* clone() const; #endif + protected: + QSK_EVENT_DISABLE_COPY( QskEvent ) }; class QSK_EXPORT QskGeometryChangeEvent : public QskEvent @@ -68,6 +76,9 @@ class QSK_EXPORT QskGeometryChangeEvent : public QskEvent QskGeometryChangeEvent* clone() const override; + protected: + QSK_EVENT_DISABLE_COPY( QskGeometryChangeEvent ) + private: const QRectF m_rect; const QRectF m_oldRect; @@ -83,6 +94,9 @@ class QSK_EXPORT QskWindowChangeEvent : public QskEvent QskWindowChangeEvent* clone() const override; + protected: + QSK_EVENT_DISABLE_COPY( QskWindowChangeEvent ) + private: QQuickWindow* const m_oldWindow; QQuickWindow* const m_window; @@ -97,6 +111,9 @@ class QSK_EXPORT QskPopupEvent : public QskEvent QskPopupEvent* clone() const override; + protected: + QSK_EVENT_DISABLE_COPY( QskPopupEvent ) + private: QskPopup* const m_popup; }; @@ -110,6 +127,9 @@ class QSK_EXPORT QskGestureEvent : public QskEvent QskGestureEvent* clone() const override; + protected: + QSK_EVENT_DISABLE_COPY( QskGestureEvent ) + private: std::shared_ptr< const QskGesture > m_gesture; }; @@ -130,6 +150,9 @@ class QSK_EXPORT QskAnimatorEvent : public QskEvent QskAnimatorEvent* clone() const override; + protected: + QSK_EVENT_DISABLE_COPY( QskAnimatorEvent ) + private: const QskAspect m_aspect; const State m_state; diff --git a/src/controls/QskMenu.cpp b/src/controls/QskMenu.cpp index 118510fd..387435f9 100644 --- a/src/controls/QskMenu.cpp +++ b/src/controls/QskMenu.cpp @@ -37,6 +37,11 @@ namespace QString text; QskGraphic graphic; + +#if 0 + // TODO ... + bool isEnabled = true; +#endif }; } @@ -75,6 +80,9 @@ QskMenu::~QskMenu() { } +#if 1 + +// has no effect as we do not offer submenus yet. TODO ... bool QskMenu::isCascading() const { return flagHint( QskMenu::Panel | QskAspect::Style ); @@ -92,6 +100,8 @@ void QskMenu::resetCascading() Q_EMIT cascadingChanged( isCascading() ); } +#endif + void QskMenu::setOrigin( const QPointF& origin ) { if ( origin != m_data->origin ) diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index 05ba53ed..95b78b67 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -12,6 +12,7 @@ #include "QskGraphicProviderMap.h" #include "QskSkinHintTable.h" #include "QskStandardSymbol.h" +#include "QskPlatform.h" #include "QskMargins.h" @@ -28,6 +29,9 @@ QSK_QT_PRIVATE_END #include "QskBox.h" #include "QskBoxSkinlet.h" +#include "QskCheckBox.h" +#include "QskCheckBoxSkinlet.h" + #include "QskFocusIndicator.h" #include "QskFocusIndicatorSkinlet.h" @@ -128,7 +132,6 @@ class QskSkin::PrivateData std::unordered_map< const QMetaObject*, SkinletData > skinletMap; QskSkinHintTable hintTable; - QskAspect::States stateMask = QskAspect::AllStates; std::unordered_map< int, QFont > fonts; std::unordered_map< int, QskColorFilter > graphicFilters; @@ -143,6 +146,7 @@ QskSkin::QskSkin( QObject* parent ) declareSkinlet< QskControl, QskSkinlet >(); declareSkinlet< QskBox, QskBoxSkinlet >(); + declareSkinlet< QskCheckBox, QskCheckBoxSkinlet >(); declareSkinlet< QskFocusIndicator, QskFocusIndicatorSkinlet >(); declareSkinlet< QskGraphicLabel, QskGraphicLabelSkinlet >(); declareSkinlet< QskListView, QskListViewSkinlet >(); @@ -217,13 +221,15 @@ void QskSkin::declareSkinlet( const QMetaObject* metaObject, void QskSkin::setupFonts( const QString& family, int weight, bool italic ) { + const int sizes[] = { 10, 15, 20, 32, 66 }; + static_assert( sizeof( sizes ) / sizeof( sizes[ 0 ] ) == HugeFont, + "QskSkin::setupFonts: bad list size." ); + QFont font( family, -1, weight, italic ); - const uint base = TinyFont; for ( int i = TinyFont; i <= HugeFont; i++ ) { - // TODO: make the scaling components configurable - font.setPixelSize( int( std::pow( uint( i ) - base + 2, 2.5 ) ) ); + font.setPixelSize( qskDpiScaled( sizes[i-1] ) ); m_data->fonts[ i ] = font; } @@ -349,22 +355,6 @@ const int* QskSkin::dialogButtonLayout( Qt::Orientation orientation ) const return QPlatformDialogHelper::buttonLayout( orientation, policy ); } -void QskSkin::setStateMask( QskAspect::States mask ) -{ - for ( auto state : { QskControl::Disabled, QskControl::Hovered, QskControl::Focused } ) - { - if ( mask & state ) - m_data->stateMask |= state; - else - m_data->stateMask &= ~state; - } -} - -QskAspect::States QskSkin::stateMask() const -{ - return m_data->stateMask; -} - QskSkinlet* QskSkin::skinlet( const QMetaObject* metaObject ) { while ( metaObject ) diff --git a/src/controls/QskSkin.h b/src/controls/QskSkin.h index adeb2c54..723b0115 100644 --- a/src/controls/QskSkin.h +++ b/src/controls/QskSkin.h @@ -77,9 +77,6 @@ class QSK_EXPORT QskSkin : public QObject virtual const int* dialogButtonLayout( Qt::Orientation ) const; virtual QString dialogButtonText( int button ) const; - void setStateMask( QskAspect::States ); - QskAspect::States stateMask() const; - QskSkinlet* skinlet( const QMetaObject* ); const QskSkinHintTable& hintTable() const; diff --git a/src/controls/QskSkinHintTable.cpp b/src/controls/QskSkinHintTable.cpp index 9bac96f0..7b654c65 100644 --- a/src/controls/QskSkinHintTable.cpp +++ b/src/controls/QskSkinHintTable.cpp @@ -64,41 +64,11 @@ QskSkinHintTable::QskSkinHintTable() { } -QskSkinHintTable::QskSkinHintTable( const QskSkinHintTable& other ) - : m_hints( nullptr ) - , m_animatorCount( other.m_animatorCount ) - , m_statefulCount( other.m_statefulCount ) -{ - if ( other.m_hints ) - m_hints = new HintMap( *( other.m_hints ) ); -} - QskSkinHintTable::~QskSkinHintTable() { delete m_hints; } -QskSkinHintTable& QskSkinHintTable::operator=( const QskSkinHintTable& other ) -{ - m_animatorCount = other.m_animatorCount; - m_statefulCount = other.m_statefulCount; - - if ( other.m_hints ) - { - if ( m_hints == nullptr ) - m_hints = new HintMap(); - - *m_hints = *other.m_hints; - } - else - { - delete m_hints; - m_hints = nullptr; - } - - return *this; -} - const std::unordered_map< QskAspect, QVariant >& QskSkinHintTable::hints() const { if ( m_hints ) @@ -126,11 +96,7 @@ bool QskSkinHintTable::setHint( QskAspect aspect, const QVariant& skinHint ) QSK_ASSERT_COUNTER( m_animatorCount ); } - if ( aspect.hasStates() ) - { - m_statefulCount++; - QSK_ASSERT_COUNTER( m_statefulCount ); - } + m_states |= aspect.states(); return true; } @@ -158,8 +124,7 @@ bool QskSkinHintTable::removeHint( QskAspect aspect ) if ( aspect.isAnimator() ) m_animatorCount--; - if ( aspect.hasStates() ) - m_statefulCount--; + // how to clear m_states ? TODO ... if ( m_hints->empty() ) { @@ -184,8 +149,7 @@ QVariant QskSkinHintTable::takeHint( QskAspect aspect ) if ( aspect.isAnimator() ) m_animatorCount--; - if ( aspect.hasStates() ) - m_statefulCount--; + // how to clear m_states ? TODO ... if ( m_hints->empty() ) { @@ -206,14 +170,14 @@ void QskSkinHintTable::clear() m_hints = nullptr; m_animatorCount = 0; - m_statefulCount = 0; + m_states = QskAspect::NoState; } const QVariant* QskSkinHintTable::resolvedHint( QskAspect aspect, QskAspect* resolvedAspect ) const { if ( m_hints != nullptr ) - return qskResolvedHint( aspect, *m_hints, resolvedAspect ); + return qskResolvedHint( aspect & m_states, *m_hints, resolvedAspect ); return nullptr; } @@ -223,7 +187,7 @@ QskAspect QskSkinHintTable::resolvedAspect( QskAspect aspect ) const QskAspect a; if ( m_hints != nullptr ) - qskResolvedHint( aspect, *m_hints, &a ); + qskResolvedHint( aspect & m_states, *m_hints, &a ); return a; } @@ -233,6 +197,8 @@ QskAspect QskSkinHintTable::resolvedAnimator( { if ( m_hints && m_animatorCount > 0 ) { + aspect &= m_states; + Q_FOREVER { auto it = m_hints->find( aspect ); @@ -268,15 +234,16 @@ bool QskSkinHintTable::setAnimation( bool QskSkinHintTable::isResolutionMatching( QskAspect aspect1, QskAspect aspect2 ) const { + // remove states we do not have early + aspect1 &= m_states; + aspect2 &= m_states; + if ( aspect1 == aspect2 ) return true; if ( aspect1.trunk() != aspect2.trunk() ) return false; - if ( !hasStates() ) - return false; - const auto a1 = aspect1; const auto a2 = aspect2; diff --git a/src/controls/QskSkinHintTable.h b/src/controls/QskSkinHintTable.h index c1e6a647..9ae56be9 100644 --- a/src/controls/QskSkinHintTable.h +++ b/src/controls/QskSkinHintTable.h @@ -17,12 +17,8 @@ class QSK_EXPORT QskSkinHintTable { public: QskSkinHintTable(); - QskSkinHintTable( const QskSkinHintTable& other ); - ~QskSkinHintTable(); - QskSkinHintTable& operator=( const QskSkinHintTable& ); - bool setAnimation( QskAspect, QskAnimationHint ); QskAnimationHint animation( QskAspect ) const; @@ -40,9 +36,10 @@ class QSK_EXPORT QskSkinHintTable const std::unordered_map< QskAspect, QVariant >& hints() const; bool hasAnimators() const; - bool hasStates() const; bool hasHints() const; + QskAspect::States states() const; + void clear(); const QVariant* resolvedHint( QskAspect, @@ -56,13 +53,15 @@ class QSK_EXPORT QskSkinHintTable bool isResolutionMatching( QskAspect, QskAspect ) const; private: + Q_DISABLE_COPY( QskSkinHintTable ) + static const QVariant invalidHint; typedef std::unordered_map< QskAspect, QVariant > HintMap; HintMap* m_hints = nullptr; unsigned short m_animatorCount = 0; - unsigned short m_statefulCount = 0; + QskAspect::States m_states; }; inline bool QskSkinHintTable::hasHints() const @@ -70,9 +69,9 @@ inline bool QskSkinHintTable::hasHints() const return m_hints != nullptr; } -inline bool QskSkinHintTable::hasStates() const +inline QskAspect::States QskSkinHintTable::states() const { - return m_statefulCount > 0; + return m_states; } inline bool QskSkinHintTable::hasAnimators() const diff --git a/src/controls/QskSkinTransition.cpp b/src/controls/QskSkinTransition.cpp index 9b769199..985ee28b 100644 --- a/src/controls/QskSkinTransition.cpp +++ b/src/controls/QskSkinTransition.cpp @@ -20,6 +20,61 @@ #include #include +static void qskSendStyleEventRecursive( QQuickItem* item ) +{ + QEvent event( QEvent::StyleChange ); + QCoreApplication::sendEvent( item, &event ); + + const auto children = item->childItems(); + for ( auto child : children ) + qskSendStyleEventRecursive( child ); +} + +static void qskAddCandidates( const QskSkinTransition::Type mask, + const QskSkin* skin, QSet< QskAspect >& candidates ) +{ + for ( const auto& entry : skin->hintTable().hints() ) + { + const auto aspect = entry.first.trunk(); + + if ( aspect.isAnimator() ) + continue; + + bool isCandidate = false; + + switch( aspect.type() ) + { + case QskAspect::Flag: + { + if ( aspect.flagPrimitive() == QskAspect::GraphicRole ) + { + isCandidate = mask & QskSkinTransition::Color; + } +#if 0 + else if ( aspect.flagPrimitive() == QskAspect::FontRole ) + { + isCandidate = mask & QskSkinTransition::Metric; + } +#endif + break; + } + case QskAspect::Color: + { + isCandidate = mask & QskSkinTransition::Color; + break; + } + case QskAspect::Metric: + { + isCandidate = mask & QskSkinTransition::Metric; + break; + } + } + + if ( isCandidate ) + candidates += aspect; + } +} + namespace { class UpdateInfo @@ -40,475 +95,85 @@ namespace int updateModes; }; - class AnimatorCandidate + class HintAnimator : public QskHintAnimator { public: - AnimatorCandidate() = default; - - inline AnimatorCandidate( QskAspect aspect, - const QVariant& from, const QVariant& to ) - : aspect( aspect ) - , from( from ) - , to( to ) + inline HintAnimator( const QskControl* control, const QskAspect aspect, + const QVariant& value1, const QVariant& value2, QskAnimationHint hint ) { - } + setAspect( aspect ); + setStartValue( value1 ); + setEndValue( value2 ); - QskAspect aspect; - QVariant from; - QVariant to; + setDuration( hint.duration ); + setEasingCurve( hint.type ); + setUpdateFlags( hint.updateFlags ); + + setWindow( control->window() ); + } }; -} -static QVector< AnimatorCandidate > qskAnimatorCandidates( - QskSkinTransition::Type mask, - const QskSkinHintTable& oldTable, - const std::unordered_map< int, QskColorFilter >& oldFilters, - const QskSkinHintTable& newTable, - const std::unordered_map< int, QskColorFilter >& newFilters ) -{ - // building a list of candidates for animations by comparing - // the old/new set of skin hints - - const QskColorFilter noFilter; - QVector< AnimatorCandidate > candidates; - - if ( !oldTable.hasHints() ) - return candidates; - - for ( const auto& entry : newTable.hints() ) - { - const auto aspect = entry.first; - - if ( aspect.isAnimator() ) - continue; - - const auto type = aspect.type(); - - if ( type == QskAspect::Flag ) - { - switch ( aspect.flagPrimitive() ) - { - case QskAspect::GraphicRole: - { - if ( !( mask & QskSkinTransition::Color ) ) - continue; - - int role1 = 0; - - const auto value = oldTable.resolvedHint( aspect ); - if ( value ) - role1 = value->toInt(); - - const int role2 = entry.second.toInt(); - - /* - When the role is the same we already have the animators - for the graphic filter table running - */ - if ( role1 != role2 ) - { - const auto it1 = oldFilters.find( role1 ); - const auto it2 = newFilters.find( role2 ); - - if ( it1 != oldFilters.end() || it2 != newFilters.end() ) - { - const auto& f1 = ( it1 != oldFilters.end() ) ? it1->second : noFilter; - const auto& f2 = ( it2 != newFilters.end() ) ? it2->second : noFilter; - - if ( f1 != f2 ) - { - candidates += AnimatorCandidate( aspect, - QVariant::fromValue( f1 ), QVariant::fromValue( f2 ) ); - } - } - } - break; - } - case QskAspect::FontRole: - { - if ( !( mask & QskSkinTransition::Metric ) ) - continue; - - break; - } - default: - ; - } - } - else - { - if ( ( ( type == QskAspect::Color ) && ( mask & QskSkinTransition::Color ) ) || - ( ( type == QskAspect::Metric ) && ( mask & QskSkinTransition::Metric ) ) ) - { - auto value = oldTable.resolvedHint( aspect ); - if ( value == nullptr && aspect.subControl() != QskAspect::Control ) - { - auto a = aspect; - a.setSubControl( QskAspect::Control ); - a.clearStates(); - value = oldTable.resolvedHint( a ); - } - - /* - We are missing transitions, when a hint in newTable - gets resolved from QskControl. TODO ... - */ - if ( value && *value != entry.second ) - candidates += AnimatorCandidate( aspect, *value, entry.second ); - } - } - } - - return candidates; -} - -namespace -{ - class AnimatorGroup + class WindowAnimator { public: - AnimatorGroup( QQuickWindow* window = nullptr ) - : m_window( window ) - { - } + WindowAnimator( QQuickWindow* = nullptr ); - inline const QQuickWindow* window() const - { - return m_window; - } + const QQuickWindow* window() const; - void start() - { - for ( auto& it : m_hintAnimatorMap ) - it.second.start(); + void start(); + bool isRunning() const; - for ( auto& it : m_graphicFilterAnimatorMap ) - it.second.start(); - } + QVariant animatedHint( QskAspect ) const; + QVariant animatedGraphicFilter( int graphicRole ) const; - bool isRunning() const - { - if ( !m_hintAnimatorMap.empty() ) - { - const auto& animator = m_hintAnimatorMap.begin()->second; - if ( animator.isRunning() ) - return true; - } + void addGraphicFilterAnimators( const QskAnimationHint&, + const QskSkin*, const QskSkin* ); - if ( !m_graphicFilterAnimatorMap.empty() ) - { - const auto& animator = m_graphicFilterAnimatorMap.begin()->second; - if ( animator.isRunning() ) - return true; - } + void addItemAspects( QQuickItem*, + const QskAnimationHint&, const QSet< QskAspect >&, + const QskSkin*, const QskSkin* ); - return false; - } - - inline QVariant animatedHint( QskAspect aspect ) const - { - auto it = m_hintAnimatorMap.find( aspect ); - if ( it != m_hintAnimatorMap.cend() ) - { - const auto& animator = it->second; - if ( animator.isRunning() ) - return animator.currentValue(); - } - - return QVariant(); - } - - inline QVariant animatedGraphicFilter( int graphicRole ) const - { - auto it = m_graphicFilterAnimatorMap.find( graphicRole ); - if ( it != m_graphicFilterAnimatorMap.cend() ) - { - const auto& animator = it->second; - if ( animator.isRunning() ) - return animator.currentValue(); - } - - return QVariant(); - } - - void addGraphicFilterAnimators( - const QskAnimationHint& animatorHint, - const std::unordered_map< int, QskColorFilter >& oldFilters, - const std::unordered_map< int, QskColorFilter >& newFilters ) - { - const QskColorFilter noFilter; - - for ( auto it2 = newFilters.begin(); it2 != newFilters.end(); ++it2 ) - { - auto it1 = oldFilters.find( it2->first ); - if ( it1 == oldFilters.cend() ) - it1 = oldFilters.find( 0 ); - - const auto& f1 = ( it1 != oldFilters.cend() ) ? it1->second : noFilter; - const auto& f2 = it2->second; - - if ( f1 != f2 ) - { - QskVariantAnimator animator; - animator.setWindow( m_window ); - animator.setDuration( animatorHint.duration ); - animator.setEasingCurve( animatorHint.type ); - animator.setStartValue( QVariant::fromValue( f1 ) ); - animator.setEndValue( QVariant::fromValue( f2 ) ); - - m_graphicFilterAnimatorMap.emplace( it2->first, animator ); - } - } - } - - void addAnimators( QQuickItem* item, const QskAnimationHint& animatorHint, - const QVector< AnimatorCandidate >& candidates, QskSkin* skin ) - { - if ( !item->isVisible() ) - return; - - if ( auto control = qskControlCast( item ) ) - { - if ( control->isInitiallyPainted() && ( skin == control->effectiveSkin() ) ) - { - addControlAnimators( control, animatorHint, candidates ); -#if 1 - /* - As it is hard to identify which controls depend on the animated - graphic filters we schedule an initial update and let the - controls do the rest: see QskSkinnable::effectiveGraphicFilter - */ - control->update(); -#endif - } - } - - const auto children = item->childItems(); - for ( auto child : children ) - addAnimators( child, animatorHint, candidates, skin ); - } - - void update() - { - for ( auto& info : m_updateInfos ) - { - if ( auto control = info.control ) - { - if ( info.updateModes & UpdateInfo::Polish ) - { - control->resetImplicitSize(); - control->polish(); - } - - if ( info.updateModes & UpdateInfo::Update ) - control->update(); - } - } - } + void update(); private: - void addControlAnimators( QskControl* control, const QskAnimationHint& animatorHint, - const QVector< AnimatorCandidate >& candidates ) - { - const auto subControls = control->subControls(); + bool isControlAffected( const QskControl*, + const QVector< QskAspect::Subcontrol >&, QskAspect ) const; - for ( const auto& candidate : candidates ) - { - if ( !candidate.aspect.isMetric() ) - { - if ( !( control->flags() & QQuickItem::ItemHasContents ) ) - { - // while metrics might have an effect on layouts, we - // can safely ignore others for controls without content - continue; - } - } + void addHints( const QskControl*, + const QskAnimationHint&, const QSet< QskAspect >& candidates, + const QskSkin* skin1, const QskSkin* skin2 ); - const auto subControl = candidate.aspect.subControl(); - if ( subControl != control->effectiveSubcontrol( subControl ) ) - { - // The control uses subcontrol redirection, so we can assume it - // is not interested in this subcontrol. - continue; - } + void storeAnimator( const QskControl*, const QskAspect, + const QVariant&, const QVariant&, QskAnimationHint ); - if ( subControl != QskAspect::Control ) - { - if ( !subControls.contains( subControl ) ) - { - // the control is not interested in the aspect - continue; - } - } - else - { - if ( !control->autoFillBackground() ) - { - // no need to animate the background unless we show it - continue; - } - } - - auto a = candidate.aspect; - a.setStates( control->skinStates() ); - - const auto requestState = control->hintStatus( a ); - - if ( requestState.source != QskSkinHintStatus::Skin ) - { - // The control does not resolve the aspect from the skin. - continue; - } - - if ( candidate.aspect != requestState.aspect ) - { - // the aspect was resolved to something else - continue; - } - - addAnimator( control->window(), candidate, animatorHint ); - storeUpdateInfo( control, candidate.aspect ); - } - } - - void addAnimator( QQuickWindow* window, - const AnimatorCandidate& candidate, QskAnimationHint animationHint ) - { - auto it = m_hintAnimatorMap.find( candidate.aspect ); - if ( it != m_hintAnimatorMap.end() ) - return; // already there - - it = m_hintAnimatorMap.emplace( candidate.aspect, QskHintAnimator() ).first; - auto& animator = it->second; - - animator.setAspect( candidate.aspect ); - animator.setStartValue( candidate.from ); - animator.setEndValue( candidate.to ); - - animator.setDuration( animationHint.duration ); - animator.setEasingCurve( animationHint.type ); - animator.setUpdateFlags( animationHint.updateFlags ); - - animator.setControl( nullptr ); - animator.setWindow( window ); - } - - inline void storeUpdateInfo( QskControl* control, QskAspect aspect ) - { - UpdateInfo info; - info.control = control; - - info.updateModes = UpdateInfo::Update; - if ( aspect.isMetric() ) - info.updateModes |= UpdateInfo::Polish; - - auto it = std::lower_bound( - m_updateInfos.begin(), m_updateInfos.end(), info, UpdateInfo::compare ); - - if ( ( it != m_updateInfos.end() ) && ( it->control == info.control ) ) - it->updateModes |= info.updateModes; - else - m_updateInfos.insert( it, info ); - } + void storeUpdateInfo( const QskControl*, QskAspect ); QQuickWindow* m_window; - std::unordered_map< QskAspect, QskHintAnimator > m_hintAnimatorMap; + std::unordered_map< QskAspect, HintAnimator > m_animatorMap; std::unordered_map< int, QskVariantAnimator > m_graphicFilterAnimatorMap; std::vector< UpdateInfo > m_updateInfos; // vector: for fast iteration }; - class AnimatorGroups : public QObject + class ApplicationAnimator : public QObject { Q_OBJECT public: - ~AnimatorGroups() - { - reset(); - } + ~ApplicationAnimator(); - inline AnimatorGroup* animatorGroup( const QQuickWindow* window ) - { - if ( !m_animatorGroups.empty() && window ) - { - for ( auto group : m_animatorGroups ) - { - if ( group->window() == window ) - return group; - } - } + WindowAnimator* windowAnimator( const QQuickWindow* ); - return nullptr; - } + void add( WindowAnimator* ); - void add( AnimatorGroup* group ) - { - m_animatorGroups.push_back( group ); - } - - void start() - { - m_connections[0] = QskAnimator::addAdvanceHandler( - this, SLOT(notify(QQuickWindow*)), Qt::UniqueConnection ); - - m_connections[1] = QskAnimator::addCleanupHandler( - this, SLOT(cleanup(QQuickWindow*)), Qt::UniqueConnection ); - - for ( auto& group : m_animatorGroups ) - group->start(); - } - - void reset() - { - qDeleteAll( m_animatorGroups ); - m_animatorGroups.clear(); - - disconnect( m_connections[0] ); - disconnect( m_connections[1] ); - } - - inline bool isRunning() const - { - return !m_animatorGroups.empty(); - } + void start(); + void reset(); + bool isRunning() const; private Q_SLOTS: - void notify( QQuickWindow* window ) - { - for ( auto& group : m_animatorGroups ) - { - if ( group->window() == window ) - { - group->update(); - return; - } - } - } - - void cleanup( QQuickWindow* window ) - { - for ( auto it = m_animatorGroups.begin(); - it != m_animatorGroups.end(); ++it ) - { - auto group = *it; - if ( group->window() == window ) - { - if ( !group->isRunning() ) - { - // The notification might be for other animators - - m_animatorGroups.erase( it ); - delete group; - } - - break; - } - } - - if ( m_animatorGroups.empty() ) - reset(); - } + // using functor slots ? + void notify( QQuickWindow* ); + void cleanup( QQuickWindow* ); private: /* @@ -518,12 +183,375 @@ namespace But as skin transitions are no operations, that happen often, we can accept the overhaed of the current implementation and do the finetuning later. */ - std::vector< AnimatorGroup* > m_animatorGroups; + std::vector< WindowAnimator* > m_windowAnimators; QMetaObject::Connection m_connections[2]; }; } -Q_GLOBAL_STATIC( AnimatorGroups, qskSkinAnimator ) +Q_GLOBAL_STATIC( ApplicationAnimator, qskApplicationAnimator ) + +WindowAnimator::WindowAnimator( QQuickWindow* window ) + : m_window( window ) +{ +} + +inline const QQuickWindow* WindowAnimator::window() const +{ + return m_window; +} + +void WindowAnimator::start() +{ + for ( auto& it : m_animatorMap ) + it.second.start(); + + for ( auto& it : m_graphicFilterAnimatorMap ) + it.second.start(); +} + +bool WindowAnimator::isRunning() const +{ + if ( !m_animatorMap.empty() ) + { + const auto& animator = m_animatorMap.begin()->second; + if ( animator.isRunning() ) + return true; + } + + if ( !m_graphicFilterAnimatorMap.empty() ) + { + const auto& animator = m_graphicFilterAnimatorMap.begin()->second; + if ( animator.isRunning() ) + return true; + } + + return false; +} + +inline QVariant WindowAnimator::animatedHint( QskAspect aspect ) const +{ + auto it = m_animatorMap.find( aspect ); + if ( it != m_animatorMap.cend() ) + { + const auto& animator = it->second; + if ( animator.isRunning() ) + return animator.currentValue(); + } + + return QVariant(); +} + +inline QVariant WindowAnimator::animatedGraphicFilter( int graphicRole ) const +{ + auto it = m_graphicFilterAnimatorMap.find( graphicRole ); + if ( it != m_graphicFilterAnimatorMap.cend() ) + { + const auto& animator = it->second; + if ( animator.isRunning() ) + return animator.currentValue(); + } + + return QVariant(); +} + +void WindowAnimator::addGraphicFilterAnimators( const QskAnimationHint& animatorHint, + const QskSkin* skin1, const QskSkin* skin2 ) +{ + const QskColorFilter noFilter; + + const auto& filter1 = skin1->graphicFilters(); + const auto& filter2 = skin2->graphicFilters(); + + for ( auto it2 = filter2.begin(); it2 != filter2.end(); ++it2 ) + { + auto it1 = filter1.find( it2->first ); + if ( it1 == filter1.cend() ) + it1 = filter1.find( 0 ); + + const auto& f1 = ( it1 != filter1.cend() ) ? it1->second : noFilter; + const auto& f2 = it2->second; + + if ( f1 != f2 ) + { + QskVariantAnimator animator; + animator.setWindow( m_window ); + animator.setDuration( animatorHint.duration ); + animator.setEasingCurve( animatorHint.type ); + animator.setStartValue( QVariant::fromValue( f1 ) ); + animator.setEndValue( QVariant::fromValue( f2 ) ); + + m_graphicFilterAnimatorMap.emplace( it2->first, animator ); + } + } +} + +void WindowAnimator::addItemAspects( QQuickItem* item, + const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates, + const QskSkin* skin1, const QskSkin* skin2 ) +{ + if ( !item->isVisible() ) + return; + + if ( auto control = qskControlCast( item ) ) + { + if ( control->isInitiallyPainted() && ( control->effectiveSkin() == skin2 ) ) + { + addHints( control, animatorHint, candidates, skin1, skin2 ); +#if 1 + /* + As it is hard to identify which controls depend on the animated + graphic filters we schedule an initial update and let the + controls do the rest: see QskSkinnable::effectiveGraphicFilter + */ + control->update(); +#endif + } + } + + const auto children = item->childItems(); + for ( auto child : children ) + addItemAspects( child, animatorHint, candidates, skin1, skin2 ); +} + +void WindowAnimator::update() +{ + for ( auto& info : m_updateInfos ) + { + if ( auto control = info.control ) + { + if ( info.updateModes & UpdateInfo::Polish ) + { + control->resetImplicitSize(); + control->polish(); + } + + if ( info.updateModes & UpdateInfo::Update ) + control->update(); + } + } +} + +void WindowAnimator::addHints( const QskControl* control, + const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates, + const QskSkin* skin1, const QskSkin* skin2 ) +{ + const auto subControls = control->subControls(); + + const auto& localTable = control->hintTable(); + + const auto& table1 = skin1->hintTable(); + const auto& table2 = skin2->hintTable(); + + for ( auto aspect : candidates ) + { + if ( !isControlAffected( control, subControls, aspect ) ) + continue; + + aspect.setPlacement( control->effectivePlacement() ); + aspect.setStates( control->skinStates() ); + + if ( localTable.resolvedHint( aspect ) ) + { + // value is not from the skin - ignored + continue; + } + + QskAspect r1, r2; + + const auto v1 = table1.resolvedHint( aspect, &r1 ); + const auto v2 = table2.resolvedHint( aspect, &r2 ); + + if ( v1 && v2 ) + { + if ( QskVariantAnimator::maybeInterpolate( *v1, *v2 ) ) + { + if ( r1.placement() == r2.placement() ) + aspect.setPlacement( r2.placement() ); + + if ( r1.states() == r2.states() ) + aspect.setStates( r2.states() ); + + storeAnimator( control, aspect, *v1, *v2, animatorHint ); + storeUpdateInfo( control, aspect ); + } + } + else if ( v1 ) + { + aspect.setPlacement( r1.placement() ); + aspect.setStates( r1.states() ); + + storeAnimator( control, aspect, *v1, QVariant(), animatorHint ); + storeUpdateInfo( control, aspect ); + } + else if ( v2 ) + { + aspect.setPlacement( r1.placement() ); + aspect.setStates( r1.states() ); + + storeAnimator( control, aspect, QVariant(), *v2, animatorHint ); + storeUpdateInfo( control, aspect ); + } + } +} + +inline bool WindowAnimator::isControlAffected( const QskControl* control, + const QVector< QskAspect::Subcontrol >& subControls, const QskAspect aspect ) const +{ + if ( !aspect.isMetric() ) + { + if ( !( control->flags() & QQuickItem::ItemHasContents ) ) + { + // while metrics might have an effect on layouts, we + // ignore all others for controls without content + return false; + } + } + + const auto subControl = aspect.subControl(); + if ( subControl != control->effectiveSubcontrol( subControl ) ) + { + // The control uses subcontrol redirection, so we can assume it + // is not interested in this subcontrol. + + return false; + } + + if ( subControl == QskAspect::Control ) + { + if ( !control->autoFillBackground() ) + { + // no need to animate the background unless we show it + return false; + } + } + else + { + if ( !subControls.contains( subControl ) ) + { + // the control is not interested in the aspect + return false; + } + } + + return true; +} + +inline void WindowAnimator::storeAnimator( const QskControl* control, const QskAspect aspect, + const QVariant& value1, const QVariant& value2, QskAnimationHint hint ) +{ + if ( m_animatorMap.find( aspect ) == m_animatorMap.cend() ) + { + m_animatorMap.emplace( aspect, + HintAnimator( control, aspect, value1, value2, hint ) ); + } +} + +inline void WindowAnimator::storeUpdateInfo( const QskControl* control, QskAspect aspect ) +{ + UpdateInfo info; + info.control = const_cast< QskControl* >( control ); + + info.updateModes = UpdateInfo::Update; + if ( aspect.isMetric() ) + info.updateModes |= UpdateInfo::Polish; + + auto it = std::lower_bound( + m_updateInfos.begin(), m_updateInfos.end(), info, UpdateInfo::compare ); + + if ( ( it != m_updateInfos.end() ) && ( it->control == info.control ) ) + it->updateModes |= info.updateModes; + else + m_updateInfos.insert( it, info ); +} + +ApplicationAnimator::~ApplicationAnimator() +{ + reset(); +} + +inline WindowAnimator* ApplicationAnimator::windowAnimator( const QQuickWindow* window ) +{ + if ( window ) + { + for ( auto animator : m_windowAnimators ) + { + if ( animator->window() == window ) + return animator; + } + } + + return nullptr; +} + +void ApplicationAnimator::add( WindowAnimator* animator ) +{ + m_windowAnimators.push_back( animator ); +} + +void ApplicationAnimator::start() +{ + m_connections[0] = QskAnimator::addAdvanceHandler( + this, SLOT(notify(QQuickWindow*)), Qt::UniqueConnection ); + + m_connections[1] = QskAnimator::addCleanupHandler( + this, SLOT(cleanup(QQuickWindow*)), Qt::UniqueConnection ); + + for ( auto& animator : m_windowAnimators ) + animator->start(); +} + +void ApplicationAnimator::reset() +{ + qDeleteAll( m_windowAnimators ); + m_windowAnimators.clear(); + + disconnect( m_connections[0] ); + disconnect( m_connections[1] ); +} + +inline bool ApplicationAnimator::isRunning() const +{ + return !m_windowAnimators.empty(); +} + +void ApplicationAnimator::notify( QQuickWindow* window ) +{ + for ( auto& animator : m_windowAnimators ) + { + if ( animator->window() == window ) + { + animator->update(); + return; + } + } +} + +void ApplicationAnimator::cleanup( QQuickWindow* window ) +{ + for ( auto it = m_windowAnimators.begin(); + it != m_windowAnimators.end(); ++it ) + { + auto animator = *it; + if ( animator->window() == window ) + { + if ( !animator->isRunning() ) + { + // The notification might be for other animators + + m_windowAnimators.erase( it ); + delete animator; + } + + // let the items know, that we are done + qskSendStyleEventRecursive( window->contentItem() ); + + break; + } + } + + if ( m_windowAnimators.empty() ) + reset(); +} class QskSkinTransition::PrivateData { @@ -589,38 +617,20 @@ void QskSkinTransition::updateSkin( QskSkin*, QskSkin* ) void QskSkinTransition::process() { - auto skinFrom = m_data->skins[ 0 ]; - auto skinTo = m_data->skins[ 1 ]; + qskApplicationAnimator->reset(); - if ( ( skinFrom == nullptr ) || ( skinTo == nullptr ) ) + auto skin1 = m_data->skins[ 0 ]; + auto skin2 = m_data->skins[ 1 ]; + + QSet< QskAspect > candidates; + + if ( skin1 && skin2 ) { - // do nothing - return; - } - - qskSkinAnimator->reset(); - - if ( ( m_data->animationHint.duration <= 0 ) || ( m_data->mask == 0 ) ) - { - // no animations, we can apply the changes - updateSkin( skinFrom, skinTo ); - return; - } - - QVector< AnimatorCandidate > candidates; - const auto oldFilters = skinFrom->graphicFilters(); - - { - // copy out all hints before updating the skin - // - would be good to have Copy on Write here - - const auto oldTable = skinFrom->hintTable(); - - // apply the changes - updateSkin( skinFrom, skinTo ); - - candidates = qskAnimatorCandidates( m_data->mask, oldTable, oldFilters, - skinTo->hintTable(), skinTo->graphicFilters() ); + if ( ( m_data->animationHint.duration > 0 ) && ( m_data->mask != 0 ) ) + { + qskAddCandidates( m_data->mask, skin1, candidates ); + qskAddCandidates( m_data->mask, skin2, candidates ); + } } if ( !candidates.isEmpty() ) @@ -631,21 +641,19 @@ void QskSkinTransition::process() for ( const auto window : windows ) { - if ( auto quickWindow = qobject_cast< QQuickWindow* >( window ) ) + if ( auto w = qobject_cast< QQuickWindow* >( window ) ) { - if ( !quickWindow->isVisible() || - ( qskEffectiveSkin( quickWindow ) != skinTo ) ) + if ( !w->isVisible() || ( qskEffectiveSkin( w ) != skin2 ) ) { continue; } - auto group = new AnimatorGroup( quickWindow ); + auto animator = new WindowAnimator( w ); if ( doGraphicFilter ) { - group->addGraphicFilterAnimators( - m_data->animationHint, oldFilters, - skinTo->graphicFilters() ); + animator->addGraphicFilterAnimators( + m_data->animationHint, skin1, skin2 ); doGraphicFilter = false; } @@ -655,21 +663,24 @@ void QskSkinTransition::process() over the the item trees. */ - group->addAnimators( quickWindow->contentItem(), - m_data->animationHint, candidates, skinTo ); + animator->addItemAspects( w->contentItem(), + m_data->animationHint, candidates, skin1, skin2 ); - qskSkinAnimator->add( group ); + qskApplicationAnimator->add( animator ); } } - qskSkinAnimator->start(); + qskApplicationAnimator->start(); } + + // apply the changes + updateSkin( skin1, skin2 ); } bool QskSkinTransition::isRunning() { - if ( qskSkinAnimator.exists() ) - return qskSkinAnimator->isRunning(); + if ( qskApplicationAnimator.exists() ) + return qskApplicationAnimator->isRunning(); return false; } @@ -677,10 +688,10 @@ bool QskSkinTransition::isRunning() QVariant QskSkinTransition::animatedHint( const QQuickWindow* window, QskAspect aspect ) { - if ( qskSkinAnimator.exists() ) + if ( qskApplicationAnimator.exists() ) { - if ( const auto group = qskSkinAnimator->animatorGroup( window ) ) - return group->animatedHint( aspect ); + if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) ) + return animator->animatedHint( aspect ); } return QVariant(); @@ -689,10 +700,10 @@ QVariant QskSkinTransition::animatedHint( QVariant QskSkinTransition::animatedGraphicFilter( const QQuickWindow* window, int graphicRole ) { - if ( qskSkinAnimator.exists() ) + if ( qskApplicationAnimator.exists() ) { - if ( const auto group = qskSkinAnimator->animatorGroup( window ) ) - return group->animatedGraphicFilter( graphicRole ); + if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) ) + return animator->animatedGraphicFilter( graphicRole ); } return QVariant(); diff --git a/src/controls/QskSkinnable.cpp b/src/controls/QskSkinnable.cpp index 74f5787b..69ce1bea 100644 --- a/src/controls/QskSkinnable.cpp +++ b/src/controls/QskSkinnable.cpp @@ -49,17 +49,6 @@ static inline bool qskIsControl( const QskSkinnable* skinnable ) #endif } -static inline QVariant qskTypedNullValue( const QVariant& value ) -{ -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - const auto vType = static_cast< QMetaType >( value.userType() ); -#else - const auto vType = value.userType(); -#endif - - return QVariant( vType, nullptr ); -} - static inline bool qskSetFlag( QskSkinnable* skinnable, const QskAspect aspect, int flag ) { @@ -951,23 +940,12 @@ const QVariant& QskSkinnable::storedHint( { const auto skin = effectiveSkin(); - // clearing all state bits not being handled from the skin - aspect.clearStates( ~skin->stateMask() ); - QskAspect resolvedAspect; const auto& localTable = m_data->hintTable; if ( localTable.hasHints() ) { - auto a = aspect; - - if ( !localTable.hasStates() ) - { - // we don't need to clear the state bits stepwise - a.clearStates(); - } - - if ( const QVariant* value = localTable.resolvedHint( a, &resolvedAspect ) ) + if ( const auto value = localTable.resolvedHint( aspect, &resolvedAspect ) ) { if ( status ) { @@ -983,10 +961,7 @@ const QVariant& QskSkinnable::storedHint( const auto& skinTable = skin->hintTable(); if ( skinTable.hasHints() ) { - auto a = aspect; - - const QVariant* value = skinTable.resolvedHint( a, &resolvedAspect ); - if ( value ) + if ( const auto value = skinTable.resolvedHint( aspect, &resolvedAspect ) ) { if ( status ) { @@ -1004,8 +979,7 @@ const QVariant& QskSkinnable::storedHint( aspect.setSubControl( QskAspect::Control ); aspect.clearStates(); - value = skinTable.resolvedHint( aspect, &resolvedAspect ); - if ( value ) + if ( const auto value = skinTable.resolvedHint( aspect, &resolvedAspect ) ) { if ( status ) { @@ -1182,28 +1156,12 @@ void QskSkinnable::startHintTransition( QskAspect aspect, if ( control->window() == nullptr || !isTransitionAccepted( aspect ) ) return; - /* - We might be invalid for one of the values, when an aspect - has not been defined for all states ( f.e. metrics are expected - to fallback to 0.0 ). In this case we create a default one. - */ + if ( !QskVariantAnimator::maybeInterpolate( from, to ) ) + return; auto v1 = from; auto v2 = to; - if ( !v1.isValid() ) - { - v1 = qskTypedNullValue( v2 ); - } - else if ( !v2.isValid() ) - { - v2 = qskTypedNullValue( v1 ); - } - else if ( v1.userType() != v2.userType() ) - { - return; - } - if ( aspect.flagPrimitive() == QskAspect::GraphicRole ) { const auto skin = effectiveSkin(); @@ -1268,7 +1226,8 @@ void QskSkinnable::setSkinStates( QskAspect::States newStates ) if ( skin ) { - const auto mask = skin->stateMask(); + const auto mask = skin->hintTable().states() | m_data->hintTable.states(); + if ( ( newStates & mask ) == ( m_data->skinStates & mask ) ) { // the modified bits are not handled by the skin @@ -1308,24 +1267,13 @@ void QskSkinnable::setSkinStates( QskAspect::States newStates ) const auto primitive = static_cast< QskAspect::Primitive >( i ); aspect.setPrimitive( type, primitive ); - auto a1 = aspect | m_data->skinStates; - auto a2 = aspect | newStates; + const auto a1 = aspect | m_data->skinStates; + const auto a2 = aspect | newStates; bool doTransition = true; - if ( !m_data->hintTable.hasStates() ) - { - /* - The hints are found by stripping the state bits one by - one until a lookup into the hint table is successful. - So for deciding whether two aspects lead to the same hint - we can stop as soon as the aspects have the same state bits. - This way we can reduce the number of lookups significantly - for skinnables with many state bits. - - */ + if ( m_data->hintTable.states() == QskAspect::NoState ) doTransition = !skinTable.isResolutionMatching( a1, a2 ); - } if ( doTransition ) { @@ -1354,12 +1302,7 @@ QskSkin* QskSkinnable::effectiveSkin() const if ( skin == nullptr ) { if ( const auto control = owningControl() ) - { - if ( auto window = qobject_cast< const QskWindow* >( control->window() ) ) - { - skin = window->skin(); - } - } + skin = qskEffectiveSkin( control->window() ); } return skin ? skin : qskSetup->skin(); diff --git a/src/controls/QskSkinnable.h b/src/controls/QskSkinnable.h index b73ea956..97324e2c 100644 --- a/src/controls/QskSkinnable.h +++ b/src/controls/QskSkinnable.h @@ -230,6 +230,8 @@ class QSK_EXPORT QskSkinnable bool resetGraphicRoleHint( QskAspect ); int graphicRoleHint( QskAspect, QskSkinHintStatus* = nullptr ) const; + const QskSkinHintTable& hintTable() const; + protected: virtual void updateNode( QSGNode* ); virtual bool isTransitionAccepted( QskAspect ) const; @@ -237,7 +239,6 @@ class QSK_EXPORT QskSkinnable virtual QskAspect::Subcontrol substitutedSubcontrol( QskAspect::Subcontrol ) const; QskSkinHintTable& hintTable(); - const QskSkinHintTable& hintTable() const; private: Q_DISABLE_COPY( QskSkinnable ) diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index ccaa6dd6..8ea80e1b 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -301,6 +301,8 @@ QskTextInput::QskTextInput( QQuickItem* parent ) m_data->hasPanel = true; setPolishOnResize( true ); + + setAcceptHoverEvents( true ); setFocusPolicy( Qt::StrongFocus ); setFlag( QQuickItem::ItemAcceptsInputMethod ); diff --git a/src/controls/QskVariantAnimator.cpp b/src/controls/QskVariantAnimator.cpp index 6372fbf0..c54d4ea3 100644 --- a/src/controls/QskVariantAnimator.cpp +++ b/src/controls/QskVariantAnimator.cpp @@ -53,13 +53,13 @@ Q_CONSTRUCTOR_FUNCTION( qskRegisterInterpolator ) #endif #if defined( Q_CC_CLANG ) -#if __has_feature( address_sanitizer ) -#define QSK_DECL_INSANE __attribute__( ( no_sanitize( "undefined" ) ) ) -#endif + #if __has_feature( address_sanitizer ) + #define QSK_DECL_INSANE __attribute__( ( no_sanitize( "undefined" ) ) ) + #endif #endif #if !defined( QSK_DECL_INSANE ) -#define QSK_DECL_INSANE + #define QSK_DECL_INSANE #endif QSK_DECL_INSANE static inline QVariant qskInterpolate( @@ -76,6 +76,31 @@ QSK_DECL_INSANE static inline QVariant qskInterpolate( return f( from.constData(), to.constData(), progress ); } +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + +using QskMetaType = int; +static inline QskMetaType qskMetaType( const QVariant& v ) { return v.userType(); } + +#else + +using QskMetaType = QMetaType; +static inline QskMetaType qskMetaType( const QVariant& v ) { return v.metaType(); } + +#endif + +static inline QVariant qskDefaultVariant( QskMetaType type ) +{ + return QVariant( type, nullptr ); +} + +static inline QVariant qskConvertedVariant( const QVariant& from, QskMetaType type ) +{ + auto v = from; + v.convert( type ); + + return v; +} + QskVariantAnimator::QskVariantAnimator() : m_interpolator( nullptr ) { @@ -87,11 +112,13 @@ QskVariantAnimator::~QskVariantAnimator() void QskVariantAnimator::setStartValue( const QVariant& value ) { + stop(); m_startValue = value; } void QskVariantAnimator::setEndValue( const QVariant& value ) { + stop(); m_endValue = value; } @@ -100,16 +127,60 @@ void QskVariantAnimator::setCurrentValue( const QVariant& value ) m_currentValue = value; } +bool QskVariantAnimator::convertValues( QVariant& v1, QVariant& v2 ) +{ + if ( !v1.isValid() && !v2.isValid() ) + return false; + + const auto type1 = qskMetaType( v1 ); + const auto type2 = qskMetaType( v2 ); + + if ( !v1.isValid() ) + { + v1 = qskDefaultVariant( type2 ); + return true; + } + + if ( !v2.isValid() ) + { + v2 = qskDefaultVariant( type1 ); + return true; + } + + if ( type1 != type2 ) + { + if ( v1.canConvert( type2 ) ) + { + v1.convert( type2 ); + return true; + } + + if ( v2.canConvert( type1 ) ) + { + v2.convert( type1 ); + return true; + } + + return false; + } + + return true; +} + void QskVariantAnimator::setup() { m_interpolator = nullptr; - const auto type = m_startValue.userType(); - if ( type == m_endValue.userType() ) + if ( convertValues( m_startValue, m_endValue ) ) { - // all what has been registered by qRegisterAnimationInterpolator - m_interpolator = reinterpret_cast< void ( * )() >( - QVariantAnimationPrivate::getInterpolator( type ) ); + if ( m_startValue != m_endValue ) + { + const auto id = m_startValue.userType(); + + // all what has been registered by qRegisterAnimationInterpolator + m_interpolator = reinterpret_cast< void ( * )() >( + QVariantAnimationPrivate::getInterpolator( id ) ); + } } m_currentValue = m_interpolator ? m_startValue : m_endValue; @@ -122,6 +193,8 @@ void QskVariantAnimator::advance( qreal progress ) if ( qFuzzyCompare( progress, 1.0 ) ) progress = 1.0; + Q_ASSERT( qskMetaType( m_startValue ) == qskMetaType( m_endValue ) ); + m_currentValue = qskInterpolate( m_interpolator, m_startValue, m_endValue, progress ); } @@ -131,3 +204,32 @@ void QskVariantAnimator::done() { m_interpolator = nullptr; } + +bool QskVariantAnimator::maybeInterpolate( + const QVariant& value1, const QVariant& value2 ) +{ + if ( !value1.isValid() && !value2.isValid() ) + return false; + + const auto type1 = qskMetaType( value1 ); + const auto type2 = qskMetaType( value2 ); + + if ( !value1.isValid() ) + return value2 != qskDefaultVariant( type2 ); + + if ( !value2.isValid() ) + return value1 != qskDefaultVariant( type1 ); + + if ( type1 != type2 ) + { + if ( value1.canConvert( type2 ) ) + return value2 != qskConvertedVariant( value1, type2 ); + + if ( value2.canConvert( type1 ) ) + return value1 != qskConvertedVariant( value2, type1 ); + + return false; + } + + return value1 != value2; +} diff --git a/src/controls/QskVariantAnimator.h b/src/controls/QskVariantAnimator.h index 1507670d..9e036b06 100644 --- a/src/controls/QskVariantAnimator.h +++ b/src/controls/QskVariantAnimator.h @@ -24,6 +24,9 @@ class QSK_EXPORT QskVariantAnimator : public QskAnimator void setEndValue( const QVariant& ); QVariant endValue() const; + static bool maybeInterpolate( const QVariant&, const QVariant& ); + static bool convertValues( QVariant&, QVariant& ); + protected: void setup() override; void advance( qreal value ) override; diff --git a/src/controls/QskWindow.cpp b/src/controls/QskWindow.cpp index 2f6bd307..a506e308 100644 --- a/src/controls/QskWindow.cpp +++ b/src/controls/QskWindow.cpp @@ -501,15 +501,15 @@ QSize QskWindow::sizeConstraint() const const auto children = contentItem()->childItems(); for ( auto child : children ) { - if ( auto control = qskControlCast( child ) ) + if ( !qskIsTransparentForPositioner( child ) ) { - const QSizeF itemConstraint = control->sizeConstraint(); + const auto size = qskSizeConstraint( child, Qt::PreferredSize ); if ( doWidth ) - constraint.setWidth( qMax( constraint.width(), itemConstraint.width() ) ); + constraint.setWidth( qMax( constraint.width(), size.width() ) ); if ( doHeight ) - constraint.setHeight( qMax( constraint.height(), itemConstraint.height() ) ); + constraint.setHeight( qMax( constraint.height(), size.height() ) ); } } } diff --git a/src/graphic/QskGraphicProviderMap.cpp b/src/graphic/QskGraphicProviderMap.cpp index 1dd7d560..21c01a3c 100644 --- a/src/graphic/QskGraphicProviderMap.cpp +++ b/src/graphic/QskGraphicProviderMap.cpp @@ -48,11 +48,7 @@ void QskGraphicProviderMap::insert( void QskGraphicProviderMap::remove( const QString& providerId ) { - const auto it = m_data->hashTab.find( qskKey( providerId ) ); - if ( it == m_data->hashTab.end() ) - delete it.value(); - - m_data->hashTab.erase( it ); + delete take( providerId ); } QskGraphicProvider* QskGraphicProviderMap::take( const QString& providerId ) @@ -60,10 +56,11 @@ QskGraphicProvider* QskGraphicProviderMap::take( const QString& providerId ) QskGraphicProvider* provider = nullptr; const auto it = m_data->hashTab.find( qskKey( providerId ) ); - if ( it == m_data->hashTab.end() ) + if ( it != m_data->hashTab.end() ) + { provider = it.value(); - - m_data->hashTab.erase( it ); + m_data->hashTab.erase( it ); + } return provider; } diff --git a/src/graphic/QskGraphicProviderMap.h b/src/graphic/QskGraphicProviderMap.h index 119dab2c..607217cf 100644 --- a/src/graphic/QskGraphicProviderMap.h +++ b/src/graphic/QskGraphicProviderMap.h @@ -12,7 +12,7 @@ class QskGraphicProvider; class QString; -class QSK_EXPORT QskGraphicProviderMap +class QskGraphicProviderMap { public: QskGraphicProviderMap(); diff --git a/src/inputpanel/QskPinyinTextPredictor.cpp b/src/inputpanel/QskPinyinTextPredictor.cpp index 4c849ab6..b26d04d5 100644 --- a/src/inputpanel/QskPinyinTextPredictor.cpp +++ b/src/inputpanel/QskPinyinTextPredictor.cpp @@ -102,3 +102,5 @@ void QskPinyinTextPredictor::request( const QString& text ) m_data->candidates = candidates; Q_EMIT predictionChanged(); } + +#include "moc_QskPinyinTextPredictor.cpp" diff --git a/src/inputpanel/QskPinyinTextPredictor.h b/src/inputpanel/QskPinyinTextPredictor.h index 09ad51c8..fa671d2c 100644 --- a/src/inputpanel/QskPinyinTextPredictor.h +++ b/src/inputpanel/QskPinyinTextPredictor.h @@ -12,6 +12,8 @@ class QSK_INPUTCONTEXT_EXPORT QskPinyinTextPredictor : public QskTextPredictor { + Q_OBJECT + using Inherited = QskTextPredictor; public: diff --git a/src/src.pro b/src/src.pro index a25be7da..2f51cb2b 100644 --- a/src/src.pro +++ b/src/src.pro @@ -63,7 +63,6 @@ SOURCES += \ common/QskScaleTickmarks.cpp \ common/QskShadowMetrics.cpp \ common/QskSizePolicy.cpp \ - common/QskStateCombination.cpp \ common/QskTextColors.cpp \ common/QskTextOptions.cpp @@ -142,6 +141,8 @@ HEADERS += \ controls/QskBoundedValueInput.h \ controls/QskBox.h \ controls/QskBoxSkinlet.h \ + controls/QskCheckBox.h \ + controls/QskCheckBoxSkinlet.h \ controls/QskControl.h \ controls/QskControlPrivate.h \ controls/QskDirtyItemFilter.h \ @@ -222,6 +223,8 @@ SOURCES += \ controls/QskBoundedValueInput.cpp \ controls/QskBox.cpp \ controls/QskBoxSkinlet.cpp \ + controls/QskCheckBox.cpp \ + controls/QskCheckBoxSkinlet.cpp \ controls/QskControl.cpp \ controls/QskControlPrivate.cpp \ controls/QskDirtyItemFilter.cpp \ diff --git a/support/SkinnyFont.cpp b/support/SkinnyFont.cpp deleted file mode 100644 index 7278c388..00000000 --- a/support/SkinnyFont.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the 3-clause BSD License - *****************************************************************************/ - -#include "SkinnyFont.h" - -#include -#include -#include -#include - -#define STRINGIFY(x) #x -#define STRING(x) STRINGIFY(x) - -void SkinnyFont::init( QGuiApplication* ) -{ -#ifdef FONTCONFIG_FILE - const char env[] = "FONTCONFIG_FILE"; - if ( !qEnvironmentVariableIsSet( env ) ) - qputenv( env, STRING( FONTCONFIG_FILE ) ); -#endif - - QElapsedTimer timer; - timer.start(); - - QFontDatabase(); - - const auto elapsed = timer.elapsed(); - - if ( elapsed > 20 ) - { - qWarning() << "Loading fonts needed" << elapsed << "ms" - << "- usually because of creating a font cache."; - } - - /* - The default initialization in QskSkin sets up its font table - with using the application font for the default font role. - */ - QGuiApplication::setFont( QFont( "DejaVuSans", 12 ) ); -} - diff --git a/support/SkinnyGlobal.h b/support/SkinnyGlobal.h index 4f5a9a17..9c350ab8 100644 --- a/support/SkinnyGlobal.h +++ b/support/SkinnyGlobal.h @@ -3,23 +3,20 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#ifndef SKINNY_GLOBAL_H -#define SKINNY_GLOBAL_H +#pragma once #include #ifdef QSK_DLL -#if defined( SKINNY_MAKEDLL ) // create a DLL library -#define SKINNY_EXPORT Q_DECL_EXPORT -#else // use a DLL library -#define SKINNY_EXPORT Q_DECL_IMPORT -#endif + #if defined( SKINNY_MAKEDLL ) // create a DLL library + #define SKINNY_EXPORT Q_DECL_EXPORT + #else // use a DLL library + #define SKINNY_EXPORT Q_DECL_IMPORT + #endif #endif // QSK_DLL #ifndef SKINNY_EXPORT -#define SKINNY_EXPORT -#endif - + #define SKINNY_EXPORT #endif diff --git a/support/SkinnyNamespace.cpp b/support/SkinnyNamespace.cpp new file mode 100644 index 00000000..baf2d6a6 --- /dev/null +++ b/support/SkinnyNamespace.cpp @@ -0,0 +1,140 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#include "SkinnyNamespace.h" + +#include +#include +#include +#include +#include + +#include +#include + +#define STRINGIFY(x) #x +#define STRING(x) STRINGIFY(x) + +#if defined( ENSURE_SKINS ) + + #include + #include + + static void initSkins() + { + if ( qskSkinManager->skinNames().isEmpty() ) + { + /* + To avoid having problems with not finding the skin plugins + we manually add them here. + */ + + qskSkinManager->registerFactory( "SquiekFactory", new QskSquiekSkinFactory() ); + qskSkinManager->registerFactory( "MaterialFactory", new QskMaterialSkinFactory() ); + + qWarning() << "Couldn't find skin plugins, adding some manually."; + } + } + + Q_COREAPP_STARTUP_FUNCTION( initSkins ) + +#endif + +#define ENSURE_FONTS + +#if defined( ENSURE_FONTS ) + + #include + #include + + static void initFonts() + { + #ifdef FONTCONFIG_FILE + const char env[] = "FONTCONFIG_FILE"; + if ( !qEnvironmentVariableIsSet( env ) ) + qputenv( env, STRING( FONTCONFIG_FILE ) ); + #endif + + QElapsedTimer timer; + timer.start(); + + QFontDatabase(); + + const auto elapsed = timer.elapsed(); + + if ( elapsed > 20 ) + { + qWarning() << "Loading fonts needed" << elapsed << "ms" + << "- usually because of creating a font cache."; + } + + /* + The default initialization in QskSkin sets up its font table + with using the application font for the default font role. + */ + QGuiApplication::setFont( QFont( "DejaVuSans", 12 ) ); + } +#endif + +Q_COREAPP_STARTUP_FUNCTION( initFonts ) + +void Skinny::changeSkin( QskAnimationHint hint ) +{ + const auto names = qskSkinManager->skinNames(); + if ( names.size() <= 1 ) + return; + + int index = names.indexOf( qskSetup->skinName() ); + index = ( index + 1 ) % names.size(); + + auto oldSkin = qskSetup->skin(); + if ( oldSkin->parent() == qskSetup ) + oldSkin->setParent( nullptr ); // otherwise setSkin deletes it + + if ( auto newSkin = qskSetup->setSkin( names[ index ] ) ) + { + QskSkinTransition transition; + + //transition.setMask( QskAspect::Color ); // Metrics are flickering -> TODO + transition.setSourceSkin( oldSkin ); + transition.setTargetSkin( newSkin ); + transition.setAnimation( hint ); + + transition.process(); + + if ( oldSkin->parent() == nullptr ) + delete oldSkin; + } +} + +void Skinny::changeFonts( int increment ) +{ + auto skin = qskSetup->skin(); + + const auto fonts = skin->fonts(); + + for ( auto it = fonts.begin(); it != fonts.end(); ++it ) + { + auto role = it->first; + auto font = it->second; + + if ( font.pixelSize() > 0 ) + { + const auto newSize = font.pixelSize() + increment; + if ( newSize > 0 ) + font.setPixelSize( newSize ); + } + else + { + const auto newSize = font.pointSizeF() + increment; + if ( newSize > 0 ) + font.setPointSizeF( font.pointSizeF() + increment ); + } + + skin->setFont( role, font ); + } + + Q_EMIT qskSetup->skinChanged( skin ); +} diff --git a/support/SkinnyFont.h b/support/SkinnyNamespace.h similarity index 63% rename from support/SkinnyFont.h rename to support/SkinnyNamespace.h index db2675a9..072e60b2 100644 --- a/support/SkinnyFont.h +++ b/support/SkinnyNamespace.h @@ -3,16 +3,13 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#ifndef SKINNY_FONT_H_ -#define SKINNY_FONT_H_ +#pragma once #include "SkinnyGlobal.h" +#include -class QGuiApplication; - -namespace SkinnyFont +namespace Skinny { - SKINNY_EXPORT void init( QGuiApplication* ); + SKINNY_EXPORT void changeSkin( QskAnimationHint hint = 500 ); + SKINNY_EXPORT void changeFonts( int increment ); } - -#endif diff --git a/support/SkinnyShapeFactory.h b/support/SkinnyShapeFactory.h index 3cbb591d..42794218 100644 --- a/support/SkinnyShapeFactory.h +++ b/support/SkinnyShapeFactory.h @@ -3,8 +3,7 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#ifndef SKINNY_SHAPE_FACTORY_H_ -#define SKINNY_SHAPE_FACTORY_H_ +#pragma once #include "SkinnyGlobal.h" #include @@ -32,5 +31,3 @@ namespace SkinnyShapeFactory SKINNY_EXPORT QPainterPath shapePath( Shape, const QSizeF& ); } - -#endif diff --git a/support/SkinnyShapeProvider.h b/support/SkinnyShapeProvider.h index 3b5f7962..fb0a8155 100644 --- a/support/SkinnyShapeProvider.h +++ b/support/SkinnyShapeProvider.h @@ -3,8 +3,7 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#ifndef SKINNY_SHAPE_PROVIDER_H -#define SKINNY_SHAPE_PROVIDER_H +#pragma once #include "SkinnyGlobal.h" #include @@ -15,5 +14,3 @@ class SKINNY_EXPORT SkinnyShapeProvider : public QskGraphicProvider const QskGraphic* loadGraphic( const QString& id ) const override final; }; -#endif - diff --git a/support/SkinnyShortcut.cpp b/support/SkinnyShortcut.cpp index 670405ca..6226f559 100644 --- a/support/SkinnyShortcut.cpp +++ b/support/SkinnyShortcut.cpp @@ -1,15 +1,11 @@ #include "SkinnyShortcut.h" +#include "SkinnyNamespace.h" #include #include -#include #include -#include -#include #include #include -#include -#include #include #include @@ -17,7 +13,6 @@ #include #include -#include #include SkinnyShortcut::SkinnyShortcut( QObject* parent ): @@ -34,17 +29,17 @@ void SkinnyShortcut::enable( Types types ) if ( types & RotateSkin ) { QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL | Qt::Key_S ), - false, &s_shortcut, &SkinnyShortcut::rotateSkin ); + false, &s_shortcut, [] { Skinny::changeSkin(); } ); cout << "CTRL-S to change the skin." << endl; } if ( types & ChangeFonts ) { QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL | Qt::Key_F ), - false, &s_shortcut, [] { s_shortcut.changeFonts( +1 ); } ); + false, &s_shortcut, [] { Skinny::changeFonts( +1 ); } ); QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL | Qt::Key_G ), - false, &s_shortcut, [] { s_shortcut.changeFonts( -1 ); } ); + false, &s_shortcut, [] { Skinny::changeFonts( -1 ); } ); cout << "CTRL-F to increase the font size." << endl; cout << "CTRL-G to decrease the font size." << endl; @@ -75,34 +70,6 @@ void SkinnyShortcut::enable( Types types ) } } -void SkinnyShortcut::rotateSkin() -{ - const QStringList names = qskSkinManager->skinNames(); - if ( names.size() <= 1 ) - return; - - int index = names.indexOf( qskSetup->skinName() ); - index = ( index + 1 ) % names.size(); - - QskSkin* oldSkin = qskSetup->skin(); - if ( oldSkin->parent() == qskSetup ) - oldSkin->setParent( nullptr ); // otherwise setSkin deletes it - - QskSkin* newSkin = qskSetup->setSkin( names[ index ] ); - - QskSkinTransition transition; - - //transition.setMask( QskAspect::Color ); // Metrics are flickering -> TODO - transition.setSourceSkin( oldSkin ); - transition.setTargetSkin( newSkin ); - transition.setAnimation( 500 ); - - transition.process(); - - if ( oldSkin->parent() == nullptr ) - delete oldSkin; -} - void SkinnyShortcut::showBackground() { #if 0 @@ -148,36 +115,6 @@ void SkinnyShortcut::showBackground() } } -void SkinnyShortcut::changeFonts( int increment ) -{ - auto skin = qskSetup->skin(); - - const auto fonts = skin->fonts(); - - for ( auto it = fonts.begin(); it != fonts.end(); ++it ) - { - auto role = it->first; - auto font = it->second; - - if ( font.pixelSize() > 0 ) - { - const auto newSize = font.pixelSize() + increment; - if ( newSize > 0 ) - font.setPixelSize( newSize ); - } - else - { - const auto newSize = font.pointSizeF() + increment; - if ( newSize > 0 ) - font.setPointSizeF( font.pointSizeF() + increment ); - } - - skin->setFont( role, font ); - } - - Q_EMIT qskSetup->skinChanged( skin ); -} - static inline void countNodes( const QSGNode* node, int& counter ) { if ( node ) diff --git a/support/SkinnyShortcut.h b/support/SkinnyShortcut.h index 52222f18..8f63781f 100644 --- a/support/SkinnyShortcut.h +++ b/support/SkinnyShortcut.h @@ -3,8 +3,7 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ -#ifndef SKINNY_SHORTCUT_H_ -#define SKINNY_SHORTCUT_H_ +#pragma once #include "SkinnyGlobal.h" #include @@ -34,12 +33,8 @@ class SKINNY_EXPORT SkinnyShortcut : public QObject private: SkinnyShortcut( QObject* parent = nullptr ); - void rotateSkin(); - void changeFonts( int increment ); void showBackground(); void debugStatistics(); }; Q_DECLARE_OPERATORS_FOR_FLAGS( SkinnyShortcut::Types ) - -#endif diff --git a/support/support.pro b/support/support.pro index 699b1f2b..b02cb0d9 100644 --- a/support/support.pro +++ b/support/support.pro @@ -12,13 +12,13 @@ contains(QSK_CONFIG, QskDll): DEFINES += SKINNY_MAKEDLL HEADERS += \ SkinnyGlobal.h \ - SkinnyFont.h \ + SkinnyNamespace.h \ SkinnyShapeFactory.h \ SkinnyShapeProvider.h \ SkinnyShortcut.h SOURCES += \ - SkinnyFont.cpp \ + SkinnyNamespace.cpp \ SkinnyPlugin.cpp \ SkinnyShapeFactory.cpp \ SkinnyShapeProvider.cpp \