Scale all QML elements according to the current display size

This commit is contained in:
Jan-Martin Raemer 2022-06-03 22:07:50 +02:00
parent 9e7a55fc20
commit bcdf3d94f2
15 changed files with 278 additions and 109 deletions

View file

@ -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 \

View file

@ -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
}
}
}

View file

@ -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

View file

@ -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{}

View file

@ -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

View file

@ -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

View file

@ -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
}
}

View file

@ -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
}
}
}

View file

@ -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"

View file

@ -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"

View file

@ -0,0 +1,88 @@
#include "StyleController.h"
#include <QGuiApplication>
#include <QScreen>
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);
}

View file

@ -0,0 +1,78 @@
#ifndef STYLECONTROLLER_H
#define STYLECONTROLLER_H
#include <QObject>
#include <QQmlContext>
#include <QQmlPropertyMap>
/**
* @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

View file

@ -4,6 +4,7 @@
#include <QCommandLineParser>
#include <QFileInfo>
#include <QDebug>
#include <controllers/StyleController.h>
#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());

View file

@ -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();