1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT
3
4import { GroupBox, ComboBox, VerticalBox, GroupBox, GridBox, Palette, TextEdit, Button, Switch, ScrollView, StyleMetrics } from "std-widgets.slint";
5import { NavigationListView, NavigationListViewItem, Container, ExtendedLineEdit, Icon, CardListView, CardListViewItem, IconButton, TitleText } from "../widgets.slint";
6import { ModalDialog } from "../widgets.slint";
7import { Icons } from "../assets.slint";
8import { UsecasesPalette } from "../widgets/styling.slint";
9import { DialogGlobal } from "../widgets/dialog.slint";
10
11export global MailViewAdapter { }
12
13export global MailSideBarViewAdapter {
14 out property <[string]> accounts: ["jon.doe@slint.dev", "jon.doe@my-mail.com", "jon.doe@gmail.com"];
15
16 out property <[NavigationListViewItem]> boxes: [
17 { text: @tr("Inbox"), message: "128", icon: Icons.inbox },
18 { text: @tr("Drafts"), message: "9", icon: Icons.document },
19 { text: @tr("Sent"), icon: Icons.send },
20 {
21 text: @tr("Junk"),
22 icon: Icons.junk,
23 message: "23",
24 },
25 { text: @tr("Trash"), icon: Icons.trash },
26 { text: @tr("Archive"), icon: Icons.archive }
27 ];
28
29 out property <[NavigationListViewItem]> custom-boxes: [
30 { text: @tr("Social"), message: "3972", icon: Icons.useres },
31 { text: @tr("Updates"), message: "342", icon: Icons.updates },
32 { text: @tr("Forums"), message: "128", icon: Icons.message }
33 ];
34 in-out property <int> current-box;
35 in-out property <int> current-custom-box: -1;
36
37 public pure function current-title() -> string {
38 if current-box > -1 && current-box < boxes.length {
39 return boxes[current-box].text;
40 }
41 if current-custom-box > -1 && current-custom-box < custom-boxes.length {
42 return custom-boxes[current-custom-box].text;
43 }
44 ""
45 }
46}
47
48export component MailContainer inherits Container {
49 background: UsecasesPalette.use-material ? Palette.alternate-background : Palette.control-background;
50 border-color: UsecasesPalette.use-material ? Palette.border : transparent;
51}
52
53export component MailSideBarView {
54 in property <bool> break-layout;
55
56 horizontal-stretch: 0;
57 min-width: 200px;
58
59 VerticalLayout {
60 spacing: 4px;
61
62 if !root.break-layout : ComboBox {
63 model: MailSideBarViewAdapter.accounts;
64 }
65
66 Container {
67 border-radius: UsecasesPalette.use-material ? 0 : 4px;
68
69 NavigationListView {
70 model: MailSideBarViewAdapter.boxes;
71 current-item <=> MailSideBarViewAdapter.current-box;
72 min-height: 248px;
73 vertical-stretch: 0;
74
75 selected(index) => {
76 MailSideBarViewAdapter.current-custom-box = -1;
77 }
78 }
79
80 Rectangle {
81 background: Palette.border;
82 height: 1px;
83 }
84
85 NavigationListView {
86 model: MailSideBarViewAdapter.custom-boxes;
87 current-item <=> MailSideBarViewAdapter.current-custom-box;
88
89 selected(index) => {
90 MailSideBarViewAdapter.current-box = -1;
91 }
92 }
93 }
94 }
95}
96
97export component MailSideBarDialog inherits ModalDialog {
98 in property <length> sidebar-x;
99 in property <length> sidebar-y;
100 in property <length> sidebar-height;
101
102 Rectangle {
103 x: root.sidebar-x - self.width;
104 y: root.sidebar-y;
105 width: 246px;
106 height: root.sidebar-height;
107 background: Palette.control-background;
108
109 HorizontalLayout {
110 padding: UsecasesPalette.use-material ? 0 : 4px;
111
112 side-bar := MailSideBarView {
113 width: 360px;
114 break-layout: true;
115 }
116 }
117
118 animate x {
119 duration: 250ms;
120 easing: cubic-bezier(0, 0, 0, 1);
121 }
122
123 init => {
124 self.x = root.sidebar-x;
125 }
126 }
127}
128
129export global MailBoxViewAdapter {
130 callback search-text-changed(search-text: string);
131
132 in property <string> title: MailSideBarViewAdapter.current-title();
133 in property <[CardListViewItem]> mails: [
134 {
135 title: "Simon Hausmann",
136 note: "1 hour ago",
137 sub-title: "Meeting tomorrow",
138 caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
139 },
140 { title: "Tobias Hunger", note: "1 day ago", sub-title: "Meeting tomorrow", caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." },
141 {
142 title: "Olivier Goffart",
143 note: "2 hour ago",
144 sub-title: "Meeting tomorrow",
145 caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
146 },
147 {
148 title: "Aurindam Jana",
149 note: "5 hour ago",
150 sub-title: "Meeting tomorrow",
151 caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
152 },
153 {
154 title: "Simon Hausmann",
155 note: "7 hour ago",
156 sub-title: "Meeting tomorrow",
157 caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
158 },
159 { title: "Tobias Hunger", note: "1 day ago", sub-title: "Meeting tomorrow", caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." },
160 {
161 title: "Olivier Goffart",
162 note: "8 hour ago",
163 sub-title: "Meeting tomorrow",
164 caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
165 },
166 {
167 title: "Aurindam Jana",
168 note: "9 hour ago",
169 sub-title: "Meeting tomorrow",
170 caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
171 },
172 ];
173}
174
175export component MailBoxView {
176 in property <bool> break-layout;
177
178 callback show-sidebar();
179
180 horizontal-stretch: 1;
181
182 VerticalLayout {
183 spacing: 4px;
184
185 HorizontalLayout {
186 spacing: 8px;
187
188 if root.break-layout : IconButton {
189 icon: Icons.menu;
190
191 clicked => {
192 root.show-sidebar();
193 }
194 }
195
196 TitleText {
197 text: MailBoxViewAdapter.title;
198 min-height: 32px;
199 }
200 }
201
202 MailContainer {
203 VerticalLayout {
204 spacing: 8px;
205
206 ExtendedLineEdit {
207 vertical-stretch: 0;
208 placeholder-text: "Search by Sender";
209
210 Icon {
211 source: Icons.search;
212 }
213
214 edited => {
215 MailBoxViewAdapter.search-text-changed(self.text);
216 }
217 }
218
219 CardListView {
220 model: MailBoxViewAdapter.mails;
221 }
222 }
223 }
224 }
225}
226
227export global MailMessageViewAdapter {
228 callback move-to-archive();
229 callback move-to-junk();
230 callback move-to-trash();
231 callback reply();
232 callback forward();
233 callback send();
234
235 in property <string> message: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
236 in-out property <bool> mute-this-thread: false;
237}
238
239export component MailMessageView {
240 horizontal-stretch: 1;
241
242 MailContainer {
243 HorizontalLayout {
244 spacing: 8px;
245
246 IconButton {
247 icon: Icons.archive;
248
249 clicked => {
250 MailMessageViewAdapter.move-to-archive();
251 }
252 }
253
254 IconButton {
255 icon: Icons.junk;
256
257 clicked => {
258 MailMessageViewAdapter.move-to-junk();
259 }
260 }
261
262 IconButton {
263 icon: Icons.trash;
264
265 clicked => {
266 MailMessageViewAdapter.move-to-trash();
267 }
268 }
269
270 Rectangle {}
271
272 IconButton {
273 icon: Icons.reply;
274
275 clicked => {
276 MailMessageViewAdapter.reply();
277 }
278 }
279
280 IconButton {
281 icon: Icons.forward;
282
283 clicked => {
284 MailMessageViewAdapter.forward();
285 }
286 }
287 }
288
289 VerticalLayout {
290 spacing: 4px;
291
292 text-edit := TextEdit {
293 min-height: 52px;
294 max-height: 94px;
295 wrap: word-wrap;
296 }
297
298 ScrollView {
299 VerticalLayout {
300 x: 0;
301 y: 0;
302
303 mail-text := Text {
304 vertical-alignment: top;
305 font-size: 14px;
306 font-weight: 400;
307 color: Palette.foreground;
308 text: MailMessageViewAdapter.message;
309 wrap: word-wrap;
310 }
311 }
312 }
313
314 HorizontalLayout {
315 Switch {
316 text: @tr("Mute this thread");
317 checked <=> MailMessageViewAdapter.mute-this-thread;
318 }
319
320 Button {
321 text: @tr("Send");
322 primary: true;
323 enabled: text-edit.text != "";
324
325 clicked => {
326 MailMessageViewAdapter.send();
327 }
328 }
329 }
330 }
331 }
332}
333
334export component MailView {
335 in property <bool> break-layout;
336
337 HorizontalLayout {
338 spacing: 16px;
339
340 if !root.break-layout: MailSideBarView { }
341
342 VerticalLayout {
343 spacing: 16px;
344
345 MailBoxView {
346 break-layout: root.break-layout;
347
348 show-sidebar => {
349 sidebar-dialog.show();
350 }
351 }
352
353 MailMessageView { }
354 }
355 }
356
357 sidebar-dialog := MailSideBarDialog {
358 sidebar-x: 0;
359 sidebar-y: root.absolute-position.y;
360 sidebar-height: DialogGlobal.window-height - root.absolute-position.y;
361 }
362}
363