feat: add hourly forecast to lock weather

Shows if screen is large
This commit is contained in:
2 * r + 2 * t 2026-06-02 18:24:08 +10:00
parent 18af8ab4ac
commit 318d20fec2
4 changed files with 135 additions and 6 deletions

View file

@ -7,9 +7,15 @@ import qs.services
StyledRect {
id: root
required property int rootHeight // TODO: add forecast when large height
required property int rootHeight
readonly property bool showForecast: rootHeight >= Tokens.sizes.lock.showForecastHeight
implicitHeight: layout.implicitHeight + Tokens.padding.extraLarge * 2
implicitHeight: {
const base = brief.implicitHeight + brief.anchors.topMargin;
if (showForecast)
return base + Tokens.spacing.largeIncreased + forecast.implicitHeight + forecast.anchors.margins;
return base + brief.anchors.topMargin;
}
radius: Tokens.rounding.extraExtraLarge
color: Colours.tPalette.m3surfaceContainer
@ -22,8 +28,24 @@ StyledRect {
}
BriefInfo {
id: layout
id: brief
anchors.centerIn: parent
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Tokens.padding.extraLarge
}
Loader {
id: forecast
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: Tokens.padding.large
active: root.showForecast
asynchronous: true
sourceComponent: Forecast {}
}
}

View file

@ -0,0 +1,105 @@
import QtQuick
import QtQuick.Layouts
import M3Shapes
import Caelestia
import Caelestia.Config
import qs.components
import qs.services
StyledRect {
id: root
color: Colours.layer(Colours.palette.m3surfaceContainerHigh, 2)
radius: Tokens.rounding.extraLargeIncreased
implicitHeight: header.anchors.margins + header.implicitHeight + Tokens.spacing.medium + layout.implicitHeight + layout.anchors.bottomMargin
RowLayout {
id: header
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: Tokens.padding.largeIncreased
spacing: Tokens.spacing.small
MaterialIcon {
Layout.topMargin: Math.round(fontInfo.pointSize * 0.12)
text: "schedule"
fontStyle: Tokens.font.icon.builders.medium.weight(title.font.weight).build()
}
StyledText {
id: title
text: qsTr("Hourly forecast")
font: Tokens.font.title.medium
}
}
RowLayout {
id: layout
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: Tokens.padding.largeIncreased
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small
Repeater {
model: CUtils.clamp(Math.floor((layout.width + layout.spacing) / (Tokens.sizes.lock.forecastItemWidth + layout.spacing)), 0, Weather.hourlyForecast.length)
ColumnLayout {
id: hour
required property int index
readonly property var cond: Weather.hourlyForecast[index]
Layout.fillWidth: true
spacing: Tokens.spacing.extraSmall
MaterialShape {
Layout.alignment: Qt.AlignHCenter
implicitSize: temp.implicitHeight + Tokens.padding.medium * 2
shape: MaterialShape.Cookie4Sided
color: Qt.alpha(Colours.palette.m3primary, hour.index === 0 ? 1 : 0)
Behavior on color {
CAnim {}
}
StyledText {
id: temp
anchors.centerIn: parent
text: Weather.formatTemp(hour.cond.tempC).slice(0, -1) // Remove C/F
color: hour.index === 0 ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
font: Tokens.font.title.medium
}
}
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
text: hour.cond.icon
color: Colours.palette.m3secondary
fontStyle: Tokens.font.icon.large
}
StyledText {
Layout.alignment: Qt.AlignHCenter
text: hour.cond.precipChance + "%"
color: Colours.palette.m3primary
}
StyledText {
Layout.topMargin: Tokens.spacing.extraSmall
Layout.alignment: Qt.AlignHCenter
text: hour.index === 0 ? qsTr("Now") : Qt.formatDateTime(new Date(hour.cond.timestamp.replace("T", " ")), GlobalConfig.services.useTwelveHourClock ? "ha" : "hh00")
color: Colours.palette.m3onSurfaceVariant
font: Tokens.font.body.medium
}
}
}
}
}

View file

@ -285,6 +285,8 @@ class LockTokens : public ConfigObject {
CONFIG_PROPERTY(qreal, heightMult, 0.7)
CONFIG_PROPERTY(qreal, ratio, 16.0 / 9.0)
CONFIG_PROPERTY(int, centerWidth, 600)
CONFIG_PROPERTY(int, showForecastHeight, 975)
CONFIG_PROPERTY(int, forecastItemWidth, 51)
public:
explicit LockTokens(QObject* parent = nullptr)

View file

@ -150,7 +150,7 @@ Singleton {
timestamp: json.hourly.time[i],
hour: time.getHours(),
tempC: Math.round(json.hourly.temperature_2m[i]),
tempF: Math.round(toFahrenheit(json.hourly.temperature_2m[i])),
precipChance: json.hourly.precipitation_probability[i],
weatherCode: json.hourly.weather_code[i],
icon: Icons.getWeatherIcon(json.hourly.weather_code[i])
});
@ -169,7 +169,7 @@ Singleton {
const [lat, lon] = loc.split(",").map(s => s.trim());
const baseUrl = "https://api.open-meteo.com/v1/forecast";
const params = ["latitude=" + lat, "longitude=" + lon, "hourly=weather_code,temperature_2m", "daily=weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset", "current=temperature_2m,relative_humidity_2m,apparent_temperature,is_day,weather_code,wind_speed_10m", "timezone=auto", "forecast_days=7"];
const params = ["latitude=" + lat, "longitude=" + lon, "hourly=weather_code,temperature_2m,precipitation_probability", "daily=weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset", "current=temperature_2m,relative_humidity_2m,apparent_temperature,is_day,weather_code,wind_speed_10m", "timezone=auto", "forecast_days=7"];
return baseUrl + "?" + params.join("&");
}