1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT
3
4import { VerticalBox } from "std-widgets.slint";
5import { AppPalette } from "./style/styles.slint";
6import { AppText } from "./controls/generic.slint";
7import { WeatherIcon, RainInfo, UvInfo } from "./controls/weather.slint";
8import { WeatherInfo, WeatherForecastInfo, CityWeather } from "./weather_datatypes.slint";
9
10component ForecastGraphText inherits AppText {
11 horizontal-alignment: center;
12 vertical-alignment: center;
13
14 font-size: 0.85rem;
15}
16
17component DayForecastGraphEntry inherits VerticalLayout {
18 in property <string> day-name;
19 in property <WeatherInfo> day-weather;
20 in property <bool> detailed: true;
21
22 spacing: 5px;
23
24 ForecastGraphText {
25 font-size: 1.2rem;
26 text: day-name;
27 }
28
29 WeatherIcon {
30 icon-type: day-weather.icon-type;
31 font-size: 1.6rem;
32 }
33
34 VerticalLayout {
35 spacing: 5px;
36
37 ForecastGraphText {
38 text: Math.round(day-weather.detailed_temp.max) + "° / " + Math.round(day-weather.detailed_temp.min) + "°";
39 }
40
41 RainInfo {
42 precipitation-probability: root.day-weather.precipitation_prob;
43 rain-volume: root.day-weather.rain;
44 snow-volume: root.day-weather.snow;
45
46 minimal: true;
47 }
48
49 UvInfo {
50 uv-index: root.day-weather.uv;
51
52 minimal: true;
53 }
54 }
55}
56
57export component DayForecastGraph inherits Rectangle {
58 in property <[WeatherForecastInfo]> forecast-weather;
59 in property <bool> show-animations: true;
60
61 property <length> preferred-day-width: 85px;
62
63 // max-days-count is not directly as a binding here, only when the value is actually changed.
64 // This is to avoid reevaluation of the conditional components that rely on it for every window size change.
65 // see: https://github.com/slint-ui/slint/issues/5209
66 property <int> max-days-count: 0;
67 property <int> days-count: Math.min(root.forecast-weather.length, root.max-days-count);
68
69 property <length> day-width: root.width / root.days-count;
70
71 function update-max-days-count() {
72 if (Math.floor(root.width / root.preferred-day-width) != root.max-days-count) {
73 root.max-days-count = Math.floor(root.width / root.preferred-day-width);
74 }
75 }
76
77 init => { root.update-max-days-count(); }
78 changed width => { root.update-max-days-count(); }
79
80 preferred-height: layout.preferred-height;
81
82 Path {
83 property <float> visible-part: 0%;
84
85 y: 0;
86 height: 50%;
87
88 stroke-width: 2px;
89 commands: CityWeather.get_forecast_graph_command(
90 root.forecast-weather, root.days-count, self.width, self.height);
91
92 stroke: @linear-gradient(90deg, AppPalette.foreground.with-alpha(25%) 0%,
93 AppPalette.foreground.with-alpha(25%) self.visible-part,
94 transparent self.visible-part,
95 transparent 100%);
96
97 opacity: 0.0;
98 animate opacity { duration: root.show-animations ? 1200ms : 0ms; easing: ease-in; }
99 animate visible-part { duration: root.show-animations ? 900ms : 0ms; easing: ease-in; }
100
101 init => {
102 self.opacity = 1.0;
103 self.visible-part = 100%;
104 }
105 }
106
107 layout := HorizontalLayout {
108 for index in root.days-count:
109 DayForecastGraphEntry {
110 property <WeatherForecastInfo> day-forecast-weather: root.forecast-weather[index];
111 property <duration> animation-duration: 0ms;
112
113 width: root.day-width;
114 day-name: day-forecast-weather.day-name;
115 day-weather: day-forecast-weather.weather-info;
116
117 opacity: 0.0;
118 animate opacity { duration: self.animation-duration; easing: ease-in-out-quad; }
119
120 init => {
121 if (root.show-animations) {
122 self.animation-duration = 600ms + (500ms - index * 50ms) * index;
123 }
124 self.opacity = 1.0;
125 }
126 }
127 }
128}
129