1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT
3
4import { WindowInfo } from "./ui_utils.slint";
5import { PageBase } from "page-base.slint";
6import { CityWeatherTile } from "city_weather_tile.slint";
7import { ExpandedCityWeatherTile } from "expanded_city_weather_tile.slint";
8import { CityWeather, CityWeatherInfo } from "weather_datatypes.slint";
9import { AppPalette, AppImages } from "./style/styles.slint";
10import { SlideButton } from "./controls/generic.slint";
11import { AboutBox } from "about-box.slint";
12
13component CitySlideArea inherits Rectangle {
14 in property<bool> can-move-up: true;
15 in property<bool> can-move-down: true;
16
17 callback opened;
18 callback closed;
19
20 callback up-clicked;
21 callback down-clicked;
22 callback delete-clicked;
23 callback content-clicked;
24
25 public function open() {
26 flickable.viewport-x = -buttons-layout.width;
27 root.opened();
28 }
29
30 public function close() {
31 flickable.viewport-x = 0;
32 root.closed();
33 }
34
35 height: content.preferred-height;
36
37 flickable := Flickable {
38 width: 100%;
39
40 viewport-width: slide-layout.preferred-width;
41 viewport-x: 0;
42
43 property<length> last-viewport-x: 0px;
44 flicked => {
45 if (self.last-viewport-x > self.viewport-x) {
46 root.open();
47 }
48 else {
49 root.close();
50 }
51 }
52
53 slide-layout := HorizontalLayout {
54 content := Rectangle {
55 width: root.width;
56
57 Rectangle {
58 @children
59 }
60
61 TouchArea {
62 pointer-event(event) => {
63 if (event.kind == PointerEventKind.down) {
64 flickable.last-viewport-x = flickable.viewport-x;
65 }
66 }
67
68 clicked => { root.content-clicked(); }
69 }
70 }
71
72 buttons-layout := VerticalLayout {
73 width: 50px;
74 spacing: 1px;
75
76 SlideButton {
77 icon-source: AppImages.arrow-up;
78 enabled: root.can-move-up;
79
80 clicked => { root.up-clicked(); }
81 }
82 SlideButton {
83 icon-source: AppImages.trash;
84 background-color: AppPalette.error-red;
85
86 clicked => { root.delete-clicked(); }
87 }
88 SlideButton {
89 icon-source: AppImages.arrow-down;
90 enabled: root.can-move-down;
91
92 clicked => { root.down-clicked(); }
93 }
94 }
95 }
96 }
97
98}
99
100component CityWeatherList inherits Flickable {
101 property<int> opened-index: -1;
102
103 callback expand(int, Point, length, length);
104
105 VerticalLayout {
106 alignment: start;
107 padding: 0px;
108
109 for city-weather-info[index] in CityWeather.city-weather:
110 CitySlideArea {
111 property<bool> is-opened: root.opened-index == index;
112
113 can-move-up: index > 0;
114 can-move-down: index < CityWeather.city-weather.length - 1;
115
116 changed is-opened => {
117 if (is-opened) {
118 self.open();
119 }
120 else {
121 self.close();
122 }
123 }
124
125 opened => {
126 root.opened-index = index;
127 }
128
129 content-clicked => {
130 root.opened-index = -1;
131 root.expand(index, self.absolute-position, self.width, self.height);
132 }
133
134 up-clicked => {
135 root.opened-index = index - 1;
136 CityWeather.reorder(index, root.opened-index);
137 }
138 down-clicked => {
139 root.opened-index = index + 1;
140 CityWeather.reorder(index, root.opened-index);
141 }
142 delete-clicked => {
143 root.opened-index = -1;
144 CityWeather.delete(index);
145 self.close();
146 }
147
148 CityWeatherTile {
149 city-weather-info: city-weather-info;
150 alternative-background: Math.mod(index, 2) == 0;
151 }
152 }
153
154 Rectangle {
155 // spacing
156 min-height: WindowInfo.is-portrait ? 25px : 10px;
157 }
158 AboutBox {
159 min-height: self.preferred-height;
160 }
161 }
162}
163
164struct TileInfo {
165 index: int,
166 absolute-position: Point,
167 size: { width: length, height: length }
168}
169
170export component CityListView inherits PageBase {
171 property<TileInfo> selected-tile;
172
173 CityWeatherList {
174 padding: 16px;
175
176 expand(index, absolute-position, width, height) => {
177 root.selected-tile = {
178 index: index,
179 absolute-position: absolute-position,
180 size: { width: width, height: height }
181 };
182 expanded-tile.expand();
183 }
184 }
185
186 PageBase {
187 opacity: 0.0;
188
189 states [
190 visible when expanded-tile.expanded: {
191 opacity: 1;
192
193 out {
194 animate opacity { delay: expanded-tile.animation-duration; }
195 }
196 }
197 ]
198 }
199
200 expanded-tile := ExpandedCityWeatherTile {
201 city-weather-info: CityWeather.city-weather[root.selected-tile.index];
202 alternative-background: Math.mod(root.selected-tile.index, 2) == 0;
203
204 block-x: root.selected-tile.absolute-position.x - root.absolute-position.x;
205 block-y: root.selected-tile.absolute-position.y - root.absolute-position.y;
206 block-width: root.selected-tile.size.width;
207 block-height: root.selected-tile.size.height;
208
209 full-x: 0px;
210 full-y: 0px;
211 full-width: parent.width;
212 full-height: parent.height;
213
214 clicked => {
215 self.collapse();
216 }
217 }
218}
219