Skip to content
Snippets Groups Projects
Commit e2b79416 authored by MrGVSV's avatar MrGVSV
Browse files

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)
parent d6263f15
No related branches found
No related tags found
No related merge requests found
......@@ -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>
}
......
......@@ -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
......@@ -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} />
</>
}
......
......@@ -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;
}
......
//! 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,
};
......
......@@ -17,7 +17,6 @@ pub struct ColorState {
pub normal: Color,
pub hovered: Color,
pub active: Color,
pub disabled: Color,
}
#[widget]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment