mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-02 19:48:27 -04:00
qs: numerous performance optimizations
- ClippingRectangle usages - Asynchronous loader usages - Replace cava visualizers with shaders
This commit is contained in:
@@ -7,15 +7,24 @@ Item {
|
||||
id: root
|
||||
|
||||
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
||||
readonly property bool hasActiveMedia: activePlayer !== null
|
||||
readonly property bool isPlaying: hasActiveMedia && activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing
|
||||
readonly property bool isPlaying: activePlayer !== null && activePlayer.playbackState === MprisPlaybackState.Playing
|
||||
readonly property bool live: visible && isPlaying
|
||||
|
||||
width: 20
|
||||
height: Theme.iconSize
|
||||
|
||||
Loader {
|
||||
active: isPlaying
|
||||
readonly property real maxBarHeight: Theme.iconSize - 2
|
||||
readonly property real minBarHeight: 3
|
||||
|
||||
onLiveChanged: {
|
||||
if (!live) {
|
||||
bars.bandsA = Qt.vector4d(0, 0, 0, 0);
|
||||
bars.bandsB = Qt.vector2d(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: root.live
|
||||
sourceComponent: Component {
|
||||
Ref {
|
||||
service: CavaService
|
||||
@@ -23,15 +32,8 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
readonly property real maxBarHeight: Theme.iconSize - 2
|
||||
readonly property real minBarHeight: 3
|
||||
readonly property real heightRange: maxBarHeight - minBarHeight
|
||||
property var barHeights: [minBarHeight, minBarHeight, minBarHeight, minBarHeight, minBarHeight, minBarHeight]
|
||||
|
||||
Timer {
|
||||
id: fallbackTimer
|
||||
|
||||
running: !CavaService.cavaAvailable && isPlaying
|
||||
running: !CavaService.cavaAvailable && root.live
|
||||
interval: 500
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
@@ -41,54 +43,32 @@ Item {
|
||||
|
||||
Connections {
|
||||
target: CavaService
|
||||
enabled: root.live
|
||||
function onValuesChanged() {
|
||||
if (!root.isPlaying) {
|
||||
root.barHeights = [root.minBarHeight, root.minBarHeight, root.minBarHeight, root.minBarHeight, root.minBarHeight, root.minBarHeight];
|
||||
const v = CavaService.values;
|
||||
if (v.length < 6)
|
||||
return;
|
||||
}
|
||||
|
||||
const newHeights = [];
|
||||
for (let i = 0; i < 6; i++) {
|
||||
if (CavaService.values.length <= i) {
|
||||
newHeights.push(root.minBarHeight);
|
||||
continue;
|
||||
}
|
||||
|
||||
const rawLevel = CavaService.values[i];
|
||||
if (rawLevel <= 0) {
|
||||
newHeights.push(root.minBarHeight);
|
||||
} else if (rawLevel >= 100) {
|
||||
newHeights.push(root.maxBarHeight);
|
||||
} else {
|
||||
newHeights.push(root.minBarHeight + Math.sqrt(rawLevel * 0.01) * root.heightRange);
|
||||
}
|
||||
}
|
||||
root.barHeights = newHeights;
|
||||
const n = i => {
|
||||
const x = v[i];
|
||||
return x <= 0 ? 0 : x >= 100 ? 1 : Math.sqrt(x * 0.01);
|
||||
};
|
||||
bars.bandsA = Qt.vector4d(n(0), n(1), n(2), n(3));
|
||||
bars.bandsB = Qt.vector2d(n(4), n(5));
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: 1.5
|
||||
ShaderEffect {
|
||||
id: bars
|
||||
anchors.fill: parent
|
||||
|
||||
Repeater {
|
||||
model: 6
|
||||
property real widthPx: width
|
||||
property real heightPx: height
|
||||
property real minH: root.minBarHeight
|
||||
property real maxH: root.maxBarHeight
|
||||
property vector4d bandsA: Qt.vector4d(0, 0, 0, 0)
|
||||
property vector2d bandsB: Qt.vector2d(0, 0)
|
||||
property vector4d fillColor: Qt.vector4d(Theme.primary.r, Theme.primary.g, Theme.primary.b, Theme.primary.a)
|
||||
|
||||
Rectangle {
|
||||
width: 2
|
||||
height: root.barHeights[index]
|
||||
radius: 1.5
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Behavior on height {
|
||||
enabled: root.isPlaying && !CavaService.cavaAvailable
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
easing.type: Easing.Linear
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fragmentShader: Qt.resolvedUrl("../../../Shaders/qsb/viz_bars.frag.qsb")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
@@ -234,16 +233,19 @@ BasePill {
|
||||
color: Theme.widgetTextColor
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Row {
|
||||
id: contentRow
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
visible: !root.isVerticalOrientation
|
||||
|
||||
readonly property real iconSize: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
||||
|
||||
IconImage {
|
||||
id: horizontalAppIcon
|
||||
Layout.preferredWidth: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
||||
Layout.preferredHeight: Layout.preferredWidth
|
||||
width: contentRow.iconSize
|
||||
height: contentRow.iconSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: root.showIcon && activeWindow && status === Image.Ready
|
||||
source: {
|
||||
if (!activeWindow || !activeWindow.appId)
|
||||
@@ -264,8 +266,10 @@ BasePill {
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
Layout.preferredWidth: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.maximizeWidgetIcons, root.barConfig?.iconScale)
|
||||
size: Layout.preferredWidth
|
||||
id: horizontalSteamIcon
|
||||
width: contentRow.iconSize
|
||||
size: contentRow.iconSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
name: "sports_esports"
|
||||
color: Theme.widgetTextColor
|
||||
visible: root.showIcon && activeWindow && activeWindow.appId && horizontalAppIcon.status !== Image.Ready && Paths.isSteamApp(activeWindow.appId)
|
||||
@@ -280,9 +284,11 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||
color: Theme.widgetTextColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
wrapMode: Text.NoWrap
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
Layout.maximumWidth: compactMode ? 80 : 180
|
||||
width: Math.min(implicitWidth, compactMode ? 80 : 180)
|
||||
visible: text.length > 0
|
||||
}
|
||||
|
||||
@@ -291,6 +297,7 @@ BasePill {
|
||||
text: compactMode ? "" : "•"
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||
color: Theme.outlineButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: !compactMode && appText.text && titleText.text
|
||||
}
|
||||
|
||||
@@ -318,9 +325,24 @@ BasePill {
|
||||
}
|
||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
|
||||
color: Theme.widgetTextColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
wrapMode: Text.NoWrap
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
Layout.maximumWidth: maxWidth - appText.implicitWidth - appSeparator.implicitWidth
|
||||
width: {
|
||||
const sp = contentRow.spacing;
|
||||
let used = 0;
|
||||
if (horizontalAppIcon.visible)
|
||||
used += horizontalAppIcon.width + sp;
|
||||
else if (horizontalSteamIcon.visible)
|
||||
used += horizontalSteamIcon.width + sp;
|
||||
if (appText.visible)
|
||||
used += appText.width + sp;
|
||||
if (appSeparator.visible)
|
||||
used += appSeparator.width + sp;
|
||||
const budget = root.maxWidth - root.horizontalPadding * 2 - used;
|
||||
return Math.min(implicitWidth, Math.max(0, budget));
|
||||
}
|
||||
visible: text.length > 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,6 +411,7 @@ DankPopout {
|
||||
anchors.fill: parent
|
||||
active: root.currentTabId === "media"
|
||||
visible: active
|
||||
asynchronous: true
|
||||
sourceComponent: Component {
|
||||
MediaPlayerTab {
|
||||
targetScreen: root.screen
|
||||
@@ -462,7 +463,7 @@ DankPopout {
|
||||
DankSpinner {
|
||||
anchors.centerIn: parent
|
||||
size: 40
|
||||
visible: wallpaperLoader.active && wallpaperLoader.status === Loader.Loading
|
||||
visible: (wallpaperLoader.active && wallpaperLoader.status === Loader.Loading) || (mediaLoader.active && mediaLoader.status === Loader.Loading) || (weatherLoader.active && weatherLoader.status === Loader.Loading)
|
||||
}
|
||||
|
||||
Loader {
|
||||
@@ -470,6 +471,7 @@ DankPopout {
|
||||
anchors.fill: parent
|
||||
active: root.currentTabId === "weather"
|
||||
visible: active
|
||||
asynchronous: true
|
||||
sourceComponent: Component {
|
||||
WeatherTab {}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ Item {
|
||||
|
||||
property int __panelHoverCount: 0
|
||||
|
||||
readonly property bool overlayBlurActive: dropdownBlur.active
|
||||
|
||||
onDropdownTypeChanged: {
|
||||
if (dropdownType === 0) {
|
||||
__panelHoverCount = 0;
|
||||
@@ -75,8 +77,10 @@ Item {
|
||||
}
|
||||
|
||||
WindowBlur {
|
||||
id: dropdownBlur
|
||||
targetWindow: root.targetWindow
|
||||
readonly property bool active: root.__activePanel !== null && root.__activePanel.visible && root.__activePanel.opacity > 0
|
||||
blurEnabled: active && Theme.connectedSurfaceBlurEnabled
|
||||
readonly property real s: root.__activePanel ? Math.min(1, root.__activePanel.scale) : 1
|
||||
blurX: root.__activePanel ? root.__activePanel.x + root.__activePanel.width * (1 - s) * 0.5 : 0
|
||||
blurY: root.__activePanel ? root.__activePanel.y + root.__activePanel.height * (1 - s) * 0.5 : 0
|
||||
|
||||
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Services.Mpris
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
@@ -70,7 +71,7 @@ Item {
|
||||
property bool _switchHold: false
|
||||
Timer {
|
||||
id: _switchHoldTimer
|
||||
interval: 650
|
||||
interval: 1500
|
||||
repeat: false
|
||||
onTriggered: _switchHold = false
|
||||
}
|
||||
@@ -78,7 +79,8 @@ Item {
|
||||
onActivePlayerChanged: {
|
||||
if (!activePlayer) {
|
||||
isSwitching = false;
|
||||
_switchHold = false;
|
||||
_switchHold = true;
|
||||
_switchHoldTimer.restart();
|
||||
return;
|
||||
}
|
||||
isSwitching = true;
|
||||
@@ -117,14 +119,10 @@ Item {
|
||||
Connections {
|
||||
target: MprisController
|
||||
function onAvailablePlayersChanged() {
|
||||
const count = (MprisController.availablePlayers?.length || 0);
|
||||
if (count === 0) {
|
||||
if ((MprisController.availablePlayers?.length || 0) === 0)
|
||||
isSwitching = false;
|
||||
_switchHold = false;
|
||||
} else {
|
||||
_switchHold = true;
|
||||
_switchHoldTimer.restart();
|
||||
}
|
||||
_switchHold = true;
|
||||
_switchHoldTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,34 +288,64 @@ Item {
|
||||
Item {
|
||||
id: bgContainer
|
||||
anchors.fill: parent
|
||||
visible: TrackArtService.resolvedArtUrl !== ""
|
||||
|
||||
Image {
|
||||
id: bgImage
|
||||
anchors.centerIn: parent
|
||||
width: Math.max(parent.width, parent.height) * 1.1
|
||||
height: width
|
||||
source: TrackArtService.resolvedArtUrl
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
asynchronous: true
|
||||
cache: true
|
||||
visible: false
|
||||
onStatusChanged: {
|
||||
if (status === Image.Ready)
|
||||
maybeFinishSwitch();
|
||||
}
|
||||
readonly property string curArt: TrackArtService.resolvedArtUrl
|
||||
// Two layers crossfade: new art loads into the hidden one and fades in once decoded.
|
||||
property bool _showA: true
|
||||
visible: layerA.ready || layerB.ready
|
||||
|
||||
onCurArtChanged: syncArt()
|
||||
Component.onCompleted: syncArt()
|
||||
|
||||
function syncArt() {
|
||||
if (curArt === "" || layerA.art == curArt || layerB.art == curArt)
|
||||
return;
|
||||
if (_showA)
|
||||
layerB.art = curArt;
|
||||
else
|
||||
layerA.art = curArt;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: blurredBg
|
||||
component BgBlurLayer: ClippingRectangle {
|
||||
id: layer
|
||||
property alias art: layerImg.source
|
||||
readonly property bool ready: layerImg.status === Image.Ready && layerImg.source != ""
|
||||
property bool front: false
|
||||
signal loaded
|
||||
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
radius: Theme.cornerRadius
|
||||
color: "transparent"
|
||||
antialiasing: true
|
||||
opacity: front ? 0.7 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 350
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: layerImg
|
||||
anchors.centerIn: parent
|
||||
width: Math.max(parent.width, parent.height) * 1.1
|
||||
height: width
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
asynchronous: true
|
||||
cache: true
|
||||
visible: false
|
||||
onStatusChanged: {
|
||||
if (status === Image.Ready && source != "")
|
||||
layer.loaded();
|
||||
}
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.centerIn: parent
|
||||
width: bgImage.width
|
||||
height: bgImage.height
|
||||
source: bgImage
|
||||
width: layerImg.width
|
||||
height: layerImg.height
|
||||
source: layerImg
|
||||
blurEnabled: true
|
||||
blurMax: 64
|
||||
blur: 0.8
|
||||
@@ -326,22 +354,26 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: bgMask
|
||||
anchors.fill: parent
|
||||
radius: Theme.cornerRadius
|
||||
visible: false
|
||||
layer.enabled: true
|
||||
BgBlurLayer {
|
||||
id: layerA
|
||||
front: bgContainer._showA
|
||||
onLoaded: {
|
||||
if (!bgContainer._showA) {
|
||||
bgContainer._showA = true;
|
||||
root.maybeFinishSwitch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
source: blurredBg
|
||||
maskEnabled: true
|
||||
maskSource: bgMask
|
||||
maskThresholdMin: 0.5
|
||||
maskSpreadAtMin: 1.0
|
||||
opacity: 0.7
|
||||
BgBlurLayer {
|
||||
id: layerB
|
||||
front: !bgContainer._showA
|
||||
onLoaded: {
|
||||
if (bgContainer._showA) {
|
||||
bgContainer._showA = false;
|
||||
root.maybeFinishSwitch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -442,7 +474,8 @@ Item {
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 1
|
||||
visible: text.length > 0
|
||||
// Reserve the line so late album metadata doesn't shift the seekbar.
|
||||
height: Math.max(implicitHeight, Theme.fontSizeSmall * 1.4)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Modules.DankDash.Overview
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -17,7 +18,7 @@ Item {
|
||||
signal navFocusRequested
|
||||
|
||||
function handleKeyEvent(event) {
|
||||
return calendarCard.handleKeyEvent(event);
|
||||
return calendarLoader.item ? calendarLoader.item.handleKeyEvent(event) : false;
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -57,16 +58,26 @@ Item {
|
||||
height: 220
|
||||
}
|
||||
|
||||
// Calendar - bottom middle (wider and taller)
|
||||
CalendarOverviewCard {
|
||||
id: calendarCard
|
||||
// Calendar - bottom middle; deferred so the grid stays off the emerge frame.
|
||||
Loader {
|
||||
id: calendarLoader
|
||||
x: parent.width * 0.2 - Theme.spacingM
|
||||
y: 100 + Theme.spacingM
|
||||
width: parent.width * 0.6
|
||||
height: 300
|
||||
asynchronous: true
|
||||
sourceComponent: Component {
|
||||
CalendarOverviewCard {
|
||||
onCloseDash: root.closeDash()
|
||||
onNavFocusRequested: root.navFocusRequested()
|
||||
}
|
||||
}
|
||||
|
||||
onCloseDash: root.closeDash()
|
||||
onNavFocusRequested: root.navFocusRequested()
|
||||
DankSpinner {
|
||||
anchors.centerIn: parent
|
||||
size: 32
|
||||
visible: calendarLoader.status === Loader.Loading
|
||||
}
|
||||
}
|
||||
|
||||
// Media - bottom right (narrow and taller)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Shapes
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
@@ -245,17 +244,6 @@ Item {
|
||||
size: Theme.iconSize * 2
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
layer.enabled: Theme.elevationEnabled
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: Theme.elevationEnabled
|
||||
shadowHorizontalOffset: Theme.elevationOffsetX(Theme.elevationLevel1)
|
||||
shadowVerticalOffset: Theme.elevationOffsetY(Theme.elevationLevel1, 1)
|
||||
shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined ? Theme.elevationLevel1.blurPx : 4) / Theme.elevationBlurMax)) : 0
|
||||
blurMax: Theme.elevationBlurMax
|
||||
shadowColor: Theme.elevationShadowColor(Theme.elevationLevel1)
|
||||
shadowOpacity: Theme.elevationLevel1 && Theme.elevationLevel1.alpha !== undefined ? Theme.elevationLevel1.alpha : 0.2
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
@@ -820,16 +808,6 @@ Item {
|
||||
property var pos: WeatherService.getSkyArcPosition(skyBox.currentDate, false)
|
||||
x: (pos?.h ?? 0) * skyBox.effectiveWidth - (moonPhase.width / 2) + skyBox.hMargin
|
||||
y: (pos?.v ?? 0) * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 - (moonPhase.height / 2) + skyBox.vMargin
|
||||
|
||||
layer.enabled: Theme.elevationEnabled
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: Theme.elevationEnabled
|
||||
shadowHorizontalOffset: Theme.elevationOffsetX(Theme.elevationLevel2)
|
||||
shadowVerticalOffset: Theme.elevationOffsetY(Theme.elevationLevel2, 4)
|
||||
shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0
|
||||
blurMax: Theme.elevationBlurMax
|
||||
shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2)
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
@@ -842,16 +820,6 @@ Item {
|
||||
property var pos: WeatherService.getSkyArcPosition(skyBox.currentDate, true)
|
||||
x: (pos?.h ?? 0) * skyBox.effectiveWidth - (sun.width / 2) + skyBox.hMargin
|
||||
y: (pos?.v ?? 0) * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 - (sun.height / 2) + skyBox.vMargin
|
||||
|
||||
layer.enabled: Theme.elevationEnabled
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: Theme.elevationEnabled
|
||||
shadowHorizontalOffset: Theme.elevationOffsetX(Theme.elevationLevel2)
|
||||
shadowVerticalOffset: Theme.elevationOffsetY(Theme.elevationLevel2, 4)
|
||||
shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0
|
||||
blurMax: Theme.elevationBlurMax
|
||||
shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -993,6 +961,7 @@ Item {
|
||||
ListView {
|
||||
id: hourlyList
|
||||
anchors.fill: parent
|
||||
reuseItems: true
|
||||
orientation: ListView.Horizontal
|
||||
spacing: Theme.spacingS
|
||||
clip: true
|
||||
@@ -1079,6 +1048,7 @@ Item {
|
||||
ListView {
|
||||
id: dailyList
|
||||
anchors.fill: parent
|
||||
reuseItems: true
|
||||
orientation: ListView.Horizontal
|
||||
spacing: Theme.spacingS
|
||||
clip: true
|
||||
|
||||
Reference in New Issue
Block a user