mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-17 16:54:01 -04:00
206 lines
7.5 KiB
QML
206 lines
7.5 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.DCommon.Widgets
|
|
import qs.Modules.DDash
|
|
import "../../../DCommon/Common/WheelInput.js" as WheelInput
|
|
|
|
Item {
|
|
id: root
|
|
|
|
LayoutMirroring.enabled: false
|
|
LayoutMirroring.childrenInherit: true
|
|
|
|
property bool daily: false
|
|
property int startIndex: 0
|
|
property int visibleCount: daily ? DashMetrics.dailyVisibleCount : DashMetrics.chartHourlyCount
|
|
|
|
signal pageRequested(int delta)
|
|
|
|
readonly property var source: (daily ? WeatherService.weather.forecast : WeatherService.weather.hourlyForecast) ?? []
|
|
readonly property int maxStart: Math.max(0, source.length - visibleCount)
|
|
readonly property int start: Math.max(0, Math.min(maxStart, startIndex))
|
|
readonly property var slice: source.slice(start, start + visibleCount)
|
|
readonly property var temps: slice.map(f => Number(daily ? f.tempMax : f.temp) || 0)
|
|
readonly property var lows: daily ? slice.map(f => Number(f.tempMin) || 0) : []
|
|
readonly property int nowIndex: (daily ? 0 : (WeatherService.weather.currentHourIndex ?? new Date().getHours())) - start
|
|
readonly property real columnWidth: slice.length > 0 ? (width - Theme.spacingM * 2) / slice.length : 0
|
|
readonly property real chartTop: Theme.spacingM + header.height + Theme.spacingS
|
|
readonly property real chartBottom: height - Theme.spacingM - footer.height - Theme.spacingS
|
|
|
|
function visualIndex(index) {
|
|
return I18n.isRtl ? slice.length - 1 - index : index;
|
|
}
|
|
|
|
function step(delta) {
|
|
const next = Math.max(0, Math.min(maxStart, start + delta));
|
|
if (next === start)
|
|
return false;
|
|
startIndex = next;
|
|
return true;
|
|
}
|
|
|
|
function labelFor(f, index) {
|
|
if (daily)
|
|
return WeatherService.formatForecastDay(f.rawDate || f.rawSunrise, start + index);
|
|
if (!f.rawTime)
|
|
return f.time ?? "--";
|
|
return new Date(f.rawTime).toLocaleTimeString(I18n.locale(), SettingsData.use24HourClock ? "HH:mm" : "h AP");
|
|
}
|
|
|
|
StyledText {
|
|
anchors.centerIn: parent
|
|
visible: root.slice.length === 0
|
|
text: I18n.tr("Forecast Not Available")
|
|
color: Theme.onSurfaceVariant
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
acceptedButtons: Qt.NoButton
|
|
onWheel: wheel => {
|
|
if (!WheelInput.isHorizontal(wheel) || root.maxStart === 0) {
|
|
wheel.accepted = false;
|
|
return;
|
|
}
|
|
const delta = WheelInput.dominantDelta(wheel.angleDelta);
|
|
const step = delta > 0 ? -1 : 1;
|
|
const next = root.start + step;
|
|
if (delta !== 0 && next >= 0 && next <= root.maxStart)
|
|
root.pageRequested(step);
|
|
wheel.accepted = true;
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
x: Theme.spacingM + root.visualIndex(root.nowIndex) * root.columnWidth
|
|
y: Theme.spacingS
|
|
width: root.columnWidth
|
|
height: parent.height - Theme.spacingS * 2
|
|
radius: Theme.cornerRadiusM
|
|
color: Theme.withAlpha(Theme.primary, Theme.stateLayerHover)
|
|
visible: root.nowIndex >= 0 && root.nowIndex < root.slice.length
|
|
}
|
|
|
|
Item {
|
|
id: header
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.top: parent.top
|
|
anchors.margins: Theme.spacingM
|
|
height: Theme.iconSize + Theme.fontSizeSmall * 2 + Theme.spacingXS * 2
|
|
|
|
Repeater {
|
|
model: root.slice
|
|
|
|
Column {
|
|
id: column
|
|
|
|
required property var modelData
|
|
required property int index
|
|
|
|
readonly property bool current: index === root.nowIndex
|
|
|
|
x: root.visualIndex(index) * root.columnWidth
|
|
width: root.columnWidth
|
|
spacing: Theme.spacingXXS
|
|
|
|
StyledText {
|
|
width: parent.width
|
|
text: root.labelFor(column.modelData, column.index)
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: column.current ? Theme.fontWeightBold : Theme.fontWeightMedium
|
|
color: column.current ? Theme.primary : Theme.onSurfaceVariant
|
|
horizontalAlignment: Text.AlignHCenter
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
DIcon {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
name: WeatherService.getWeatherIcon(column.modelData.wCode || 0, column.modelData.isDay ?? true)
|
|
size: Theme.iconSize
|
|
color: column.current ? Theme.primary : Theme.onSurfaceVariant
|
|
}
|
|
|
|
StyledText {
|
|
width: parent.width
|
|
text: WeatherService.formatTemp(root.daily ? column.modelData.tempMax : column.modelData.temp) ?? "--"
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: Theme.fontWeightMedium
|
|
font.features: ({
|
|
"tnum": 1
|
|
})
|
|
color: column.current ? Theme.primary : Theme.surfaceText
|
|
horizontalAlignment: Text.AlignHCenter
|
|
elide: Text.ElideRight
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DSparkline {
|
|
x: Theme.spacingM + root.columnWidth / 2
|
|
y: root.chartTop
|
|
width: Math.max(0, root.width - Theme.spacingM * 2 - root.columnWidth)
|
|
height: Math.max(0, root.chartBottom - root.chartTop)
|
|
values: I18n.isRtl ? root.temps.slice().reverse() : root.temps
|
|
secondaryValues: I18n.isRtl ? root.lows.slice().reverse() : root.lows
|
|
autoRange: true
|
|
showDots: true
|
|
dotRadius: DashMetrics.chartDotRadius
|
|
lineWidth: Theme.outlineWidthFocused
|
|
lineColor: Theme.primary
|
|
secondaryLineColor: Theme.tertiary
|
|
fillOpacity: 0
|
|
edgeExtension: root.columnWidth / 2
|
|
insetTop: dotRadius * 2
|
|
insetBottom: dotRadius * 2
|
|
}
|
|
|
|
Item {
|
|
id: footer
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.bottom: parent.bottom
|
|
anchors.margins: Theme.spacingM
|
|
height: Theme.fontSizeSmall + Theme.spacingXS
|
|
|
|
Repeater {
|
|
model: root.slice
|
|
|
|
Item {
|
|
id: footerColumn
|
|
required property var modelData
|
|
required property int index
|
|
|
|
x: root.visualIndex(index) * root.columnWidth
|
|
width: root.columnWidth
|
|
height: footer.height
|
|
|
|
Row {
|
|
anchors.centerIn: parent
|
|
spacing: Theme.spacingXXS
|
|
|
|
DIcon {
|
|
visible: !root.daily
|
|
name: "rainy"
|
|
size: Theme.iconSizeSmall
|
|
color: Theme.onSurfaceVariant
|
|
}
|
|
|
|
StyledText {
|
|
text: root.daily ? (WeatherService.formatTemp(footerColumn.modelData.tempMin) ?? "--") : (WeatherService.formatPercent(footerColumn.modelData.precipitationProbability) ?? "--")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: footerColumn.index === root.nowIndex ? Theme.fontWeightBold : Theme.fontWeightMedium
|
|
color: footerColumn.index === root.nowIndex ? Theme.primary : Theme.onSurfaceVariant
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|