diff --git a/modules/lock/Content.qml b/modules/lock/Content.qml index 6ae42fb4..d4e50bff 100644 --- a/modules/lock/Content.qml +++ b/modules/lock/Content.qml @@ -15,19 +15,9 @@ RowLayout { Layout.fillWidth: true spacing: Tokens.spacing.medium - StyledRect { + WeatherInfo { Layout.fillWidth: true - implicitHeight: weather.implicitHeight - - topLeftRadius: Tokens.rounding.extraLarge - radius: Tokens.rounding.medium - color: Colours.tPalette.m3surfaceContainer - - WeatherInfo { - id: weather - - rootHeight: root.height - } + rootHeight: root.height } StyledRect { diff --git a/modules/lock/WeatherInfo.qml b/modules/lock/WeatherInfo.qml index 5bc97f5c..dce8acc8 100644 --- a/modules/lock/WeatherInfo.qml +++ b/modules/lock/WeatherInfo.qml @@ -6,163 +6,14 @@ import Caelestia.Config import qs.components import qs.services -ColumnLayout { +StyledRect { id: root - required property int rootHeight + required property int rootHeight // TODO: add forecast when large height - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: Tokens.padding.extraLargeIncreased - - spacing: Tokens.spacing.small - - Loader { - asynchronous: true - Layout.topMargin: Tokens.padding.extraLargeIncreased - Layout.bottomMargin: -Tokens.padding.large - Layout.alignment: Qt.AlignHCenter - - active: root.rootHeight > 610 - visible: active - - sourceComponent: StyledText { - text: qsTr("Weather") - color: Colours.palette.m3primary - font: Tokens.font.body.builders.large.size(28).weight(Font.Medium).build() - } - } - - RowLayout { - Layout.fillWidth: true - spacing: Tokens.spacing.largeIncreased - - MaterialIcon { - animate: true - text: Weather.icon - color: Colours.palette.m3secondary - fontStyle: Tokens.font.icon.builders.extraLarge.scale(2.5).build() - } - - ColumnLayout { - spacing: Tokens.spacing.small - - StyledText { - Layout.fillWidth: true - - animate: true - text: Weather.description - color: Colours.palette.m3secondary - font: Tokens.font.body.builders.large.weight(Font.Medium).build() - elide: Text.ElideRight - } - - StyledText { - Layout.fillWidth: true - - animate: true - text: qsTr("Humidity: %1%").arg(Weather.humidity) - color: Colours.palette.m3onSurfaceVariant - font: Tokens.font.body.medium - elide: Text.ElideRight - } - } - - Loader { - asynchronous: true - Layout.rightMargin: Tokens.padding.small - active: root.width > 400 - visible: active - - sourceComponent: ColumnLayout { - spacing: Tokens.spacing.small - - StyledText { - Layout.fillWidth: true - - animate: true - text: Weather.temp - color: Colours.palette.m3primary - horizontalAlignment: Text.AlignRight - font: Tokens.font.body.builders.large.size(28).weight(Font.Medium).build() - elide: Text.ElideLeft - } - - StyledText { - Layout.fillWidth: true - - animate: true - text: qsTr("Feels like: %1").arg(Weather.feelsLike) - color: Colours.palette.m3outline - horizontalAlignment: Text.AlignRight - font: Tokens.font.body.small - elide: Text.ElideLeft - } - } - } - } - - Loader { - id: forecastLoader - - asynchronous: true - Layout.topMargin: Tokens.spacing.medium - Layout.bottomMargin: Tokens.padding.extraLargeIncreased - Layout.fillWidth: true - - active: root.rootHeight > 820 - visible: active - - sourceComponent: RowLayout { - spacing: Tokens.spacing.largeIncreased - - Repeater { - model: { - const forecast = Weather.hourlyForecast; - const count = root.width < 320 ? 3 : root.width < 400 ? 4 : 5; - if (!forecast) - return Array.from({ - length: count - }, () => null); - - return forecast.slice(0, count); - } - - ColumnLayout { - id: forecastHour - - required property var modelData - - Layout.fillWidth: true - spacing: Tokens.spacing.small - - StyledText { - Layout.fillWidth: true - text: { - const hour = forecastHour.modelData?.hour ?? 0; - return hour > 12 ? `${(hour - 12).toString().padStart(2, "0")} PM` : `${hour.toString().padStart(2, "0")} AM`; - } - color: Colours.palette.m3outline - horizontalAlignment: Text.AlignHCenter - font: Tokens.font.body.large - } - - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - text: forecastHour.modelData?.icon ?? "cloud_alert" - fontStyle: Tokens.font.icon.builders.extraLarge.scale(1.5).weight(Font.Medium).build() - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - text: GlobalConfig.services.useFahrenheit ? `${forecastHour.modelData?.tempF ?? 0}°F` : `${forecastHour.modelData?.tempC ?? 0}°C` - color: Colours.palette.m3secondary - font: Tokens.font.body.large - } - } - } - } - } + implicitHeight: layout.implicitHeight + Tokens.padding.extraLarge * 2 + radius: Tokens.rounding.extraExtraLarge + color: Colours.tPalette.m3surfaceContainer Timer { running: true @@ -171,4 +22,59 @@ ColumnLayout { interval: 900000 // 15 minutes onTriggered: Weather.reload() } + + ColumnLayout { + id: layout + + anchors.centerIn: parent + spacing: Tokens.spacing.extraSmall + + StyledText { + Layout.alignment: Qt.AlignHCenter + animate: true + text: Weather.description + color: Colours.palette.m3onSurfaceVariant + font: Tokens.font.body.large + } + + RowLayout { + Layout.alignment: Qt.AlignHCenter + spacing: Tokens.spacing.medium + + StyledText { + id: temp + + animate: true + text: Weather.temp + color: Colours.palette.m3primary + font: Tokens.font.headline.builders.large.scale(1.5).weight(Font.DemiBold).width(80).build() + } + + MaterialIcon { + animate: true + text: Weather.icon + color: Colours.palette.m3tertiary + fontStyle: Tokens.font.headline.builders.large.scale(1.5).build() + } + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + animate: true + text: qsTr("Feels like %1").arg(Weather.temp) + color: Colours.palette.m3onSurfaceVariant + font: Tokens.font.body.large + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + animate: true + text: { + const today = Weather.forecast[0]; + return qsTr("High %1 • Low %2").arg(Weather.formatTemp(today?.maxTempC)).arg(Weather.formatTemp(today?.minTempC)); + } + color: Colours.palette.m3onSurfaceVariant + font: Tokens.font.body.medium + } + } } diff --git a/services/Weather.qml b/services/Weather.qml index 9c30bf7a..3b1acaaa 100644 --- a/services/Weather.qml +++ b/services/Weather.qml @@ -17,8 +17,8 @@ Singleton { readonly property string icon: cc ? Icons.getWeatherIcon(cc.weatherCode) : "cloud_alert" readonly property string description: cc?.weatherDesc ?? qsTr("No weather") - readonly property string temp: GlobalConfig.services.useFahrenheit ? `${cc?.tempF ?? 0}°F` : `${cc?.tempC ?? 0}°C` - readonly property string feelsLike: GlobalConfig.services.useFahrenheit ? `${cc?.feelsLikeF ?? 0}°F` : `${cc?.feelsLikeC ?? 0}°C` + readonly property string temp: formatTemp(cc?.tempC) + readonly property string feelsLike: formatTemp(cc?.feelsLikeC) readonly property int humidity: cc?.humidity ?? 0 readonly property real windSpeed: cc?.windSpeed ?? 0 readonly property string sunrise: cc ? Qt.formatDateTime(new Date(cc.sunrise), GlobalConfig.services.useTwelveHourClock ? "h:mm A" : "h:mm") : "--:--" @@ -26,6 +26,10 @@ Singleton { readonly property var cachedCities: new Map() + function formatTemp(temp: var): string { + return GlobalConfig.services.useFahrenheit ? `${temp !== undefined ? Math.round(toFahrenheit(temp)) : "--"}°F` : `${temp !== undefined ? Math.round(temp) : "--"}°C`; + } + function reload(): void { const configLocation = GlobalConfig.services.weatherLocation; @@ -114,10 +118,8 @@ Singleton { cc = { weatherCode: json.current.weather_code, weatherDesc: getWeatherCondition(json.current.weather_code), - tempC: Math.round(json.current.temperature_2m), - tempF: Math.round(toFahrenheit(json.current.temperature_2m)), - feelsLikeC: Math.round(json.current.apparent_temperature), - feelsLikeF: Math.round(toFahrenheit(json.current.apparent_temperature)), + tempC: json.current.temperature_2m, + feelsLikeC: json.current.apparent_temperature, humidity: json.current.relative_humidity_2m, windSpeed: json.current.wind_speed_10m, isDay: json.current.is_day, @@ -129,10 +131,8 @@ Singleton { for (let i = 0; i < json.daily.time.length; i++) forecastList.push({ date: json.daily.time[i].replace(/-/g, "/"), - maxTempC: Math.round(json.daily.temperature_2m_max[i]), - maxTempF: Math.round(toFahrenheit(json.daily.temperature_2m_max[i])), - minTempC: Math.round(json.daily.temperature_2m_min[i]), - minTempF: Math.round(toFahrenheit(json.daily.temperature_2m_min[i])), + maxTempC: json.daily.temperature_2m_max[i], + minTempC: json.daily.temperature_2m_min[i], weatherCode: json.daily.weather_code[i], icon: Icons.getWeatherIcon(json.daily.weather_code[i]) });