diff --git a/examples/touch/content/IconPage.qml b/examples/touch/content/IconPage.qml new file mode 100644 index 0000000..0edbbe0 --- /dev/null +++ b/examples/touch/content/IconPage.qml @@ -0,0 +1,68 @@ +/**************************************************************************************** +** +** Copyright (C) 2017 Eetu Kahelin +** All rights reserved. +** +** You may use this file under the terms of BSD license as follows: +** +** 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 author 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 HOLDERS 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. +** +****************************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 1.0 //needed for the Stack attached property +import QtQuick.Controls.Nemo 1.0 +import QtQuick.Controls.Styles.Nemo 1.0 + +Page { + id: root + + headerTools: HeaderToolsLayout { showBackButton: true; title: "Icon example" } + allowedOrientations: Qt.PortraitOrientation | Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation | Qt.InvertedPortraitOrientation + + Column { + spacing: Theme.itemSpacingMedium + anchors.centerIn: parent + //fontawesome + IconButton { + source: "image://theme/compass" + } + //fontawesome + IconButton { + width: height + height: Theme.itemHeightExtraLarge + source: "image://theme/bell" + enabled:false + } + //fontawesome + IconButton { + source: "image://theme/address-book?" + Theme.fillColor + } + //glacier + IconButton { + width: height + height: Theme.itemHeightExtraLarge + source: "image://theme/icon-m-framework-close-thumbnail" + } + } +} diff --git a/examples/touch/main.qml b/examples/touch/main.qml index d26a68d..bde1128 100644 --- a/examples/touch/main.qml +++ b/examples/touch/main.qml @@ -116,6 +116,10 @@ ApplicationWindow { title: "Query Dialog" page: "content/QueryDialogPage.qml" } + ListElement { + title: "Icons" + page: "content/IconPage.qml" + } } diff --git a/examples/touch/touch.pro b/examples/touch/touch.pro index f57cd45..9546e39 100644 --- a/examples/touch/touch.pro +++ b/examples/touch/touch.pro @@ -24,29 +24,15 @@ qml.files += \ content/ButtonRowPage.qml \ content/QueryDialogPage.qml \ content/ListViewPage.qml \ - content/SelectRollerPage.qml + content/SelectRollerPage.qml \ + content/IconPage.qml qml.path = /usr/share/glacier-components/content images.files = images/*.png images.path = /usr/share/glacier-components/images -OTHER_FILES += \ - main.qml \ - content/AndroidDelegate.qml \ - content/ButtonPage.qml \ - content/ProgressBarPage.qml \ - content/SliderPage.qml \ - content/TabBarPage.qml \ - content/TextInputPage.qml \ - content/LiveCoding.qml \ - content/SpinnerPage.qml \ - content/LabelPage.qml \ - content/CheckboxPage.qml \ - content/ButtonRowPage.qml \ - content/QueryDialogPage.qml \ - content/ListViewPage.qml \ - content/SelectRollerPage.qml +OTHER_FILES += $$qml.files desktop.path = /usr/share/applications diff --git a/src/controls/ApplicationWindow.qml b/src/controls/ApplicationWindow.qml index 5fda89e..29485eb 100644 --- a/src/controls/ApplicationWindow.qml +++ b/src/controls/ApplicationWindow.qml @@ -1,5 +1,7 @@ /* * Copyright (C) 2013 Andrea Bernabei + * Copyright (C) 2013 Jolla Ltd. + * Copyright (C) 2017 Eetu Kahelin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -28,15 +30,14 @@ import QtQuick.Controls.Styles.Nemo 1.0 NemoWindow { id: root - width: 320 - height: 640 - property alias header: toolBar /*! \internal */ default property alias data: contentArea.data property alias pageStack: stackView property alias initialPage: stackView.initialItem + property bool applicationActive: Qt.application.active + property alias orientation: contentArea.uiOrientation readonly property int isUiPortrait: orientation == Qt.PortraitOrientation || orientation == Qt.InvertedPortraitOrientation @@ -140,294 +141,376 @@ NemoWindow { Item { id: backgroundItem - - anchors.centerIn: parent - // NOTE: Using Screen.height/width will cause issues when the app is not fullscreen (e.g. when testing using Qt desktop) - // because in that case the app window will be smaller but the content will still be for fullscreen size - width: __transpose ? Screen.height : Screen.width - height: __transpose ? Screen.width : Screen.height + anchors.fill: parent rotation: rotationToTransposeToPortrait() - //This is the rotating item Item { - id: contentArea - width: parent.width - height: parent.height - anchors.centerIn: parent - - property int _horizontalDimension: parent ? parent.width : 0 - property int _verticalDimension: parent ? parent.height : 0 - - property alias defaultOrientationTransition: orientationState.defaultTransition - - // This is used for states switching - property int filteredOrientation - - //this is the reliable value which changes during the orientation transition - property int uiOrientation - - property bool orientationTransitionRunning: false - - StackView { - id: stackView - anchors.top: root.isUiPortrait ? toolBar.bottom : parent.top - anchors.right: parent.right - anchors.left: root.isUiPortrait ? parent.left : toolBar.right - anchors.bottom: parent.bottom - - clip: true - Component.onCompleted: stackInitialized = true - //IMPORTANT: this property makes it so that at app startup we wait for the initial page to be pushed - //before determining the initial ui orientation (see the states logic below) - //If we don't use this, the orientation will change first time based on NemoWindow's allowedOrientation, - //and the second time based on the allowedOrientations of the initialItem of the stack. - //Using this property avoids that, and make the UI directly start in the correct orientation - //TODO: find a cleaner way to do it (if there's any) - property bool stackInitialized: false - onStackInitializedChanged: if (stackInitialized) { - //set Screen.orientation as default, if allowed - if (root.isOrientationAllowed(Screen.orientation)) { - contentArea.filteredOrientation = Screen.orientation - } else { - //let the window handle it, it will fall back to an allowed orientation - root.fallbackToAnAllowedOrientation() - } - } - - //this has to be a function, property won't work inside onCurrentItemChanged, as the property binding hasn't been updated yet there - //(hence we'd be using the old currentItem) - function _isCurrentItemNemoPage() - { - return currentItem && currentItem.hasOwnProperty("__isNemoPage") - } - - //update orientation constraints when a Page is pushed/popped - onCurrentItemChanged: { - if (_isCurrentItemNemoPage()) - root.orientationConstraintsChanged() - } + id: clipping - //This properties are accessible for free by the Page via Stack.view. - readonly property int orientation: contentArea.uiOrientation - property alias allowedOrientations: root.allowedOrientations - property alias orientationTransitionRunning: contentArea.orientationTransitionRunning + z: 1 + width: parent.width - (isUiLandscape ? stackView.panelSize : 0) + height: parent.height - (isUiPortrait ? stackView.panelSize : 0) + clip: stackView.panelSize > 0 - Connections { - id: pageConn - target: stackView._isCurrentItemNemoPage() ? stackView.currentItem : null - onAllowedOrientationsChanged: root.orientationConstraintsChanged() + //This is the rotating item + Item { + id: contentArea + anchors.centerIn: parent + + transform: Scale { + id: contentScale + property bool animationRunning: xAnim.running || yAnim.running + Behavior on xScale { NumberAnimation { id: xAnim; duration: 100 } } + Behavior on yScale { NumberAnimation { id: yAnim; duration: 100 } } } - delegate: StackViewDelegate { - pushTransition: Component { - StackViewTransition { - PropertyAnimation { - target: enterItem - property: "x" - from: target.width - to: 0 - duration: 500 - easing.type: Easing.OutQuad - } - PropertyAnimation { - target: exitItem - property: "x" - from: 0 - to: -target.width - duration: 500 - easing.type: Easing.OutQuad - } + property int _horizontalDimension: parent ? parent.width : 0 + property int _verticalDimension: parent ? parent.height : 0 + + property alias defaultOrientationTransition: orientationState.defaultTransition + + // This is used for states switching + property int filteredOrientation + + //this is the reliable value which changes during the orientation transition + property int uiOrientation + + property bool orientationTransitionRunning: false + + StackView { + id: stackView + anchors.top: root.isUiPortrait ? toolBar.bottom : parent.top + anchors.right: parent.right + anchors.left: root.isUiPortrait ? parent.left : toolBar.right + anchors.bottom: parent.bottom + + property real panelSize: 0 + property real previousImSize: 0 + property real imSize: !root.applicationActive ? 0 : (isUiPortrait ? (root._transpose ? Qt.inputMethod.keyboardRectangle.width + : Qt.inputMethod.keyboardRectangle.height) + : (root._transpose ? Qt.inputMethod.keyboardRectangle.height + : Qt.inputMethod.keyboardRectangle.width)) + onImSizeChanged: { + if (imSize <= 0 && previousImSize > 0) { + imShowAnimation.stop() + imHideAnimation.start() + } else if (imSize > 0 && previousImSize <= 0) { + imHideAnimation.stop() + imShowAnimation.to = imSize + imShowAnimation.start() + } else { + panelSize = imSize } + + previousImSize = imSize } - popTransition: Component { - StackViewTransition { - PropertyAnimation { - target: enterItem - property: "x" - from: -target.width - to: 0 - duration: 500 - easing.type: Easing.OutQuad - } - PropertyAnimation { - target: exitItem - property: "x" - from: 0 - to: target.width - duration: 500 - easing.type: Easing.OutQuad - } - } + + clip: true + Component.onCompleted: { + stackInitialized = true } + //IMPORTANT: this property makes it so that at app startup we wait for the initial page to be pushed + //before determining the initial ui orientation (see the states logic below) + //If we don't use this, the orientation will change first time based on NemoWindow's allowedOrientation, + //and the second time based on the allowedOrientations of the initialItem of the stack. + //Using this property avoids that, and make the UI directly start in the correct orientation + //TODO: find a cleaner way to do it (if there's any) + property bool stackInitialized: false + onStackInitializedChanged: if (stackInitialized) { + //set Screen.orientation as default, if allowed + if (root.isOrientationAllowed(Screen.orientation)) { + contentArea.filteredOrientation = Screen.orientation + } else { + //let the window handle it, it will fall back to an allowed orientation + root.fallbackToAnAllowedOrientation() + } + } - } - } + //this has to be a function, property won't work inside onCurrentItemChanged, as the property binding hasn't been updated yet there + //(hence we'd be using the old currentItem) + function _isCurrentItemNemoPage() + { + return currentItem && currentItem.hasOwnProperty("__isNemoPage") + } - Header { - id: toolBar - stackView: root.pageStack - appWindow: root + //update orientation constraints when a Page is pushed/popped + onCurrentItemChanged: { + if (_isCurrentItemNemoPage()) + root.orientationConstraintsChanged() + } - //used to animate the dimmer when pages are pushed/popped (see Header's QML code) - property alias __dimmer: headerDimmerContainer - } + //This properties are accessible for free by the Page via Stack.view. + readonly property int orientation: contentArea.uiOrientation + property alias allowedOrientations: root.allowedOrientations + property alias orientationTransitionRunning: contentArea.orientationTransitionRunning - Item { - //This item handles the rotation of the dimmer. - //All this because QML doesn't have a horizontal gradient (unless you import GraphicalEffects) - //and having a container which doesn't rotate but just resizes makes it easier to rotate its inner - //child - id: headerDimmerContainer - - //README: Don't use AnchorChanges for this item! - //Reason: state changes disable bindings while the transition from one state to another is running. - //This causes the dimmer not to follow the drawer when the drawer is closed right before the orientation change - anchors.top: isUiPortrait ? toolBar.bottom : parent.top - anchors.left: isUiPortrait ? parent.left : toolBar.right - anchors.right: isUiPortrait ? parent.right : undefined - anchors.bottom: isUiPortrait ? undefined : parent.bottom - //we only set the size in one orientation, the anchors will take care of the other - width: if (!isUiPortrait) Theme.itemHeightExtraSmall/2 - height: if (isUiPortrait) Theme.itemHeightExtraSmall/2 - //MAKE SURE THAT THE HEIGHT SPECIFIED BY THE THEME IS AN EVEN NUMBER, TO AVOID ROUNDING ERRORS IN THE LAYOUT - - Rectangle { - id: headerDimmer - anchors.centerIn: parent - gradient: Gradient { - GradientStop { position: 0; color: Theme.backgroundColor } - GradientStop { position: 1; color: "transparent" } + Connections { + id: pageConn + target: stackView._isCurrentItemNemoPage() ? stackView.currentItem : null + onAllowedOrientationsChanged: root.orientationConstraintsChanged() } - } - } - Item { - id: orientationState - - state: 'Unanimated' - - states: [ - State { - name: 'Unanimated' - when: !stackView || !stackInitialized - }, - State { - name: 'Portrait' - when: contentArea.filteredOrientation === Qt.PortraitOrientation// && stackInitialized - PropertyChanges { - target: contentArea - restoreEntryValues: false - width: _horizontalDimension - height: _verticalDimension - rotation: 0 - uiOrientation: Qt.PortraitOrientation - } - PropertyChanges { - target: headerDimmer - height: Theme.itemHeightExtraSmall/2 - width: parent.width - rotation: 0 - } - }, - State { - name: 'Landscape' - when: contentArea.filteredOrientation === Qt.LandscapeOrientation //&& stackInitialized - PropertyChanges { - target: contentArea - restoreEntryValues: false - width: _verticalDimension - height: _horizontalDimension - rotation: 90 - uiOrientation: Qt.LandscapeOrientation - } - PropertyChanges { - target: headerDimmer - height: Theme.itemHeightExtraSmall/2 - width: parent.height - rotation: -90 - } - }, - State { - name: 'PortraitInverted' - when: contentArea.filteredOrientation === Qt.InvertedPortraitOrientation //&& stackInitialized - PropertyChanges { - target: contentArea - restoreEntryValues: false - width: _horizontalDimension - height: _verticalDimension - rotation: 180 - uiOrientation: Qt.InvertedPortraitOrientation - } - PropertyChanges { - target: headerDimmer - height: Theme.itemHeightExtraSmall/2 - width: parent.width - rotation: 0 - } - }, - State { - name: 'LandscapeInverted' - when: contentArea.filteredOrientation === Qt.InvertedLandscapeOrientation //&& stackInitialized - PropertyChanges { - target: contentArea - restoreEntryValues: false - width: _verticalDimension - height: _horizontalDimension - rotation: 270 - uiOrientation: Qt.InvertedLandscapeOrientation + delegate: StackViewDelegate { + pushTransition: Component { + StackViewTransition { + PropertyAnimation { + target: enterItem + property: "x" + from: target.width + to: 0 + duration: 500 + easing.type: Easing.OutQuad + } + PropertyAnimation { + target: exitItem + property: "x" + from: 0 + to: -target.width + duration: 500 + easing.type: Easing.OutQuad + } + } } - PropertyChanges { - target: headerDimmer - height: Theme.itemHeightExtraSmall/2 - width: parent.height - rotation: -90 + popTransition: Component { + StackViewTransition { + PropertyAnimation { + target: enterItem + property: "x" + from: -target.width + to: 0 + duration: 500 + easing.type: Easing.OutQuad + } + PropertyAnimation { + target: exitItem + property: "x" + from: 0 + to: target.width + duration: 500 + easing.type: Easing.OutQuad + } + } } - } - ] - property Transition defaultTransition: Transition { - to: 'Portrait,Landscape,PortraitInverted,LandscapeInverted' - from: 'Portrait,Landscape,PortraitInverted,LandscapeInverted' + } SequentialAnimation { - PropertyAction { - target: contentArea - property: 'orientationTransitionRunning' - value: true + id: imHideAnimation + PauseAnimation { + duration: 200 } NumberAnimation { - target: contentArea - property: 'opacity' + target: stackView + property: 'panelSize' to: 0 - duration: 150 + duration:200 + easing.type: Easing.InOutQuad } - PropertyAction { - target: contentArea - properties: 'width,height,rotation,uiOrientation' - } - AnchorAnimation { - duration: 0 - } - PropertyAction { - target: headerDimmer - properties: 'width,height,rotation' + } + + NumberAnimation { + id: imShowAnimation + target: stackView + property: 'panelSize' + duration: 200 + easing.type: Easing.InOutQuad + } + + } + + Header { + id: toolBar + stackView: root.pageStack + appWindow: root + + //used to animate the dimmer when pages are pushed/popped (see Header's QML code) + property alias __dimmer: headerDimmerContainer + } + + Item { + //This item handles the rotation of the dimmer. + //All this because QML doesn't have a horizontal gradient (unless you import GraphicalEffects) + //and having a container which doesn't rotate but just resizes makes it easier to rotate its inner + //child + id: headerDimmerContainer + + //README: Don't use AnchorChanges for this item! + //Reason: state changes disable bindings while the transition from one state to another is running. + //This causes the dimmer not to follow the drawer when the drawer is closed right before the orientation change + anchors.top: isUiPortrait ? toolBar.bottom : parent.top + anchors.left: isUiPortrait ? parent.left : toolBar.right + anchors.right: isUiPortrait ? parent.right : undefined + anchors.bottom: isUiPortrait ? undefined : parent.bottom + //we only set the size in one orientation, the anchors will take care of the other + width: if (!isUiPortrait) Theme.itemHeightExtraSmall/2 + height: if (isUiPortrait) Theme.itemHeightExtraSmall/2 + //MAKE SURE THAT THE HEIGHT SPECIFIED BY THE THEME IS AN EVEN NUMBER, TO AVOID ROUNDING ERRORS IN THE LAYOUT + + Rectangle { + id: headerDimmer + anchors.centerIn: parent + gradient: Gradient { + GradientStop { position: 0; color: Theme.backgroundColor } + GradientStop { position: 1; color: "transparent" } } - NumberAnimation { - target: contentArea - property: 'opacity' - to: 1 - duration: 150 + } + } + + Item { + id: orientationState + + state: 'Unanimated' + + states: [ + State { + name: 'Unanimated' + when: !stackView || !stackInitialized + }, + State { + name: 'Portrait' + when: contentArea.filteredOrientation === Qt.PortraitOrientation// && stackInitialized + PropertyChanges { + target: contentArea + restoreEntryValues: false + width: _horizontalDimension + height: _verticalDimension + rotation: 0 + uiOrientation: Qt.PortraitOrientation + } + PropertyChanges { + target: headerDimmer + height: Theme.itemHeightExtraSmall/2 + width: parent.width + rotation: 0 + } + AnchorChanges { + target: clipping + anchors.top: parent.top + anchors.left: parent.left + anchors.right: undefined + anchors.bottom: undefined + } + }, + State { + name: 'Landscape' + when: contentArea.filteredOrientation === Qt.LandscapeOrientation //&& stackInitialized + PropertyChanges { + target: contentArea + restoreEntryValues: false + width: _verticalDimension + height: _horizontalDimension + rotation: 90 + uiOrientation: Qt.LandscapeOrientation + } + PropertyChanges { + target: headerDimmer + height: Theme.itemHeightExtraSmall/2 + width: parent.height + rotation: -90 + } + AnchorChanges { + target: clipping + anchors.top: undefined + anchors.left: undefined + anchors.right: parent.right + anchors.bottom: parent.bottom + } + }, + State { + name: 'PortraitInverted' + when: contentArea.filteredOrientation === Qt.InvertedPortraitOrientation //&& stackInitialized + PropertyChanges { + target: contentArea + restoreEntryValues: false + width: _horizontalDimension + height: _verticalDimension + rotation: 180 + uiOrientation: Qt.InvertedPortraitOrientation + } + PropertyChanges { + target: headerDimmer + height: Theme.itemHeightExtraSmall/2 + width: parent.width + rotation: 0 + } + AnchorChanges { + target: clipping + anchors.top: undefined + anchors.left: undefined + anchors.right: parent.right + anchors.bottom: parent.bottom + } + }, + State { + name: 'LandscapeInverted' + when: contentArea.filteredOrientation === Qt.InvertedLandscapeOrientation //&& stackInitialized + PropertyChanges { + target: contentArea + restoreEntryValues: false + width: _verticalDimension + height: _horizontalDimension + rotation: 270 + uiOrientation: Qt.InvertedLandscapeOrientation + } + PropertyChanges { + target: headerDimmer + height: Theme.itemHeightExtraSmall/2 + width: parent.height + rotation: -90 + } + AnchorChanges { + target: clipping + anchors.top: undefined + anchors.left: parent.left + anchors.right: undefined + anchors.bottom: parent.bottom + } } - PropertyAction { - target: contentArea - property: 'orientationTransitionRunning' - value: false + ] + + property Transition defaultTransition: Transition { + to: 'Portrait,Landscape,PortraitInverted,LandscapeInverted' + from: 'Portrait,Landscape,PortraitInverted,LandscapeInverted' + SequentialAnimation { + PropertyAction { + target: contentArea + property: 'orientationTransitionRunning' + value: true + } + NumberAnimation { + target: contentArea + property: 'opacity' + to: 0 + duration: 150 + } + PropertyAction { + target: contentArea + properties: 'width,height,rotation,uiOrientation' + } + AnchorAnimation { + duration: 0 + } + PropertyAction { + target: headerDimmer + properties: 'width,height,rotation' + } + NumberAnimation { + target: contentArea + property: 'opacity' + to: 1 + duration: 150 + } + PropertyAction { + target: contentArea + property: 'orientationTransitionRunning' + value: false + } } } - } - Component.onCompleted: { - if (transitions.length === 0) { - transitions = [ defaultTransition ] + Component.onCompleted: { + if (transitions.length === 0) { + transitions = [ defaultTransition ] + } } } } diff --git a/src/controls/HeaderToolsLayout.qml b/src/controls/HeaderToolsLayout.qml index 4fcce6b..b91fb8f 100644 --- a/src/controls/HeaderToolsLayout.qml +++ b/src/controls/HeaderToolsLayout.qml @@ -22,7 +22,8 @@ Item { property bool isUiPortrait: header && header.appWindow.isUiPortrait property bool showBackButton: false - + property int toolMeasure: parent.width/10 + height: toolMeasure Rectangle { id: backButton width: opacity ? size.dp(60) : 0 @@ -84,13 +85,12 @@ Item { anchors.rightMargin: size.dp(20) anchors.verticalCenter: parent.verticalCenter width: tools ? (size.dp(50) * Math.min(maxNumberOfToolButtons, tools.length)) : 0 - property int maxNumberOfToolButtons: 3 RowLayout { id: toolsRow anchors.centerIn: parent - + height: toolMeasure function assignRotationBindings() { for (var i=0; ireadImage(id); } +QPixmap NemoImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) +{ + Q_UNUSED(size); + return m_client->requestPixmap(id,requestedSize); +} diff --git a/src/controls/nemoimageprovider.h b/src/controls/nemoimageprovider.h index 6dfcc2b..fef991a 100644 --- a/src/controls/nemoimageprovider.h +++ b/src/controls/nemoimageprovider.h @@ -27,6 +27,7 @@ class NemoImageProvider : public QQuickImageProvider public: explicit NemoImageProvider(); QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize); + QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize); private: MLocalThemeDaemonClient* m_client; }; diff --git a/src/controls/qmldir b/src/controls/qmldir index 30d9467..2b7cb54 100644 --- a/src/controls/qmldir +++ b/src/controls/qmldir @@ -24,6 +24,7 @@ ListViewItemWithActions 1.0 ListViewItemWithActions.qml GlacierRoller 1.0 GlacierRoller.qml GlacierRollerItem 1.0 GlacierRollerItem.qml InverseMouseArea 1.0 InverseMouseArea.qml +IconButton 1.0 IconButton.qml # MIRRORED CONTROLS: # These are the controls that we take directly from official QQC. diff --git a/src/controls/sizing.cpp b/src/controls/sizing.cpp index 4b2c8c3..09cff96 100644 --- a/src/controls/sizing.cpp +++ b/src/controls/sizing.cpp @@ -105,3 +105,26 @@ float Sizing::dp(float value) { return value*m_dp_factor; } + + +void Sizing::setMmScaleFactor(float value) +{ + if(value != 0) + { + qDebug() << "Set custom mm scale factor"; + + m_p_width = value; + setMmScaleFactor(); + } +} + + +void Sizing::setDpScaleFactor(float value) +{ + if(value != 0) + { + qDebug() << "Set custom dp scale factor"; + + m_dp_factor = value; + } +} diff --git a/src/controls/sizing.h b/src/controls/sizing.h index 57fc8c1..5fe7ac1 100644 --- a/src/controls/sizing.h +++ b/src/controls/sizing.h @@ -37,6 +37,9 @@ class Sizing : public QObject Q_INVOKABLE float mm(float value); Q_INVOKABLE float dp(float value); + Q_INVOKABLE void setMmScaleFactor(float value); + Q_INVOKABLE void setDpScaleFactor(float value); + private: bool m_valid; diff --git a/src/controls/themedaemon/mlocalthemedaemonclient.cpp b/src/controls/themedaemon/mlocalthemedaemonclient.cpp index 23412de..3684bc5 100644 --- a/src/controls/themedaemon/mlocalthemedaemonclient.cpp +++ b/src/controls/themedaemon/mlocalthemedaemonclient.cpp @@ -48,9 +48,9 @@ MLocalThemeDaemonClient::MLocalThemeDaemonClient(const QString &testPath, QObjec MAbstractThemeDaemonClient(parent), m_pixmapCache(), m_imageDirNodes() -#ifdef HAVE_MLITE - , themeItem("/meegotouch/theme/name") -#endif + #ifdef HAVE_MLITE + , themeItem("/meegotouch/theme/name") + #endif { QStringList themeRoots; QString themeRoot = testPath; @@ -111,6 +111,7 @@ MLocalThemeDaemonClient::MLocalThemeDaemonClient(const QString &testPath, QObjec inheritanceChain.insert(nextTheme); // the paths should be stored in reverse order than in the inheritance chain themeRoots.prepend(themeRoot + QDir::separator() + nextTheme + QDir::separator() + QLatin1String("meegotouch")); + themeRoots.prepend(themeRoot + QDir::separator() + nextTheme + QDir::separator() + QLatin1String("fontawesome")); QString parentTheme = themeIndexFile.value("X-MeeGoTouch-Metatheme/X-Inherits", "").toString(); @@ -148,7 +149,8 @@ MLocalThemeDaemonClient::~MLocalThemeDaemonClient() QPixmap MLocalThemeDaemonClient::requestPixmap(const QString &id, const QSize &requestedSize) { QPixmap pixmap; - qDebug() << "ID requested: " << id; + + QStringList parts = id.split('?'); QSize size = requestedSize; if (size.width() < 1) { @@ -158,20 +160,30 @@ QPixmap MLocalThemeDaemonClient::requestPixmap(const QString &id, const QSize &r size.rheight() = 0; } - const PixmapIdentifier pixmapId(id, size); + const PixmapIdentifier pixmapId(parts.at(0), size); pixmap = m_pixmapCache.value(pixmapId); if (pixmap.isNull()) { // The pixmap is not cached yet. Decode the image and // store it into the cache as pixmap. - const QImage image = readImage(id); + const QImage image = readImage(parts.at(0)); if (!image.isNull()) { pixmap = QPixmap::fromImage(image); - if (requestedSize.isValid() && (pixmap.size() != requestedSize)) { - pixmap = pixmap.scaled(requestedSize); + } + if (parts.length() > 1) + if (parts.length() > 1 && QColor::isValidColor(parts.at(1))) + { + QPainter painter(&pixmap); + painter.setCompositionMode(QPainter::CompositionMode_SourceIn); + painter.fillRect(pixmap.rect(), parts.at(1)); + painter.end(); } - m_pixmapCache.insert(pixmapId, pixmap); - } + if (requestedSize.width() > 0 && requestedSize.height() > 0) + pixmap = pixmap.scaled(requestedSize.width(), requestedSize.height(), Qt::IgnoreAspectRatio); + else + pixmap = pixmap; + + m_pixmapCache.insert(pixmapId, pixmap); } return pixmap; } diff --git a/src/controls/themedaemon/mlocalthemedaemonclient.h b/src/controls/themedaemon/mlocalthemedaemonclient.h index 2847bf4..6333530 100644 --- a/src/controls/themedaemon/mlocalthemedaemonclient.h +++ b/src/controls/themedaemon/mlocalthemedaemonclient.h @@ -45,6 +45,8 @@ #include #include #include +#include +#include #ifdef HAVE_MLITE #include diff --git a/src/styles/ButtonStyle.qml b/src/styles/ButtonStyle.qml index 80f0124..943330c 100644 --- a/src/styles/ButtonStyle.qml +++ b/src/styles/ButtonStyle.qml @@ -46,7 +46,7 @@ ButtonStyle { RadialGradient { x: control.pressX - width/2 y: control.pressY - height/2 - width: Theme.itemWidthMedium + width: Theme.itemWidthSmall height: width visible: control.pressed diff --git a/src/styles/IconButtonStyle.qml b/src/styles/IconButtonStyle.qml new file mode 100644 index 0000000..4b3ad4a --- /dev/null +++ b/src/styles/IconButtonStyle.qml @@ -0,0 +1,43 @@ +/**************************************************************************************** +** +** Copyright (C) 2017 Eetu Kahelin +** All rights reserved. +** +** You may use this file under the terms of BSD license as follows: +** +** 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 author 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 HOLDERS 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. +** +****************************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.4 +import QtGraphicalEffects 1.0 + +ButtonStyle { + background: Image { + fillMode: Image.PreserveAspectFit + opacity: control.enabled ? 1.0 : 0.5 + source:control._showPress ? control.highlightSource : control.source + } +} diff --git a/src/styles/qmldir b/src/styles/qmldir index 9b80dd4..f28c52f 100644 --- a/src/styles/qmldir +++ b/src/styles/qmldir @@ -20,3 +20,4 @@ SpinBoxStyle 1.0 SpinBoxStyle.qml TabViewStyle 1.0 TabViewStyle.qml TableViewStyle 1.0 TableViewStyle.qml TextFieldStyle 1.0 TextFieldStyle.qml +IconButtonStyle 1.0 IconButtonStyle.qml diff --git a/src/styles/styles.pro b/src/styles/styles.pro index 6c9144b..f703a17 100644 --- a/src/styles/styles.pro +++ b/src/styles/styles.pro @@ -27,6 +27,7 @@ QML_FILES = \ TextFieldStyle.qml \ ToolBarStyle.qml \ ToolButtonStyle.qml \ + IconButtonStyle.qml # Images QML_FILES += \