1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT
3
4import { StyleMetrics } from "std-widgets.slint";
5
6component SideBarItem inherits Rectangle {
7 in property <bool> selected;
8 in property <bool> has-focus;
9 in-out property <string> text <=> label.text;
10
11 callback clicked <=> touch.clicked;
12
13 min-height: l.preferred-height;
14
15 states [
16 pressed when touch.pressed : {
17 state.opacity: 0.8;
18 }
19 hover when touch.has-hover : {
20 state.opacity: 0.6;
21 }
22 selected when root.selected : {
23 state.opacity: 1;
24 }
25 focused when root.has-focus : {
26 state.opacity: 0.8;
27 }
28 ]
29
30 state := Rectangle {
31 opacity: 0;
32 background: StyleMetrics.window-background;
33
34 animate opacity { duration: 150ms; }
35 }
36
37 l := HorizontalLayout {
38 y: (parent.height - self.height) / 2;
39 padding: StyleMetrics.layout-padding;
40 spacing: 0px;
41
42 label := Text {
43 color: StyleMetrics.default-text-color;
44 vertical-alignment: center;
45 }
46 }
47
48 touch := TouchArea {
49 width: 100%;
50 height: 100%;
51 }
52}
53
54export component SideBar inherits Rectangle {
55 in property <[string]> model: [];
56 in property <string> title <=> label.text;
57 out property <int> current-item: 0;
58 out property <int> current-focused: fs.has-focus ? fs.focused-tab : -1; // The currently focused tab
59
60 width: 180px;
61 forward-focus: fs;
62 accessible-role: tab;
63 accessible-delegate-focus: root.current-focused >= 0 ? root.current-focused : root.current-item;
64
65 Rectangle {
66 background: StyleMetrics.window-background.darker(0.2);
67
68 fs := FocusScope {
69 key-pressed(event) => {
70 if (event.text == "\n") {
71 root.current-item = root.current-focused;
72 return accept;
73 }
74 if (event.text == Key.UpArrow) {
75 self.focused-tab = Math.max(self.focused-tab - 1, 0);
76 return accept;
77 }
78 if (event.text == Key.DownArrow) {
79 self.focused-tab = Math.min(self.focused-tab + 1, root.model.length - 1);
80 return accept;
81 }
82 return reject;
83 }
84
85 key-released(event) => {
86 if (event.text == " ") {
87 root.current-item = root.current-focused;
88 return accept;
89 }
90 return reject;
91 }
92
93 property <int> focused-tab: 0;
94
95 x: 0;
96 width: 0; // Do not react on clicks
97 }
98 }
99
100 VerticalLayout {
101 padding-top: StyleMetrics.layout-padding;
102 padding-bottom: StyleMetrics.layout-padding;
103 spacing: StyleMetrics.layout-spacing;
104 alignment: start;
105
106 label := Text {
107 font-size: 16px;
108 horizontal-alignment: center;
109 }
110
111 navigation := VerticalLayout {
112 alignment: start;
113 vertical-stretch: 0;
114 for item[index] in root.model : SideBarItem {
115 clicked => { root.current-item = index; }
116
117 has-focus: index == root.current-focused;
118 text: item;
119 selected: index == root.current-item;
120 }
121 }
122
123 VerticalLayout {
124 bottom := VerticalLayout {
125 padding-left: StyleMetrics.layout-padding;
126 padding-right: StyleMetrics.layout-padding;
127
128 @children
129 }
130 }
131 }
132}
133