1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4export component MenuBarItemBase {
5 in property <MenuEntry> entry;
6 in property <brush> default-foreground;
7 in property <brush> hover-foreground;
8 in property <brush> pressed-foreground;
9 in property <brush> default-background;
10 in property <brush> hover-background;
11 in property <brush> pressed-background;
12 in property <length> font-size <=> label.font-size;
13 in property <int> font-weight <=> label.font-weight;
14 in property <length> border-radius <=> background-layer.border-radius;
15
16 callback clicked;
17 callback hovered;
18
19 background-layer := Rectangle {
20 background: root.default-background;
21
22 touch-area := TouchArea {
23 layout := HorizontalLayout {
24 padding-top: root.padding-top;
25 padding-bottom: root.padding-bottom;
26 padding-left: root.padding-left;
27 padding-right: root.padding-right;
28
29 label := Text {
30 text: entry.title;
31 color: root.default-foreground;
32 }
33 }
34
35 pointer-event(event) => {
36 if event.kind == PointerEventKind.down {
37 root.clicked();
38 }
39 }
40
41 changed has-hover => {
42 if self.has-hover {
43 root.hovered();
44 }
45 }
46 }
47 }
48
49 states [
50 pressed when touch-area.pressed : {
51 background-layer.background: root.pressed-background;
52 label.color: root.pressed-foreground;
53 }
54 has-hover when touch-area.has-hover : {
55 background-layer.background: root.hover-background;
56 label.color: root.hover-foreground;
57 }
58 ]
59}
60
61export component MenuBarBase inherits Rectangle {
62 in property <length> spacing <=> layout.spacing;
63 in property <LayoutAlignment> alignment <=> layout.alignment;
64 in property <length> min-layout-height;
65
66 min-height: max(root.min-layout-height, layout.min-height);
67
68 layout := HorizontalLayout {
69 alignment: start;
70 padding: root.padding;
71 padding-left: root.padding-left;
72 padding-right: root.padding-right;
73 padding-top: root.padding-top;
74 padding-bottom: root.padding-bottom;
75
76 @children
77 }
78}
79
80export component MenuFrameBase inherits Rectangle {
81 in property <length> spacing <=> layout.spacing;
82 in property <length> layout-min-width;
83
84 clip: true;
85 min-width: max(root.layout-min-width, layout.min-width);
86
87 layout := VerticalLayout {
88 padding: root.padding;
89
90 @children
91 }
92}
93
94export component MenuItemBase {
95 in property <brush> default-foreground;
96 in property <brush> default-background;
97 in property <brush> current-foreground;
98 in property <brush> current-background;
99 in property <brush> separator-color;
100 in property <length> font-size <=> label.font-size;
101 in property <int> font-weight <=> label.font-weight;
102 in property <length> border-radius <=> background-layer.border-radius;
103 in property <bool> is-current;
104 in property <MenuEntry> entry;
105 in property <image> sub-menu-icon;
106 in property <length> spacing <=> layout.spacing;
107 in property <length> icon-size;
108
109 callback set-current();
110 callback clear-current();
111 callback activate(entry : MenuEntry, y: length);
112
113 background-layer := Rectangle {
114 background: root.default-background;
115
116 touch-area := TouchArea {
117 visible: !entry.is-separator;
118 enabled: entry.enabled;
119
120 layout := HorizontalLayout {
121 padding-top: root.padding-top;
122 padding-bottom: root.padding-bottom;
123 padding-left: root.padding-left;
124 padding-right: root.padding-right;
125
126 label := Text {
127 text: entry.title;
128 color: root.default-foreground;
129 vertical-alignment: center;
130 }
131
132 if entry.has-sub-menu : Image {
133 width: root.icon-size;
134 y: (parent.height - self.height) / 2;
135 source: root.sub-menu-icon;
136 colorize: label.color;
137 accessible-role: none;
138 }
139 }
140
141 pointer-event(event) => {
142 if event.kind == PointerEventKind.move && !root.is-current {
143 root.set-current()
144 } else if event.kind == PointerEventKind.down && entry.has-sub-menu && entry.enabled {
145 activate(entry, self.absolute-position.y);
146 } else if event.kind == PointerEventKind.up
147 && self.mouse-y > 0 && self.mouse-y < self.height
148 && self.mouse-x > 0 && self.mouse-x < self.width
149 && entry.enabled {
150 // can't put this in `clicked` because then the menu would close causing a panic in the pointer-event
151 activate(entry, self.absolute-position.y);
152 }
153 }
154
155 changed has-hover => {
156 if !self.has-hover && root.is-current {
157 root.clear-current();
158 }
159 }
160 }
161
162 if entry.is-separator: Rectangle {
163 height: 1px;
164 background: root.separator-color;
165 }
166 }
167
168 states [
169 is-current when root.is-current : {
170 background-layer.background: root.current-background;
171 label.color: root.current-foreground;
172 }
173 disabled when !entry.enabled : {
174 label.opacity: 0.5;
175 }
176 ]
177}
178