From bcdf3d94f299fd9ad2d763b9a210129481814d09 Mon Sep 17 00:00:00 2001 From: Jan-Martin Raemer Date: Fri, 3 Jun 2022 22:07:50 +0200 Subject: [PATCH] Scale all QML elements according to the current display size --- .gitignore | 4 +- LenaPi/LenaPi.pro | 2 + LenaPi/MediaProgress.qml | 47 +++++++------- LenaPi/MusicPlayer.qml | 4 +- LenaPi/MyScrollView.qml | 16 ++--- LenaPi/Navigation.qml | 10 +-- LenaPi/NavigationListDelegate.qml | 6 +- LenaPi/PlayerButtons.qml | 80 +++++++++++------------ LenaPi/PlayerControlPannel.qml | 38 +++++------ LenaPi/RoundImageButton.qml | 4 +- LenaPi/VolumeSlider.qml | 2 +- LenaPi/controllers/StyleController.cpp | 88 ++++++++++++++++++++++++++ LenaPi/controllers/StyleController.h | 78 +++++++++++++++++++++++ LenaPi/main.cpp | 4 ++ LenaPi/main.qml | 4 +- 15 files changed, 278 insertions(+), 109 deletions(-) create mode 100644 LenaPi/controllers/StyleController.cpp create mode 100644 LenaPi/controllers/StyleController.h diff --git a/.gitignore b/.gitignore index cf45d4e..ebd8b13 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ *.autosave -build-LenaPi-Desktop-Debug -build-LenaPi-RasPi-Debug -build-LenaPi-RasPi-Release +build-LenaPi* LenaPi/LenaPi.pro.user.4.8-pre1 musik diff --git a/LenaPi/LenaPi.pro b/LenaPi/LenaPi.pro index c21dbe6..1bc3c7b 100644 --- a/LenaPi/LenaPi.pro +++ b/LenaPi/LenaPi.pro @@ -6,6 +6,7 @@ LIBS += ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android-files SOURCES += main.cpp \ controllers/MusicPlayer.cpp \ + controllers/StyleController.cpp \ models/NavigationListModel.cpp \ models/NavigationItemModel.cpp \ controllers/NavigationController.cpp \ @@ -41,6 +42,7 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin HEADERS += \ controllers/MusicPlayer.h \ + controllers/StyleController.h \ models/MusicPlayer.h \ models/NavigationListModel.h \ models/NavigationItemModel.h \ diff --git a/LenaPi/MediaProgress.qml b/LenaPi/MediaProgress.qml index 5b6b441..d87746d 100644 --- a/LenaPi/MediaProgress.qml +++ b/LenaPi/MediaProgress.qml @@ -1,51 +1,52 @@ import QtQuick 2.0 import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 import QtQuick.Controls.Styles 1.4 -Item { +ColumnLayout { id: container property var model - // manually set height - Component.onCompleted: height = childrenRect.height + spacing: StyleSpacings.tinySpacing - Label{ - anchors.left: progress.left - anchors.bottom: progress.top - font.pointSize: 9 - color: "grey" - text: model.pMediaTitle - } + RowLayout{ + Label{ + font.pointSize: 9 + color: "grey" + text: model.pMediaTitle + } + Item{ + // spacer + Layout.fillWidth: true + } - Label{ - anchors.right: progress.right - anchors.bottom: progress.top - font.pointSize: 9 - color: "grey" - text: model.pTime + " / " + model.pMediaLength + Label{ + font.pointSize: 9 + color: "grey" + text: model.pTime + " / " + model.pMediaLength + } } ProgressBar{ id: progress - anchors.top: parent.top - anchors.left: parent.left + Layout.fillWidth: true value: model.pProgress style:ProgressBarStyle { background: Rectangle { - radius: 5 + radius: StyleSizes.progressBackgroundRadius color: "white" border.color: "grey" - border.width: 1 + border.width: StyleSizes.progressBackgroundBorderWidth implicitWidth: container.width - implicitHeight: 10 + implicitHeight: StyleSizes.progressBackgroundDefaultHeight } progress: Rectangle { color: "blue" border.color: "blue" - radius: 5 - implicitHeight: 8 + radius: StyleSizes.progressBackgroundRadius + implicitHeight: StyleSizes.progressBarDefaultHeight } } } diff --git a/LenaPi/MusicPlayer.qml b/LenaPi/MusicPlayer.qml index d37e4c6..15cf60b 100644 --- a/LenaPi/MusicPlayer.qml +++ b/LenaPi/MusicPlayer.qml @@ -5,7 +5,7 @@ import QtGraphicalEffects 1.0 Item{ id: container - property int margins: 20 + property int margins: StyleMargins.defaultMargin RoundImageButton{ id: backNavigation @@ -43,7 +43,7 @@ Item{ Image{ id: cover anchors.centerIn: parent - height: parent.height-10 + height: parent.height-StyleMargins.smallMargin width: height source: musicModel.pCoverImageSource fillMode: Image.PreserveAspectCrop diff --git a/LenaPi/MyScrollView.qml b/LenaPi/MyScrollView.qml index 4d3e6e8..63245eb 100644 --- a/LenaPi/MyScrollView.qml +++ b/LenaPi/MyScrollView.qml @@ -6,24 +6,24 @@ ScrollView{ style: ScrollViewStyle{ transientScrollBars: true handle: Item { - implicitWidth: 16 - implicitHeight: 8 + implicitWidth: StyleSizes.scrollHandleWidth + implicitHeight: StyleSizes.scrollHandleHeight Rectangle { color: "blue" border.color: "black" radius: height/2 anchors.fill: parent - anchors.topMargin: 1 - anchors.leftMargin: 1 - anchors.rightMargin: 1 - anchors.bottomMargin: 1 + anchors.topMargin: StyleMargins.scrollHandleMargin + anchors.leftMargin: StyleMargins.scrollHandleMargin + anchors.rightMargin: StyleMargins.scrollHandleMargin + anchors.bottomMargin: StyleMargins.scrollHandleMargin } } scrollBarBackground: Rectangle{ color: "transparent" border.color: "grey" - border.width: 1 - height: 8 + border.width: StyleSizes.scrollHandleBorderWidth + height: StyleSizes.scrollHandleHeight radius: height/2 } decrementControl: Item{} diff --git a/LenaPi/Navigation.qml b/LenaPi/Navigation.qml index cd5df5b..8e6f283 100644 --- a/LenaPi/Navigation.qml +++ b/LenaPi/Navigation.qml @@ -6,7 +6,7 @@ import QtQuick.Controls 2.4 */ Item { id: container - property int margins: 20 + property int margins: StyleMargins.defaultMargin RoundImageButton{ id: back @@ -36,7 +36,7 @@ Item { anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - height: 210 + height: StyleSizes.navigationListHeight color: "#99ffffff" @@ -51,12 +51,12 @@ Item { anchors.left: parent.left anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - anchors.margins: 20 + anchors.margins: StyleMargins.defaultMargin - height: parent.height - 40 + height: parent.height - 2*StyleMargins.defaultMargin model: navigationList.pModelItems - spacing: 20 + spacing: StyleSpacings.defaultSpacing orientation: ListView.Horizontal delegate: NavigationListDelegate{ id: delegate diff --git a/LenaPi/NavigationListDelegate.qml b/LenaPi/NavigationListDelegate.qml index 94b8919..aaff20a 100644 --- a/LenaPi/NavigationListDelegate.qml +++ b/LenaPi/NavigationListDelegate.qml @@ -14,11 +14,11 @@ ItemDelegate{ property alias imageSource: contentImage.source - padding: 5 + padding: StylePaddings.defaultPadding background: Rectangle{ id: background - implicitWidth: 150 + implicitWidth: StyleSizes.navigationDelegateDefaultSize implicitHeight: implicitWidth radius: container.isCircleDelegate ? height/2 : 0 color: "blue" @@ -26,7 +26,7 @@ ItemDelegate{ id: contentImage source: "qrc:/default_image" anchors.fill: parent - anchors.margins: 5 + anchors.margins: StyleMargins.tinyMargin fillMode: Image.PreserveAspectCrop layer.enabled: true diff --git a/LenaPi/PlayerButtons.qml b/LenaPi/PlayerButtons.qml index 8fe8b00..07caed0 100644 --- a/LenaPi/PlayerButtons.qml +++ b/LenaPi/PlayerButtons.qml @@ -1,62 +1,58 @@ import QtQuick 2.0 +import QtQuick.Layouts 1.3 -Item { +RowLayout{ id: container property var model - property var spacing: 20 - Row{ - id: buttons - anchors.centerIn: parent - spacing: container.spacing + property var spacing: StyleSpacings.defaultSpacing - RoundImageButton{ - id: previous - anchors.verticalCenter: parent.verticalCenter + RoundImageButton{ + id: previous + Layout.alignment: Qt.AlignVCenter - diameter: 60 - imageSource: "qrc:/icon_previous" + diameter: StyleSizes.smallPlayerButtonSize //60 + imageSource: "qrc:/icon_previous" - enabled: model.pHasPrevious + enabled: model.pHasPrevious - onClicked:{ - model.playPrevious(); - } + onClicked:{ + model.playPrevious(); } - RoundImageButton{ - id: playPause - anchors.verticalCenter: parent.verticalCenter - diameter: 80 - imageSource: model.pIsPlaying ? "qrc:/icon_pause" : "qrc:/icon_play" + } + RoundImageButton{ + id: playPause + Layout.alignment: Qt.AlignVCenter + diameter: StyleSizes.largePlayerButtonSize + imageSource: model.pIsPlaying ? "qrc:/icon_pause" : "qrc:/icon_play" - onClicked:{ - model.playPause(); - } + onClicked:{ + model.playPause(); } - RoundImageButton{ - id: stop - anchors.verticalCenter: parent.verticalCenter + } + RoundImageButton{ + id: stop + Layout.alignment: Qt.AlignVCenter - diameter: 60 - imageSource: "qrc:/icon_stop" + diameter: StyleSizes.smallPlayerButtonSize + imageSource: "qrc:/icon_stop" - enabled: model.pIsPlaying + enabled: model.pIsPlaying - onClicked:{ - model.stopMusic(); - } + onClicked:{ + model.stopMusic(); } - RoundImageButton{ - id: next - anchors.verticalCenter: parent.verticalCenter + } + RoundImageButton{ + id: next + Layout.alignment: Qt.AlignVCenter - diameter: 60 - imageSource: "qrc:/icon_next" + diameter: StyleSizes.smallPlayerButtonSize + imageSource: "qrc:/icon_next" - enabled: model.pHasNext + enabled: model.pHasNext - onClicked:{ - model.playNext(); - } + onClicked:{ + model.playNext(); } - } //Row + } } diff --git a/LenaPi/PlayerControlPannel.qml b/LenaPi/PlayerControlPannel.qml index e438945..9eb4ff2 100644 --- a/LenaPi/PlayerControlPannel.qml +++ b/LenaPi/PlayerControlPannel.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import QtQuick.Layouts 1.3 Rectangle { id: container @@ -6,28 +7,29 @@ Rectangle { property int margins color: "#99ffffff" - height: 140 + height: content.height + 2* margins //StyleSizes.playerControlPanelHeight - MediaProgress{ - id: progress - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: container.margins - model: container.model - // might require height for labels to be shown on RasPi (Qt 5.10) - //2019-04-04: defined in MediaProgress.qml -> test on rasPi - } - - PlayerButtons{ - id: buttons + ColumnLayout{ + id: content + spacing: StyleSpacings.smallSpacing + anchors.margins: container.margins anchors.left: parent.left anchors.right: parent.right - anchors.top: progress.bottom - anchors.bottom: parent.bottom - model: container.model - spacing: 20 + MediaProgress{ + id: progress + + model: container.model + } + + PlayerButtons{ + id: buttons + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.fillHeight: true + model: container.model + spacing: StyleSpacings.defaultSpacing + } } } diff --git a/LenaPi/RoundImageButton.qml b/LenaPi/RoundImageButton.qml index 5334465..e2453c3 100644 --- a/LenaPi/RoundImageButton.qml +++ b/LenaPi/RoundImageButton.qml @@ -8,7 +8,7 @@ Button { id: container property alias imageSource: image.source // default button diameter -> default width, readonly - readonly property int defaultDiameter: 65 + readonly property int defaultDiameter: StyleSizes.roundButtonDefaultSize // button diameter -> width property int diameter: defaultDiameter // diameter of content image -> width @@ -16,7 +16,7 @@ Button { background: Rectangle{ - border.width: 2 + border.width: StyleSizes.roundButtonBorderWidth border.color: "grey" color: "white" diff --git a/LenaPi/VolumeSlider.qml b/LenaPi/VolumeSlider.qml index 1667df5..6b51aca 100644 --- a/LenaPi/VolumeSlider.qml +++ b/LenaPi/VolumeSlider.qml @@ -7,7 +7,7 @@ ColumnLayout { property alias to: slider.to property alias stepSize: slider.stepSize property alias value: slider.value - spacing: 5 + spacing: StyleSpacings.tinySpacing RoundImageButton{ id: increaseButton imageSource: "qrc:///icon_increase_volume" diff --git a/LenaPi/controllers/StyleController.cpp b/LenaPi/controllers/StyleController.cpp new file mode 100644 index 0000000..764818f --- /dev/null +++ b/LenaPi/controllers/StyleController.cpp @@ -0,0 +1,88 @@ +#include "StyleController.h" +#include +#include + +StyleController::StyleController(QObject *parent) + : QObject{parent}, mStyleSizes(new QQmlPropertyMap(this)), + mMargins(new QQmlPropertyMap(this)), mSpacings(new QQmlPropertyMap(this)), mPaddings(new QQmlPropertyMap(this)) +{ + // nothing +} + +void StyleController::calculateAndSetRatio() +{ + qreal refHeight = 480; + qreal refWidth = 800; + // Scales to fullscreen. No rescaling when changing window size + QRect rect = QGuiApplication::primaryScreen()->geometry(); + qreal height = qMax(rect.width(),rect.height()); + qreal width = qMin(rect.width(), rect.height()); + + mRatio = qMin(height/refHeight, width/refWidth); +} + +void StyleController::initStyleSizes() +{ + scaleAndInsert(mStyleSizes, "roundButtonDefaultSize", 65); + scaleAndInsert(mStyleSizes, "roundButtonBorderWidth", 2); + scaleAndInsert(mStyleSizes, "smallPlayerButtonSize", 60); + scaleAndInsert(mStyleSizes, "largePlayerButtonSize", 80); + + scaleAndInsert(mStyleSizes, "scrollHandleWidth", 16); + scaleAndInsert(mStyleSizes, "scrollHandleHeight", 8); + scaleAndInsert(mStyleSizes, "scrollHandleBorderWidth", 1); + + int progressBackgroundDefaultHeight = 10; + scaleAndInsert(mStyleSizes, "progressBackgroundDefaultHeight", progressBackgroundDefaultHeight); + scaleAndInsert(mStyleSizes, "progressBackgroundRadius", progressBackgroundDefaultHeight/2); + scaleAndInsert(mStyleSizes, "progressBarDefaultHeight", 8); + scaleAndInsert(mStyleSizes, "progressBackgroundBorderWidth", 1); + + scaleAndInsert(mStyleSizes, "navigationListHeight", 210); + scaleAndInsert(mStyleSizes, "navigationDelegateDefaultSize", 150); + +} + +void StyleController::initSpacings() +{ + scaleAndInsert(mSpacings, "defaultSpacing", 20); + scaleAndInsert(mSpacings, "smallSpacing", 10); + scaleAndInsert(mSpacings, "tinySpacing", 5); +} + +void StyleController::initMargins() +{ + scaleAndInsert(mMargins, "defaultMargin", 20); + scaleAndInsert(mMargins, "smallMargin", 10); + scaleAndInsert(mMargins, "tinyMargin", 5); + scaleAndInsert(mMargins, "scrollHandleMargins", 1); +} + +void StyleController::initPaddings() +{ + scaleAndInsert(mPaddings, "defaultPadding", 5); +} + +void StyleController::scaleAndInsert(QQmlPropertyMap *map, const QString &key, int value) +{ + map->insert(key, applyRatio(value)); +} + +int StyleController::applyRatio(int size) const +{ + return size*mRatio; +} + +void StyleController::init(QQmlContext *context) +{ + calculateAndSetRatio(); + initStyleSizes(); + initMargins(); + initSpacings(); + initPaddings(); + + context->setContextProperty("StyleSizes", mStyleSizes); + context->setContextProperty("StyleSpacings", mSpacings); + context->setContextProperty("StyleMargins", mMargins); + context->setContextProperty("StylePaddings", mPaddings); +} diff --git a/LenaPi/controllers/StyleController.h b/LenaPi/controllers/StyleController.h new file mode 100644 index 0000000..aa4714f --- /dev/null +++ b/LenaPi/controllers/StyleController.h @@ -0,0 +1,78 @@ +#ifndef STYLECONTROLLER_H +#define STYLECONTROLLER_H + +#include +#include +#include + +/** + * @brief Contains all style sizes, margins and paddings used throughout the application + * + * Based on a reference screen size, maps for scaled margins, paddings and spacings are created and + * registered in the QML context. The values stored in the maps can then be used from QML. + * Map names: StyleSizes, StylePaddings, StyleSpacings, StyleMargins + * + * Always use values from these maps in QML! Never use magic numbers! They will NOT scale when + * the application is run on a different screen. + * + * See https://doc.qt.io/qt-5/scalability.html + */ +class StyleController : public QObject +{ + Q_OBJECT +public: + explicit StyleController(QObject *parent = nullptr); + + /** + * @brief Calculates ratio, initializes all maps and registers them in QML context + * @param context Context to register Maps in + */ + void init(QQmlContext* context); + +private: + /** + * @brief Calculates ratio from reference screen size and size of the current primary screen and inits member mRatio + */ + void calculateAndSetRatio(); + /** + * @brief Initializes map containing all sizes (e.g., button or delegate sizes) + */ + void initStyleSizes(); + /** + * @brief Initializes map containing all spacings + */ + void initSpacings(); + /** + * @brief Initializes map containing all margins + */ + void initMargins(); + /** + * @brief Initializes map containing all paddings + */ + void initPaddings(); + /** + * @brief Scales the value with calculated ratio and inserts it into the given map under the given key + * @param map Map to insert key-value pair into + * @param key Key used in the map. This is the name used to retrieve the value in QML + * @param value Unscaled value + * @see mRatio + * + * In QML the value with the key "defaulSpacing" in the Map "StyleSpacings" is accessed by StyleSpacings.defaultSpacing + */ + void scaleAndInsert(QQmlPropertyMap* map, const QString& key, int value); + /** + * @brief Applys ratio to given size + * @param size Size to be scaled + * @return scaled size + * @see mRatio + */ + int applyRatio(int size) const; + + qreal mRatio = 1.0; + QQmlPropertyMap* mStyleSizes = nullptr; + QQmlPropertyMap* mMargins = nullptr; + QQmlPropertyMap* mSpacings = nullptr; + QQmlPropertyMap* mPaddings = nullptr; +}; + +#endif // STYLECONTROLLER_H diff --git a/LenaPi/main.cpp b/LenaPi/main.cpp index dee97c8..c1aafb2 100644 --- a/LenaPi/main.cpp +++ b/LenaPi/main.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "controllers/NavigationController.h" #include "MouseEventSpy.h" #include "EnergySaver.h" @@ -42,6 +43,9 @@ int main(int argc, char *argv[]) const auto settingsHandler = SettingsHandler::createSettingsHandlerAndFillWithDefaultsIfMissing(settings); + // init style + StyleController styleController; + styleController.init(engine.rootContext()); // init main app NavigationController navController; navController.setContext(engine.rootContext()); diff --git a/LenaPi/main.qml b/LenaPi/main.qml index 7d26c1b..c796e43 100644 --- a/LenaPi/main.qml +++ b/LenaPi/main.qml @@ -4,8 +4,8 @@ import QtQuick.Controls 2.4 Window { visible: true - width: 800 - height: 480 + // width: 800 + //height: 480 title: "LenaPi 1.2" Component.onCompleted: showMaximized();