1use std::time::Duration;
2
3use tuix_core::{IntoChildIterator, TreeExt};
4
5use crate::{ButtonEvent, CheckboxEvent, common::*};
6
7use crate::{List};
8
9use crate::AnimationState;
10
11#[derive(Clone, Debug, PartialEq)]
12pub enum TabEvent {
13 SwitchTab(String),
14 CloseTab(String),
15}
16
17pub struct TabBar {
18 list: List,
19}
20
21impl TabBar {
22 pub fn new() -> Self {
23 Self { list: List::new() }
24 }
25}
26
27impl Widget for TabBar {
28 type Ret = Entity;
29 type Data = ();
30 fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
31 self.list.on_build(state, entity);
32
33 entity.set_element(state, "tab_bar")
34 }
35
36 fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
37 self.list.on_event(state, entity, event);
38 }
39}
40
41pub struct Tab {
42 pub name: String,
43 checked: bool,
44
45 on_checked: Option<Box<dyn Fn(&mut Self, &mut State, Entity)>>,
46 on_unchecked: Option<Box<dyn Fn(&mut Self, &mut State, Entity)>>,
47
48 key: Code,
49}
50
51impl Tab {
52 pub fn new(name: &str) -> Self {
53
54 let name = name.to_owned();
55
56 Self {
57 name: name.clone(),
58 checked: false,
59
60 on_checked: None,
61 on_unchecked: None,
62
63 key: Code::Space,
64 }
65 }
66
67 pub fn on_checked<F>(mut self, callback: F) -> Self
68 where
69 F: 'static + Fn(&mut Self, &mut State, Entity)
70 {
71 self.on_checked = Some(Box::new(callback));
72
73 self
74 }
75
76 pub fn on_unchecked<F>(mut self, callback: F) -> Self
77 where
78 F: 'static + Fn(&mut Self, &mut State, Entity)
79 {
80 self.on_unchecked = Some(Box::new(callback));
81
82 self
83 }
84}
85
86impl Widget for Tab {
87 type Ret = Entity;
88 type Data = ();
89 fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
90 entity.set_element(state, "tab")
91 }
92
93 fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
94 if let Some(tab_event) = event.message.downcast::<TabEvent>() {
95 match tab_event {
96 TabEvent::SwitchTab(name) => {
97 if name == &self.name && event.origin != entity {
98 println!("Switch the tab!");
99 state.insert_event(
100 Event::new(CheckboxEvent::Checked)
101 .target(entity)
102 .origin(entity),
103 );
104 }
105 }
106
107 TabEvent::CloseTab(name) => {
108 if name == &self.name {
109 state.remove(entity);
110 }
111 }
112 }
113 }
114
115 if let Some(checkbox_event) = event.message.downcast::<CheckboxEvent>() {
116 match checkbox_event {
117 CheckboxEvent::Switch => {
118 if event.target == entity {
119 if self.checked {
120
121 state.insert_event(
124 Event::new(CheckboxEvent::Unchecked)
125 .target(entity)
126 .origin(entity),
127 );
128 } else {
129
130 state.insert_event(
133 Event::new(CheckboxEvent::Checked)
134 .target(entity)
135 .origin(entity),
136 );
137 }
138 }
139 }
140
141 CheckboxEvent::Check => {
142 self.checked = true;
143 entity.set_checked(state, true);
144 }
145
146 CheckboxEvent::Uncheck => {
147 self.checked = false;
148 entity.set_checked(state, false);
149 }
150
151 CheckboxEvent::Checked => {
152 self.checked = true;
153
154 entity.set_checked(state, true);
155
156 if let Some(callback) = self.on_checked.take() {
157 (callback)(self, state, entity);
158 self.on_checked = Some(callback);
159 }
160 }
161
162 CheckboxEvent::Unchecked => {
163 self.checked = false;
164
165 entity.set_checked(state, false);
166
167 if let Some(callback) = self.on_unchecked.take() {
168 (callback)(self, state, entity);
169 self.on_unchecked = Some(callback);
170 }
171 }
172 }
173 }
174
175 if let Some(window_event) = event.message.downcast::<WindowEvent>() {
176 match window_event {
177 WindowEvent::MouseDown(button) if *button == MouseButton::Left => {
178 if entity == event.target && !entity.is_disabled(state) {
179 state.capture(entity);
180 }
181 }
182
183 WindowEvent::MouseUp(button) if *button == MouseButton::Left => {
184 if entity == event.target && state.mouse.left.pressed == entity {
185 state.release(entity);
186 entity.set_active(state, false);
187 if !entity.is_disabled(state) {
188 if state.hovered == entity {
189 state.insert_event(
190 Event::new(CheckboxEvent::Switch)
191 .target(entity)
192 .origin(entity),
193 );
194 }
195
196 state.insert_event(
197 Event::new(TabEvent::SwitchTab(self.name.clone())).propagate(Propagation::Up).target(entity).origin(entity),
198 )
199 }
200 }
201 }
202
203 WindowEvent::KeyDown(code, _) if *code == self.key => {
204 if state.focused == entity && !entity.is_disabled(state) {
205 state.insert_event(
206 Event::new(ButtonEvent::Pressed)
207 .target(entity)
208 .origin(entity),
209 );
210
211 state.insert_event(
212 Event::new(CheckboxEvent::Switch)
213 .target(entity)
214 .origin(entity),
215 );
216 }
217 }
218
219 WindowEvent::KeyUp(code, _) if *code == self.key => {
220 state.insert_event(
221 Event::new(ButtonEvent::Released)
222 .target(entity)
223 .origin(entity),
224 );
225 }
226
227 _ => {}
228 }
229 }
230 }
231}
232
233pub struct TabView {
234 pub tab_bar: Entity,
235 pub tab_page: Entity,
236}
237
238impl TabView {
239 pub fn new() -> Self {
240 Self {
241 tab_bar: Entity::null(),
242 tab_page: Entity::null(),
243 }
244 }
245}
246
247impl Widget for TabView {
248 type Ret = (Entity, Entity);
249 type Data = ();
250 fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
251 self.tab_bar = TabBar2::new().build(state, entity, |builder| builder);
252
253 self.tab_page = Element::new().build(state, entity, |builder| builder.class("viewport"));
254
255 entity.set_element(state, "tab_manager");
256
257 (self.tab_bar, self.tab_page)
258 }
259
260 fn on_event(&mut self, state: &mut State, _entity: Entity, event: &mut Event) {
261 if let Some(tab_event) = event.message.downcast::<TabEvent>() {
262 match tab_event {
263 TabEvent::SwitchTab(name) => {
264 if event
265 .origin
266 .is_descendant_of(&state.tree, self.tab_bar)
267 || event.target == self.tab_bar
268 {
269 println!("Received request to switch tab: {}", name);
270 for child in self.tab_page.child_iter(&state.tree.clone()) {
271 state.insert_event(
272 Event::new(TabEvent::SwitchTab(name.clone()))
273 .target(child)
274 .propagate(Propagation::Direct)
275 .origin(event.origin),
276 );
277 }
278
279 }
281 }
282
283 TabEvent::CloseTab(name) => {
284 if event
285 .origin
286 .is_descendant_of(&state.tree, self.tab_bar)
287 || event.target == self.tab_bar
288 {
289 for child in self.tab_page.child_iter(&state.tree.clone()) {
290 state.insert_event(
291 Event::new(TabEvent::CloseTab(name.clone()))
292 .target(child)
293 .propagate(Propagation::Direct)
294 .origin(event.origin),
295 );
296 }
297 }
298
299 event.consume();
300 }
301 }
302 }
303 }
304}
305
306pub struct TabContainer {
307 pub name: String,
308}
309
310impl TabContainer {
311 pub fn new(name: &str) -> Self {
312 Self {
313 name: name.to_string(),
314 }
315 }
316}
317
318impl Widget for TabContainer {
319 type Ret = Entity;
320 type Data = ();
321 fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
322 entity.set_element(state, "tab_container")
323 }
324
325 fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
326 if let Some(tab_event) = event.message.downcast::<TabEvent>() {
327 match tab_event {
328 TabEvent::SwitchTab(name) => {
329 if name == &self.name {
332 entity.set_display(state, Display::Flex);
333 } else {
334 entity.set_display(state, Display::None);
335 }
336 }
337
338 TabEvent::CloseTab(name) => {
339 if name == &self.name {
340 state.remove(entity);
341 }
342 }
343 }
344 }
345 }
346}
347
348#[derive(Debug, Clone, PartialEq)]
351pub enum MovableTabEvent {
352 StartMove(Entity),
353 StopMove(Entity),
354 Moving(Entity, f32),
355 Switch(bool),
356}
357
358pub struct TabBar2 {
359 phantom_tab1: Entity,
360 phantom_tab2: Entity,
361 shrink_animation: Animation,
362 grow_animation: Animation,
363 tab_moving: Entity,
364
365 list: List,
366}
367
368impl TabBar2 {
369 pub fn new() -> Self {
370 Self {
371 phantom_tab1: Entity::default(),
372 phantom_tab2: Entity::default(),
373 shrink_animation: Animation::default(),
374 grow_animation: Animation::default(),
375 tab_moving: Entity::null(),
376 list: List::new(),
377 }
378 }
379}
380
381impl Widget for TabBar2 {
382 type Ret = Entity;
383 type Data = ();
384 fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
385 self.list.on_build(state, entity);
386
387 self.phantom_tab1 = Tab::new("phantom1").build(
388 state,
389 entity,
390 |builder| {
391 builder
392 .set_display(Display::None)
393 .set_width(Units::Pixels(30.0))
394 .set_background_color(Color::red())
395 },
396 );
397 self.phantom_tab2 = Tab::new("phantom2").build(
398 state,
399 entity,
400 |builder|
401 builder
402 .set_display(Display::None)
403 .set_background_color(Color::green())
404 );
405
406 let shrink_animation_state = AnimationState::new()
408 .with_duration(std::time::Duration::from_millis(100))
409 .with_keyframe((0.0, Units::Pixels(100.0)))
410 .with_keyframe((1.0, Units::Pixels(0.0)));
411
412 self.shrink_animation = state.style.width.insert_animation(shrink_animation_state);
413
414 let grow_animation_state = AnimationState::new()
416 .with_duration(std::time::Duration::from_millis(100))
417 .with_keyframe((0.0, Units::Pixels(0.0)))
418 .with_keyframe((1.0, Units::Pixels(100.0)));
419
420 self.grow_animation = state.style.width.insert_animation(grow_animation_state);
421
422 entity.set_element(state, "tab_bar")
423 }
424
425 fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
426 self.list.on_event(state, entity, event);
427
428 if let Some(movable_tab_event) = event.message.downcast::<MovableTabEvent>() {
429 match movable_tab_event {
430 MovableTabEvent::StartMove(tab) => {
431 self.tab_moving = *tab;
432 self.phantom_tab1.set_display(state, Display::Flex);
433 self.phantom_tab2.set_display(state, Display::Flex);
434
435 let _ = state
436 .tree
437 .set_prev_sibling(*tab, self.phantom_tab1);
438
439 let tab_width = state.data.get_width(*tab);
443 println!("Tab Width: {}", tab_width);
450
451 self.phantom_tab1
452 .set_height(state, Units::Pixels(30.0));
453 self.phantom_tab1.set_width(state, Units::Pixels(tab_width));
454
455 self.phantom_tab2
456 .set_height(state, Units::Pixels(30.0));
457 self.phantom_tab2.set_width(state, Units::Pixels(0.0));
458
459 if let Some(grow_animation) = state.style.width.get_animation_mut(self.grow_animation) {
460 grow_animation.keyframes.last_mut().unwrap().1 = Pixels(tab_width);
461 grow_animation.duration = Duration::from_millis(tab_width as u64);
462 }
463
464 if let Some(shrink_animation) = state.style.width.get_animation_mut(self.shrink_animation) {
465 shrink_animation.keyframes.first_mut().unwrap().1 = Pixels(tab_width);
466 shrink_animation.duration = Duration::from_millis(tab_width as u64);
467 }
468
469 if let Some(last_child) = state.tree.get_last_child(entity) {
471 if last_child != *tab {
472 state.tree.set_next_sibling(last_child, *tab).unwrap();
473 }
474 }
475
476 state
477 .tree
478 .set_next_sibling(*tab, self.phantom_tab2)
479 .unwrap();
480
481 event.consume();
482 }
483
484 MovableTabEvent::StopMove(tab) => {
485 println!("STOP MOVE");
486 self.tab_moving = Entity::null();
487 if state.data.get_width(self.phantom_tab1) > 0.0 {
491 state
492 .tree
493 .set_prev_sibling(self.phantom_tab1, *tab)
494 .unwrap();
495 } else if state.data.get_width(self.phantom_tab2) > 0.0 {
496 state
497 .tree
498 .set_prev_sibling(self.phantom_tab2, *tab)
499 .unwrap();
500 }
501
502 self.phantom_tab1.set_display(state, Display::None).set_width(state, Pixels(0.0)).set_height(state, Pixels(0.0));
503 self.phantom_tab2.set_display(state, Display::None).set_width(state, Pixels(0.0)).set_height(state, Pixels(0.0));
504 event.consume();
505 }
506
507 MovableTabEvent::Switch(position_state) => {
508 if self.tab_moving != Entity::null() {
509 if !state.style.width.is_animating(self.phantom_tab1) && !state.style.width.is_animating(self.phantom_tab2) {
510 if *position_state {
511 if state.tree.get_next_sibling(event.target)
512 == Some(self.phantom_tab1)
513 {
514 let _ = state
515 .tree
516 .set_prev_sibling(event.target, self.phantom_tab2);
517
518 if state.data.get_width(self.phantom_tab1) != 0.0 {
519 state
520 .style
521 .width
522 .play_animation(self.phantom_tab1, self.shrink_animation);
523 }
524
525 if state.data.get_width(self.phantom_tab2) == 0.0 {
526 state
527 .style
528 .width
529 .play_animation(self.phantom_tab2, self.grow_animation);
530 }
531
532
533 let tab_width = state.data.get_width(self.tab_moving);
534 self.phantom_tab1.set_width(state, Units::Pixels(0.0));
535 self.phantom_tab2.set_width(state, Units::Pixels(tab_width));
536 } else if state.tree.get_next_sibling(event.target)
537 == Some(self.phantom_tab2)
538 {
539 let _ = state
540 .tree
541 .set_prev_sibling(event.target, self.phantom_tab1);
542
543 if state.data.get_width(self.phantom_tab2) != 0.0 {
544 state
545 .style
546 .width
547 .play_animation(self.phantom_tab2, self.shrink_animation);
548 }
549
550 if state.data.get_width(self.phantom_tab1) == 0.0 {
551 state
552 .style
553 .width
554 .play_animation(self.phantom_tab1, self.grow_animation);
555 }
556
557 let tab_width = state.data.get_width(self.tab_moving);
558 self.phantom_tab2.set_width(state, Units::Pixels(0.0));
559 self.phantom_tab1.set_width(state, Units::Pixels(tab_width));
560 }
561 } else {
562 if state.tree.get_prev_sibling(event.target)
563 == Some(self.phantom_tab1)
564 {
565 let _ = state
566 .tree
567 .set_next_sibling(event.target, self.phantom_tab2);
568
569 if state.data.get_width(self.phantom_tab1) != 0.0 {
570 state
571 .style
572 .width
573 .play_animation(self.phantom_tab1, self.shrink_animation);
574 }
575
576 if state.data.get_width(self.phantom_tab2) == 0.0 {
577 state
578 .style
579 .width
580 .play_animation(self.phantom_tab2, self.grow_animation);
581 }
582
583 let tab_width = state.data.get_width(self.tab_moving);
584 self.phantom_tab1.set_width(state, Units::Pixels(0.0));
585 self.phantom_tab2.set_width(state, Units::Pixels(tab_width));
586 } else if state.tree.get_prev_sibling(event.target)
587 == Some(self.phantom_tab2)
588 {
589 let _ = state
590 .tree
591 .set_next_sibling(event.target, self.phantom_tab1);
592
593 if state.data.get_width(self.phantom_tab2) != 0.0 {
594 state
595 .style
596 .width
597 .play_animation(self.phantom_tab2, self.shrink_animation);
598 }
599
600 if state.data.get_width(self.phantom_tab1) == 0.0 {
601 state
602 .style
603 .width
604 .play_animation(self.phantom_tab1, self.grow_animation);
605 }
606
607 let tab_width = state.data.get_width(self.tab_moving);
608 self.phantom_tab2.set_width(state, Units::Pixels(0.0));
609 self.phantom_tab1.set_width(state, Units::Pixels(tab_width));
610 }
611 }
612 }
613
614 }
615 }
616
617 _ => {}
618 }
619 }
620 }
621}
622
623pub struct MovableTab {
624 moving: bool,
625 dragging: bool,
626 pos_down_x: f32,
627 pos_down_y: f32,
628 previous_height: Units,
629 previous_width: Units,
630 position_state: bool,
631 tab: Tab,
632}
633
634impl MovableTab {
635 pub fn new(name: &str) -> Self {
636 Self {
637 moving: false,
638 dragging: false,
639 pos_down_x: 0.0,
640 pos_down_y: 0.0,
641 previous_height: Units::default(),
642 previous_width: Units::default(),
643 position_state: false,
644 tab: Tab::new(name),
645 }
646 }
647}
648
649impl Widget for MovableTab {
650 type Ret = Entity;
651 type Data = ();
652 fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
653
654
655 entity.add_listener(state, |tab: &mut Self, state, entity, event|{
656 if let Some(window_event) = event.message.downcast() {
657 match window_event {
658 MovableTabEvent::Moving(moving_tab, _) => {
659 if *moving_tab != entity {
660
661 let px = state.data.get_posx(*moving_tab);
662 let w = state.data.get_width(*moving_tab);
663
664 let xx = px + w;
665 let ww = w / 2.0;
666
667 let ww2 = state.data.get_width(entity)/2.0;
668
669 if xx < state.data.get_posx(entity) + state.data.get_width(entity) && xx > state.data.get_posx(entity) + state.data.get_width(entity) - ww.min(ww2) {
672 tab.position_state = false;
674 state.insert_event(
675 Event::new(MovableTabEvent::Switch(tab.position_state))
676 .target(entity),
677 );
678 } else if px > state.data.get_posx(entity) && px < state.data.get_posx(entity) + ww.min(ww2) {
680 tab.position_state = true;
682 state.insert_event(
683 Event::new(MovableTabEvent::Switch(tab.position_state))
684 .target(entity),
685 );
686 }
688 }
729 }
730
731 _=> {}
732 }
733 }
734 });
735
736 self.tab.on_build(state, entity)
737 }
738
739 fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
740 self.tab.on_event(state, entity, event);
741
742 if let Some(window_event) = event.message.downcast::<WindowEvent>() {
743 match window_event {
744 WindowEvent::MouseDown(button) => {
745 if *button == MouseButton::Left {
746 self.moving = true;
747
748 self.pos_down_x = state.data.get_posx(entity);
749 self.pos_down_y = state.data.get_posy(entity);
750
751 state.data.set_hoverable(entity, false);
752
753 self.previous_height = entity.get_height(state);
754 self.previous_width = entity.get_width(state);
755
756 entity.set_height(state, Units::Pixels(state.data.get_height(entity)));
757 entity.set_width(state, Units::Pixels(state.data.get_width(entity)));
758
759 let parent = state.tree.get_parent(entity).unwrap();
760 let parent_posx = state.data.get_posx(parent);
761 let parent_posy = state.data.get_posy(parent);
762
763 entity.set_left(state, Units::Pixels(self.pos_down_x - parent_posx));
764 entity.set_top(state, Units::Pixels(self.pos_down_y - parent_posy));
765
766 entity.set_position_type(state, PositionType::SelfDirected);
767 entity.set_z_order(state, 10);
768 state.capture(entity);
769 state.insert_event(
770 Event::new(MovableTabEvent::StartMove(entity)).target(entity),
771 );
772 }
773 }
774
775 WindowEvent::MouseUp(button) => {
776 if *button == MouseButton::Left {
777 self.moving = false;
778 self.dragging = false;
779 entity.set_position_type(state, PositionType::ParentDirected);
782 state.data.set_hoverable(entity, true);
783 entity.set_left(state, Units::Auto);
784 entity.set_top(state, Units::Auto);
785 entity.set_z_order(state, 0);
786 state.release(entity);
787 entity.set_left(state, Units::Auto);
788 entity.set_top(state, Units::Auto);
789 state.insert_event(
790 Event::new(MovableTabEvent::StopMove(entity)).target(entity),
791 );
792 }
793 }
794
795 WindowEvent::MouseMove(x, _) => {
796 if self.moving {
797 let parent = state.tree.get_parent(entity).unwrap();
798 let parent_posx = state.data.get_posx(parent);
799 let dist = *x - state.mouse.left.pos_down.0;
802
803 if dist.abs() > 5.0 {
806 self.dragging = true;
807 }
808
809 if self.dragging {
810 entity.set_left(
811 state,
812 Units::Pixels(self.pos_down_x - parent_posx + dist),
813 );
814 }
816
817 entity.emit(state, MovableTabEvent::Moving(entity, *x));
818
819 }
825
826 }
848
849 _ => {}
850 }
851 }
852 }
853}
854
855
856
857