From e2b79416f2813a64916ff8187dccc69fbe39fce5 Mon Sep 17 00:00:00 2001
From: MrGVSV <gino.valente.code@gmail.com>
Date: Sat, 15 Jan 2022 00:04:31 -0800
Subject: [PATCH] Removed add/remove tabs due to bugs

Main bug is that removals or additions to the widget tree are not
properly reflected in the focuss tree (i.e. out of order)
---
 examples/tabs/tab.rs         | 68 +++---------------------------------
 examples/tabs/tab_bar.rs     | 62 ++++----------------------------
 examples/tabs/tab_box.rs     |  9 +++--
 examples/tabs/tab_content.rs |  2 ++
 examples/tabs/tabs.rs        | 52 +++++++++------------------
 examples/tabs/theming.rs     |  1 -
 6 files changed, 35 insertions(+), 159 deletions(-)

diff --git a/examples/tabs/tab.rs b/examples/tabs/tab.rs
index 63b34d9..5af1081 100644
--- a/examples/tabs/tab.rs
+++ b/examples/tabs/tab.rs
@@ -2,7 +2,7 @@ use kayak_ui::{
     core::{
         render_command::RenderCommand,
         styles::{LayoutType, Style, StyleProp, Units},
-        Bound, EventType, Handler, KeyCode, OnEvent, rsx, use_state, widget,
+        Bound, EventType, OnEvent, rsx, use_state, widget,
     },
     widgets::{Background, Text},
 };
@@ -16,17 +16,17 @@ enum TabHoverState {
     Active,
 }
 
+/// The actual tab, displayed in a [TabBar](crate::tab_bar::TabBar)
 #[widget]
-pub fn Tab(context: &mut KayakContext, content: String, selected: bool, on_request_remove: Handler) {
+pub fn Tab(context: &mut KayakContext, content: String, selected: bool) {
     let theme = context.create_consumer::<TabTheme>().unwrap_or_default();
+    let (focus_state, set_focus_state, ..) = use_state!(false);
     let (hover_state, set_hover_state, ..) = use_state!(TabHoverState::None);
     match hover_state {
         TabHoverState::Inactive if selected => set_hover_state(TabHoverState::Active),
         TabHoverState::Active if !selected => set_hover_state(TabHoverState::Inactive),
         _ => {}
     };
-    let (focus_state, set_focus_state, ..) = use_state!(false);
-    let (is_exit_hovered, set_is_exit_hovered, ..) = use_state!(false);
 
     let event_handler = OnEvent::new(move |_, event| {
         match event.event_type {
@@ -50,36 +50,6 @@ pub fn Tab(context: &mut KayakContext, content: String, selected: bool, on_reque
         }
     });
 
-    let exit_btn_event_handler = OnEvent::new(move |_, event| {
-        match event.event_type {
-            EventType::Hover => {
-                set_is_exit_hovered(true);
-            }
-            EventType::MouseOut => {
-                set_is_exit_hovered(false);
-            }
-            EventType::Focus => {
-                set_is_exit_hovered(true);
-            }
-            EventType::Blur => {
-                set_is_exit_hovered(false);
-            }
-            EventType::Click => {
-                // Stop propagation so we don't select a deleted tab!
-                event.stop_propagation();
-                on_request_remove.call(());
-            }
-            EventType::KeyDown(evt) => {
-                if evt.key() == KeyCode::Return || evt.key() == KeyCode::Space {
-                    // Stop propagation so we don't select a deleted tab!
-                    event.stop_propagation();
-                    on_request_remove.call(());
-                }
-            }
-            _ => {}
-        }
-    });
-
     let tab_color = match hover_state {
         TabHoverState::None if selected => theme.get().active_tab.normal,
         TabHoverState::None => theme.get().inactive_tab.normal,
@@ -96,7 +66,6 @@ pub fn Tab(context: &mut KayakContext, content: String, selected: bool, on_reque
         ..Default::default()
     };
 
-
     let border_width = Units::Pixels(2.0);
     let border_styles = Style {
         background_color: if focus_state {
@@ -125,32 +94,6 @@ pub fn Tab(context: &mut KayakContext, content: String, selected: bool, on_reque
         ..Default::default()
     };
 
-    let exit_styles = Style {
-        background_color: if is_exit_hovered {
-            let mut darkened = theme.get().inactive_tab.hovered;
-            darkened.r -= 0.025;
-            darkened.g -= 0.025;
-            darkened.b -= 0.025;
-            StyleProp::Value(darkened)
-        } else {
-            StyleProp::Value(tab_color)
-        },
-        width: StyleProp::Value(Units::Pixels(theme.get().tab_height - 4.0)),
-        height: StyleProp::Value(Units::Pixels(theme.get().tab_height - 4.0)),
-        top: StyleProp::Value(Units::Stretch(0.175)),
-        bottom: StyleProp::Value(Units::Stretch(1.0)),
-        left: StyleProp::Value(Units::Stretch(1.0)),
-        ..Default::default()
-    };
-
-    let exit_text_styles = Style {
-        left: StyleProp::Value(Units::Stretch(1.0)),
-        right: StyleProp::Value(Units::Stretch(1.0)),
-        top: StyleProp::Value(Units::Stretch(0.35)),
-        bottom: StyleProp::Value(Units::Stretch(1.0)),
-        ..Default::default()
-    };
-
     self.styles = Some(Style {
         render_command: StyleProp::Value(RenderCommand::Layout),
         height: StyleProp::Value(Units::Pixels(theme.get().tab_height)),
@@ -162,9 +105,6 @@ pub fn Tab(context: &mut KayakContext, content: String, selected: bool, on_reque
         <Background focusable={Some(true)} on_event={Some(event_handler)} styles={Some(border_styles)}>
             <Background styles={Some(bg_styles)}>
                 <Text content={content} size={12.0} styles={Some(text_styles)} />
-                <Background focusable={Some(true)} on_event={Some(exit_btn_event_handler)} styles={Some(exit_styles)}>
-                    <Text content={"X".to_string()} size={8.0} styles={Some(exit_text_styles)} />
-                </Background>
             </Background>
         </Background>
     }
diff --git a/examples/tabs/tab_bar.rs b/examples/tabs/tab_bar.rs
index 2d9cece..12608a8 100644
--- a/examples/tabs/tab_bar.rs
+++ b/examples/tabs/tab_bar.rs
@@ -2,18 +2,18 @@ use kayak_ui::{
     core::{
         styles::{LayoutType, Style, StyleProp, Units},
         Bound, constructor, EventType, Handler, KeyCode, OnEvent,
-        rsx, use_state, VecTracker, widget,
+        rsx, VecTracker, widget,
     },
-    widgets::{Background, Element, Text},
+    widgets::Background,
 };
 
 use crate::tab::Tab;
 use crate::TabTheme;
 
+/// A widget displaying a collection of tabs in a horizontal bar
 #[widget]
-pub fn TabBar(context: &mut KayakContext, tabs: Vec<String>, selected: usize, on_select_tab: Handler<usize>, on_add_tab: Handler, on_remove_tab: Handler<usize>) {
+pub fn TabBar(context: &mut KayakContext, tabs: Vec<String>, selected: usize, on_select_tab: Handler<usize>) {
     let theme = context.create_consumer::<TabTheme>().unwrap_or_default();
-    let (is_add_hovered, set_is_add_hovered, ..) = use_state!(false);
 
     let tabs = tabs.iter().enumerate().map(|(index, tab)| {
         let on_select = on_select_tab.clone();
@@ -24,6 +24,7 @@ pub fn TabBar(context: &mut KayakContext, tabs: Vec<String>, selected: usize, on
                 }
                 EventType::KeyDown(evt) => {
                     if evt.key() == KeyCode::Return || evt.key() == KeyCode::Space {
+                        // We want the focused tab to also be selected by `Enter` or `Space`
                         on_select.call(index);
                     }
                 }
@@ -31,59 +32,11 @@ pub fn TabBar(context: &mut KayakContext, tabs: Vec<String>, selected: usize, on
             }
         });
 
-        let on_remove = on_remove_tab.clone();
-        let on_request_remove = Handler::new(move |_| {
-            on_remove.call(index);
-        });
-
         constructor! {
-            <Tab content={tab.clone()} on_event={Some(tab_event_handler.clone())} selected={selected == index} on_request_remove={on_request_remove} />
+            <Tab content={tab.clone()} on_event={Some(tab_event_handler.clone())} selected={selected == index} />
         }
     }).collect::<Vec<_>>();
 
-    let add_btn_event_handler = OnEvent::new(move |_, event| match event.event_type {
-        EventType::Hover => {
-            set_is_add_hovered(true);
-        }
-        EventType::MouseOut => {
-            set_is_add_hovered(false);
-        }
-        EventType::Focus => {
-            set_is_add_hovered(true);
-        }
-        EventType::Blur => {
-            set_is_add_hovered(false);
-        }
-        EventType::Click => {
-            on_add_tab.call(());
-        }
-        EventType::KeyDown(evt) => {
-            if evt.key() == KeyCode::Return || evt.key() == KeyCode::Space {
-                on_add_tab.call(());
-            }
-        }
-        _ => {}
-    });
-
-    let add_btn_styles = Style {
-        height: StyleProp::Value(Units::Pixels(theme.get().tab_height)),
-        width: StyleProp::Value(Units::Pixels(theme.get().tab_height)),
-        border_radius: StyleProp::Value((10.0, 10.0, 10.0, 10.0)),
-        ..Default::default()
-    };
-    let add_btn_text_styles = Style {
-        color: if is_add_hovered {
-            StyleProp::Value(theme.get().text.hovered)
-        } else {
-            StyleProp::Value(theme.get().text.normal)
-        },
-        left: StyleProp::Value(Units::Stretch(1.0)),
-        right: StyleProp::Value(Units::Stretch(1.0)),
-        height: StyleProp::Value(Units::Percentage(100.0)),
-        width: StyleProp::Value(Units::Percentage(100.0)),
-        ..Default::default()
-    };
-
     let background_styles = Style {
         layout_type: StyleProp::Value(LayoutType::Row),
         background_color: StyleProp::Value(theme.get().bg),
@@ -95,9 +48,6 @@ pub fn TabBar(context: &mut KayakContext, tabs: Vec<String>, selected: usize, on
     rsx! {
         <Background styles={Some(background_styles)}>
             <VecTracker data={tabs} />
-            <Element focusable={Some(true)} on_event={Some(add_btn_event_handler)} styles={Some(add_btn_styles)}>
-                <Text content={"+".to_string()} size={16.0} styles={Some(add_btn_text_styles)} />
-            </Element>
         </Background>
     }
 }
\ No newline at end of file
diff --git a/examples/tabs/tab_box.rs b/examples/tabs/tab_box.rs
index 0f7353f..1931c0a 100644
--- a/examples/tabs/tab_box.rs
+++ b/examples/tabs/tab_box.rs
@@ -13,12 +13,17 @@ use crate::tab_content::TabContent;
 
 #[derive(Debug, Default, Clone, PartialEq)]
 pub struct TabData {
+    /// The name of this tab
     pub name: String,
+    /// The content to display for this tab, wrapped in a [Fragment]
     pub content: Fragment,
 }
 
+/// The actual tab container widget.
+///
+/// This houses both the tab bar and its content.
 #[widget]
-pub fn TabBox(context: &mut KayakContext, tabs: Vec<TabData>, initial_tab: usize, on_add_tab: Handler, on_remove_tab: Handler<usize>) {
+pub fn TabBox(context: &mut KayakContext, tabs: Vec<TabData>, initial_tab: usize) {
     let theme = context.create_consumer::<TabTheme>().unwrap_or_default();
     let (selected, set_selected, ..) = use_state!(initial_tab);
 
@@ -41,7 +46,7 @@ pub fn TabBox(context: &mut KayakContext, tabs: Vec<TabData>, initial_tab: usize
 
     rsx! {
         <>
-            <TabBar tabs={tab_names} selected={selected} on_select_tab={on_select_tab} on_add_tab={on_add_tab} on_remove_tab={on_remove_tab} />
+            <TabBar tabs={tab_names} selected={selected} on_select_tab={on_select_tab} />
             <TabContent tabs={tab_content} selected={selected}  />
         </>
     }
diff --git a/examples/tabs/tab_content.rs b/examples/tabs/tab_content.rs
index 1b5b024..0dbde71 100644
--- a/examples/tabs/tab_content.rs
+++ b/examples/tabs/tab_content.rs
@@ -9,11 +9,13 @@ use kayak_ui::{
 
 use crate::TabTheme;
 
+/// A widget that displays the selected tab's content
 #[widget]
 pub fn TabContent(context: &mut KayakContext, tabs: Vec<Fragment>, selected: usize) {
     let theme = context.create_consumer::<TabTheme>().unwrap_or_default();
 
     if selected >= tabs.len() {
+        // Invalid tab -> don't do anything
         return;
     }
 
diff --git a/examples/tabs/tabs.rs b/examples/tabs/tabs.rs
index 926ab29..1a169e1 100644
--- a/examples/tabs/tabs.rs
+++ b/examples/tabs/tabs.rs
@@ -1,3 +1,9 @@
+//! This example demonstrates how one might create a tab system
+//!
+//! Additionally, it showcases focus navigation. Press `Tab` and `Shift + Tab` to move
+//! between focusable widgets. This example also sets it up so that `Enter` or `Space`
+//! can be used in place of a normal click.
+
 use bevy::{
     prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut},
     window::WindowDescriptor,
@@ -8,7 +14,7 @@ use kayak_ui::{
     bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle},
     core::{
         styles::{Style, StyleProp, Units},
-        Children, Color, constructor, Handler, Index, render, rsx, use_state, widget
+        Children, Color, constructor, Index, render, rsx, widget
     },
     widgets::{App, Text, Window},
 };
@@ -34,11 +40,14 @@ fn TabDemo() {
         ..Default::default()
     };
 
-    let (count, set_count, ..) = use_state!(0);
-    let (tabs, set_tabs, ..) = use_state!(vec![
+    // This is not the most ideal way to generate tabs. For one, the `content` has no access to its actual context
+    // (i.e. where it actually exists in the hierarchy). Additionally, it would be better if tabs were created as
+    // children of `TabBox`. These are issues that will be addressed in the future, so for now, this will work.
+    let tabs = vec![
         TabData {
             name: "Tab 1".to_string(),
             content: {
+                // This is a temporary hack to prevent the `constructor` macro from throwing an error
                 let children = Children::None;
                 let text_style = text_style.clone();
                 constructor! {
@@ -72,36 +81,10 @@ fn TabDemo() {
                 }
             },
         },
-    ]);
-
-    let tab_clone = tabs.clone();
-    let set_added_tabs = set_tabs.clone();
-    let on_add_tab = Handler::new(move |_| {
-        let mut tab_clone = (&tab_clone).clone();
-        tab_clone.push(TabData {
-            name: format!("Tab {}", count),
-            content: {
-                let children = Children::None;
-                constructor! {
-                    <>
-                        <Text content={"Hello".to_string()} size={12.0} />
-                    </>
-                }
-            },
-        }, );
-        set_count(count + 1);
-        set_added_tabs(tab_clone);
-    });
-
-    let tab_clone = tabs.clone();
-    let on_remove_tab = Handler::new(move |index: usize| {
-        let mut tab_clone = (&tab_clone).clone();
-        tab_clone.remove(index);
-        set_tabs(tab_clone);
-    });
+    ];
 
     rsx! {
-        <TabBox tabs={tabs} on_add_tab={on_add_tab} on_remove_tab={on_remove_tab} />
+        <TabBox tabs={tabs} />
     }
 }
 
@@ -123,19 +106,16 @@ fn startup(
             normal: Color::new(0.949, 0.956, 0.968, 1.0),
             hovered: Color::new(0.650, 0.574, 0.669, 1.0),
             active: Color::new(0.949, 0.956, 0.968, 1.0),
-            disabled: Color::new(0.662, 0.678, 0.694, 1.0),
         },
         active_tab: ColorState {
             normal: Color::new(0.286, 0.353, 0.392, 1.0),
             hovered: Color::new(0.246, 0.323, 0.352, 1.0),
-            active: Default::default(),
-            disabled: Color::new(0.474, 0.486, 0.505, 1.0),
+            active: Color::new(0.196, 0.283, 0.312, 1.0),
         },
         inactive_tab: ColorState {
             normal: Color::new(0.176, 0.227, 0.255, 1.0),
             hovered: Color::new(0.16, 0.21, 0.23, 1.0),
-            active: Default::default(),
-            disabled: Color::new(0.474, 0.486, 0.505, 1.0),
+            active: Color::new(0.196, 0.283, 0.312, 1.0),
         },
         tab_height: 22.0,
     };
diff --git a/examples/tabs/theming.rs b/examples/tabs/theming.rs
index cbcc1b3..118908b 100644
--- a/examples/tabs/theming.rs
+++ b/examples/tabs/theming.rs
@@ -17,7 +17,6 @@ pub struct ColorState {
     pub normal: Color,
     pub hovered: Color,
     pub active: Color,
-    pub disabled: Color,
 }
 
 #[widget]
-- 
GitLab