1use {
2 crate::{
3 tab_close_button::{TabCloseButtonAction, TabCloseButton},
4 makepad_draw::*,
5 }
6};
7
8live_design!{
9 link widgets;
10 use link::theme::*;
11 use link::widgets::*;
12 use makepad_draw::shader::std::*;
13
14 pub TabBase = {{Tab}} {}
15 pub Tab = <TabBase> {
16 width: Fit, height: Fit, align: {x: 0.0, y: 0.5}
19 padding: <THEME_MSPACE_3> { top: (THEME_SPACE_2 * 1.2) }
20 margin: {right: (THEME_SPACE_1), top: (THEME_SPACE_FACTOR)}
21
22 close_button: <TabCloseButton> {}
23
24 draw_text: {
25 text_style: <THEME_FONT_REGULAR> {
26 font_size: (THEME_FONT_SIZE_P)
27 }
28 instance hover: 0.0
29 instance active: 0.0
30
31 uniform color: (THEME_COLOR_LABEL_INNER_INACTIVE)
32 uniform color_hover: (THEME_COLOR_LABEL_INNER_HOVER)
33 uniform color_active: (THEME_COLOR_LABEL_INNER_ACTIVE)
34
35 fn get_color(self) -> vec4 {
36 return mix(
37 mix(
38 self.color,
39 self.color_active,
40 self.active
41 ),
42 self.color_hover,
43 self.hover
44 )
45 }
46 }
47
48 draw_bg: {
49 instance hover: float
50 instance active: float
51
52 uniform border_size: 1.
53 uniform border_radius: (THEME_CORNER_RADIUS)
54 uniform color_dither: 1.
55
56 uniform color: (THEME_COLOR_D_HIDDEN)
57 uniform color_hover: (THEME_COLOR_D_2)
58 uniform color_active: (THEME_COLOR_BG_APP)
59
60 uniform border_color_1: (THEME_COLOR_U_HIDDEN)
61 uniform border_color_1_hover: (THEME_COLOR_U_HIDDEN)
62 uniform border_color_1_active: (THEME_COLOR_BEVEL_OUTSET_1)
63
64 uniform border_color_2: (THEME_COLOR_D_HIDDEN)
65 uniform border_color_2_hover: (THEME_COLOR_D_HIDDEN)
66 uniform border_color_2_active: (THEME_COLOR_D_HIDDEN)
67
68 uniform overlap_fix: 1.
69
70 fn pixel(self) -> vec4 {
71 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
72 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
73
74 let border_sz_uv = vec2(
75 self.border_size / self.rect_size.x,
76 self.border_size / self.rect_size.y
77 )
78
79 let scale_factor_border = vec2(
80 self.rect_size.x / self.rect_size.x,
81 self.rect_size.y / self.rect_size.y
82 );
83
84 let gradient_border = vec2(
85 self.pos.x * scale_factor_border.x + dither,
86 self.pos.y * scale_factor_border.y + dither
87 )
88
89 let sz_inner_px = vec2(
90 self.rect_size.x - self.border_size * 2.,
91 self.rect_size.y - self.border_size * 2.
92 );
93
94 let scale_factor_fill = vec2(
95 self.rect_size.x / sz_inner_px.x,
96 self.rect_size.y / sz_inner_px.y
97 );
98
99 let gradient_fill = vec2(
100 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
101 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
102 )
103
104 sdf.box_y(
105 self.border_size,
106 self.border_size,
107 self.rect_size.x - self.border_size * 2.,
108 self.rect_size.y,
109 self.border_radius,
110 self.border_size * 0.5
111 )
112
113 sdf.fill_keep(
114 mix(
115 mix(
116 self.color,
117 self.color_hover,
118 self.hover
119 ),
120 self.color_active,
121 self.active
122 )
123 )
124
125 sdf.stroke(
126 mix(
127 mix(
128 mix(self.border_color_1, self.border_color_2, gradient_border.y),
129 mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
130 self.hover
131 ),
132 mix(self.border_color_1_active, self.border_color_2_active, gradient_border.y),
133 self.active
134 ), self.border_size
135 )
136
137 return sdf.result
138 }
139 }
140
141 animator: {
142 hover = {
143 default: off
144 off = {
145 from: {all: Forward {duration: 0.2}}
146 apply: {
147 draw_bg: {hover: 0.0}
148 draw_text: {hover: 0.0}
149 }
150 }
151
152 on = {
153 cursor: Hand,
154 from: {all: Forward {duration: 0.1}}
155 apply: {
156 draw_bg: {hover: [{time: 0.0, value: 1.0}]}
157 draw_text: {hover: [{time: 0.0, value: 1.0}]}
158 }
159 }
160 }
161
162 active = {
163 default: off
164 off = {
165 from: {all: Forward {duration: 0.3}}
166 apply: {
167 close_button: {draw_button: {active: 0.0}}
168 draw_bg: {active: 0.0}
169 draw_text: {active: 0.0}
170 }
171 }
172
173 on = {
174 from: {all: Snap}
175 apply: {
176 close_button: {draw_button: {active: 1.0}}
177 draw_bg: {active: 1.0}
178 draw_text: {active: 1.0}
179 }
180 }
181 }
182 }
183 }
184
185 pub TabFlat = <Tab> {
186 margin: 0.
187 padding: <THEME_MSPACE_3> { }
188
189 draw_bg: {
190 border_size: 1.
191 border_radius: 0.5
192 color_dither: 1.
193
194 color: (THEME_COLOR_D_HIDDEN)
195 color_hover: (THEME_COLOR_D_HIDDEN)
196 color_active: (THEME_COLOR_FG_APP)
197
198 border_color_1: (THEME_COLOR_U_HIDDEN)
199 border_color_1_hover: (THEME_COLOR_U_HIDDEN)
200 border_color_1_active: (THEME_COLOR_U_HIDDEN)
201
202 border_color_2: (THEME_COLOR_D_HIDDEN)
203 border_color_2_hover: (THEME_COLOR_D_HIDDEN)
204 border_color_2_active: (THEME_COLOR_D_HIDDEN)
205
206 overlap_fix: 0.
207 }
208 }
209
210 pub TabGradientX = <Tab> {
211 draw_bg: {
212 border_size: 1.
213 border_radius: (THEME_CORNER_RADIUS)
214 color_dither: 1.
215
216 uniform color_1: (THEME_COLOR_D_HIDDEN)
217 uniform color_1_hover: (THEME_COLOR_D_2)
218 uniform color_1_active: (THEME_COLOR_BG_APP)
219
220 uniform color_2: (THEME_COLOR_D_HIDDEN)
221 uniform color_2_hover: (THEME_COLOR_D_2)
222 uniform color_2_active: (THEME_COLOR_BG_APP)
223
224 border_color_1: (THEME_COLOR_U_HIDDEN)
225 border_color_1_hover: (THEME_COLOR_U_HIDDEN)
226 border_color_1_active: (THEME_COLOR_BEVEL_OUTSET_1)
227
228 border_color_2: (THEME_COLOR_D_HIDDEN)
229 border_color_2_hover: (THEME_COLOR_D_HIDDEN)
230 border_color_2_active: (THEME_COLOR_D_HIDDEN)
231
232 fn pixel(self) -> vec4 {
233 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
234 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
235
236 let border_sz_uv = vec2(
237 self.border_size / self.rect_size.x,
238 self.border_size / self.rect_size.y
239 )
240
241 let scale_factor_border = vec2(
242 self.rect_size.x / self.rect_size.x,
243 self.rect_size.y / self.rect_size.y
244 );
245
246 let gradient_border = vec2(
247 self.pos.x * scale_factor_border.x + dither,
248 self.pos.y * scale_factor_border.y + dither
249 )
250
251 let sz_inner_px = vec2(
252 self.rect_size.x - self.border_size * 2.,
253 self.rect_size.y - self.border_size * 2.
254 );
255
256 let scale_factor_fill = vec2(
257 self.rect_size.x / sz_inner_px.x,
258 self.rect_size.y / sz_inner_px.y
259 );
260
261 let gradient_fill = vec2(
262 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
263 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
264 )
265
266 sdf.box_y(
267 self.border_size,
268 self.border_size,
269 self.rect_size.x - self.border_size * 2.,
270 self.rect_size.y,
271 self.border_radius,
272 self.border_size * 0.5
273 )
274
275 sdf.fill_keep(
276 mix(
277 mix(
278 mix(self.color_1, self.color_2, gradient_fill.x),
279 mix(self.color_1_hover, self.color_2_hover, grydient_fill.x),
280 self.hover
281 ),
282 mix(self.color_1_active, self.color_2_active, gradient_fill.x),
283 self.active
284 )
285 )
286
287 sdf.stroke(
288 mix(
289 mix(
290 mix(self.border_color_1, self.border_color_2, gradient_border.y),
291 mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
292 self.hover
293 ),
294 mix(self.border_color_1_active, self.border_color_2_active, gradient_border.y),
295 self.active
296 ), self.border_size
297 )
298
299 return sdf.result
300 }
301 }
302 }
303
304 pub TabGradientY = <TabGradientX> {
305 draw_bg: {
306 border_size: (THEME_BEVELING)
307 border_radius: (THEME_CORNER_RADIUS)
308 color_dither: 1.
309
310 color_1: (THEME_COLOR_D_HIDDEN)
311 color_1_hover: (THEME_COLOR_U_HIDDEN)
312 color_1_active: (THEME_COLOR_BG_APP)
313
314 color_2: (THEME_COLOR_D_HIDDEN)
315 color_2_hover: (THEME_COLOR_U_HIDDEN)
316 color_2_active: (THEME_COLOR_BG_APP)
317
318 border_color_1: (THEME_COLOR_U_HIDDEN)
319 border_color_1_hover: (THEME_COLOR_U_HIDDEN)
320 border_color_1_active: (THEME_COLOR_BEVEL_OUTSET_1)
321
322 border_color_2: (THEME_COLOR_D_HIDDEN)
323 border_color_2_hover: (THEME_COLOR_D_HIDDEN)
324 border_color_2_active: (THEME_COLOR_D_HIDDEN)
325
326 fn pixel(self) -> vec4 {
327 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
328 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
329
330 let border_sz_uv = vec2(
331 self.border_size / self.rect_size.x,
332 self.border_size / self.rect_size.y
333 )
334
335 let scale_factor_border = vec2(
336 self.rect_size.x / self.rect_size.x,
337 self.rect_size.y / self.rect_size.y
338 );
339
340 let gradient_border = vec2(
341 self.pos.x * scale_factor_border.x + dither,
342 self.pos.y * scale_factor_border.y + dither
343 )
344
345 let sz_inner_px = vec2(
346 self.rect_size.x - self.border_size * 2.,
347 self.rect_size.y - self.border_size * 2.
348 );
349
350 let scale_factor_fill = vec2(
351 self.rect_size.x / sz_inner_px.x,
352 self.rect_size.y / sz_inner_px.y
353 );
354
355 let gradient_fill = vec2(
356 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
357 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
358 )
359
360 sdf.box_y(
361 self.border_size,
362 self.border_size,
363 self.rect_size.x - self.border_size * 2.,
364 self.rect_size.y,
365 self.border_radius,
366 self.border_size * 0.5
367 )
368
369 sdf.fill_keep(
370 mix(
371 mix(
372 mix(self.color_1, self.color_2, gradient_fill.y),
373 mix(self.color_1_hover, self.color_2_hover, gradient_fill.y),
374 self.hover
375 ),
376 mix(self.color_1_active, self.color_2_active, gradient_fill.y),
377 self.active
378 )
379 )
380
381 sdf.stroke(
382 mix(
383 mix(
384 mix(self.border_color_1, self.border_color_2, gradient_border.y),
385 mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
386 self.hover
387 ),
388 mix(self.border_color_1_active, self.border_color_2_active, gradient_border.y),
389 self.active
390 ), self.border_size
391 )
392
393 return sdf.result
394 }
395 }
396
397 }
398
399}
400
401#[derive(Live, LiveHook, LiveRegister)]
402pub struct Tab {
403 #[rust] is_active: bool,
404 #[rust] is_dragging: bool,
405
406 #[live] draw_bg: DrawQuad,
407 #[live] draw_icon: DrawIcon,
408 #[live] draw_text: DrawText,
409 #[live] icon_walk: Walk,
410 #[animator] animator: Animator,
413
414 #[live] close_button: TabCloseButton,
415
416 #[live] closeable: bool,
418 #[live] hover: f32,
419 #[live] active: f32,
420
421 #[live(10.0)] min_drag_dist: f64,
422
423 #[walk] walk: Walk,
424 #[layout] layout: Layout,
425
426}
427
428pub enum TabAction {
429 WasPressed,
430 CloseWasPressed,
431 ShouldTabStartDrag,
432 ShouldTabStopDrag
433 }
435
436
437impl Tab {
438
439 pub fn is_active(&self) -> bool {
440 self.is_active
441 }
442
443 pub fn set_is_active(&mut self, cx: &mut Cx, is_active: bool, animate: Animate) {
444 self.is_active = is_active;
445 self.animator_toggle(cx, is_active, animate, id!(active.on), id!(active.off));
446 }
447
448 pub fn draw(&mut self, cx: &mut Cx2d, name: &str) {
449 self.draw_bg.begin(cx, self.walk, self.layout);
451 if self.closeable{
453 self.close_button.draw(cx);
454 }
455
456 self.draw_icon.draw_walk(cx, self.icon_walk);
457 self.draw_text.draw_walk(cx, Walk::fit(), Align::default(), name);
459 self.draw_bg.end(cx);
461
462 }
466
467 pub fn area(&self) -> Area {
468 self.draw_bg.area()
469 }
470
471 pub fn handle_event_with(
472 &mut self,
473 cx: &mut Cx,
474 event: &Event,
475 dispatch_action: &mut dyn FnMut(&mut Cx, TabAction),
476 ) {
477 self.animator_handle_event(cx, event);
478
479 let mut block_hover_out = false;
480 match self.close_button.handle_event(cx, event) {
481 TabCloseButtonAction::WasPressed if self.closeable => dispatch_action(cx, TabAction::CloseWasPressed),
482 TabCloseButtonAction::HoverIn => block_hover_out = true,
483 TabCloseButtonAction::HoverOut => self.animator_play(cx, id!(hover.off)),
484 _ => ()
485 };
486
487 match event.hits(cx, self.draw_bg.area()) {
488 Hit::FingerHoverIn(_) => {
489 self.animator_play(cx, id!(hover.on));
490 }
491 Hit::FingerHoverOut(_) => if !block_hover_out {
492 self.animator_play(cx, id!(hover.off));
493 }
494 Hit::FingerMove(e) => {
495 if !self.is_dragging && (e.abs - e.abs_start).length() > self.min_drag_dist {
496 self.is_dragging = true;
497 dispatch_action(cx, TabAction::ShouldTabStartDrag);
498 }
499 }
500 Hit::FingerUp(_) => {
501 if self.is_dragging {
502 dispatch_action(cx, TabAction::ShouldTabStopDrag);
503 self.is_dragging = false;
504 }
505 }
506 Hit::FingerDown(fde) => {
507 if fde.is_primary_hit() {
509 dispatch_action(cx, TabAction::WasPressed);
510 } else if self.closeable && fde.mouse_button().is_some_and(|b| b.is_middle()) {
511 dispatch_action(cx, TabAction::CloseWasPressed);
512 }
513 }
514 _ => {}
515 }
516 }
517}
518