177 lines
3.9 KiB
Rust
177 lines
3.9 KiB
Rust
use crate::{
|
|
assets::AssetPath,
|
|
components::{
|
|
Component, ComponentBase, ComponentTrait, RefreshData,
|
|
button::{self, ComponentButton},
|
|
},
|
|
event::CallbackDataCommon,
|
|
i18n::Translation,
|
|
layout::WidgetPair,
|
|
widget::{ConstructEssentials, div::WidgetDiv},
|
|
};
|
|
use std::{
|
|
cell::RefCell,
|
|
rc::{Rc, Weak},
|
|
sync::Arc,
|
|
};
|
|
use taffy::{
|
|
AlignItems,
|
|
prelude::{auto, length, percent},
|
|
};
|
|
|
|
pub struct Entry<'a> {
|
|
pub sprite_src: Option<AssetPath<'a>>,
|
|
pub text: Translation,
|
|
pub name: &'a str,
|
|
}
|
|
|
|
pub struct Params<'a> {
|
|
pub style: taffy::Style,
|
|
pub entries: Vec<Entry<'a>>,
|
|
pub selected_entry_name: &'a str, // default: ""
|
|
pub on_select: Option<TabSelectCallback>,
|
|
}
|
|
|
|
struct MountedEntry {
|
|
name: Rc<str>,
|
|
button: Rc<ComponentButton>,
|
|
}
|
|
|
|
pub struct TabSelectEvent {
|
|
pub name: Rc<str>,
|
|
}
|
|
|
|
pub type TabSelectCallback = Rc<dyn Fn(&mut CallbackDataCommon, TabSelectEvent) -> anyhow::Result<()>>;
|
|
|
|
struct State {
|
|
mounted_entries: Vec<MountedEntry>,
|
|
selected_entry_name: Rc<str>,
|
|
on_select: Option<TabSelectCallback>,
|
|
}
|
|
|
|
struct Data {}
|
|
|
|
pub struct ComponentTabs {
|
|
base: ComponentBase,
|
|
data: Rc<Data>,
|
|
state: Rc<RefCell<State>>,
|
|
}
|
|
|
|
impl ComponentTrait for ComponentTabs {
|
|
fn base(&self) -> &ComponentBase {
|
|
&self.base
|
|
}
|
|
|
|
fn base_mut(&mut self) -> &mut ComponentBase {
|
|
&mut self.base
|
|
}
|
|
|
|
fn refresh(&self, _data: &mut RefreshData) {
|
|
// nothing to do
|
|
}
|
|
}
|
|
|
|
impl State {
|
|
fn select_entry(&mut self, common: &mut CallbackDataCommon, name: &Rc<str>) {
|
|
let (color_accent, color_button) = {
|
|
let def = common.state.globals.defaults();
|
|
(def.accent_color, def.button_color)
|
|
};
|
|
|
|
for entry in &self.mounted_entries {
|
|
if *entry.name == **name {
|
|
entry.button.set_color(common, color_accent);
|
|
} else {
|
|
entry.button.set_color(common, color_button);
|
|
}
|
|
}
|
|
self.selected_entry_name = name.clone();
|
|
|
|
if let Some(on_select) = self.on_select.clone() {
|
|
let evt = TabSelectEvent { name: name.clone() };
|
|
common.alterables.dispatch(Box::new(move |common| {
|
|
(*on_select)(common, evt)?;
|
|
Ok(())
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ComponentTabs {
|
|
pub fn on_select(&self, callback: TabSelectCallback) {
|
|
self.state.borrow_mut().on_select = Some(callback);
|
|
}
|
|
}
|
|
|
|
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentTabs>)> {
|
|
let mut style = params.style;
|
|
|
|
// force-override style
|
|
style.overflow.y = taffy::Overflow::Scroll;
|
|
style.flex_direction = taffy::FlexDirection::Column;
|
|
style.flex_wrap = taffy::FlexWrap::NoWrap;
|
|
style.align_items = Some(AlignItems::Center);
|
|
style.gap = length(4.0);
|
|
|
|
let (root, _) = ess.layout.add_child(ess.parent, WidgetDiv::create(), style)?;
|
|
|
|
let mut mounted_entries = Vec::<MountedEntry>::new();
|
|
|
|
// Mount entries
|
|
for entry in params.entries {
|
|
let (_, button) = button::construct(
|
|
&mut ConstructEssentials {
|
|
layout: ess.layout,
|
|
parent: root.id,
|
|
},
|
|
button::Params {
|
|
text: Some(entry.text),
|
|
sprite_src: entry.sprite_src,
|
|
style: taffy::Style {
|
|
min_size: taffy::Size {
|
|
width: percent(1.0),
|
|
height: length(32.0),
|
|
},
|
|
justify_content: Some(taffy::JustifyContent::Start),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
},
|
|
)?;
|
|
|
|
mounted_entries.push(MountedEntry {
|
|
name: Rc::from(entry.name),
|
|
button,
|
|
});
|
|
}
|
|
|
|
let data = Rc::new(Data {});
|
|
let state = Rc::new(RefCell::new(State {
|
|
selected_entry_name: Rc::from(params.selected_entry_name),
|
|
mounted_entries,
|
|
on_select: params.on_select,
|
|
}));
|
|
|
|
// handle button clicks
|
|
for entry in &state.borrow().mounted_entries {
|
|
entry.button.on_click({
|
|
let entry_name = entry.name.clone();
|
|
let state = state.clone();
|
|
Rc::new(move |common, _| {
|
|
state.borrow_mut().select_entry(common, &entry_name);
|
|
Ok(())
|
|
})
|
|
});
|
|
}
|
|
|
|
let base = ComponentBase {
|
|
id: root.id,
|
|
lhandles: Default::default(),
|
|
};
|
|
|
|
let tabs = Rc::new(ComponentTabs { base, data, state });
|
|
|
|
ess.layout.defer_component_refresh(Component(tabs.clone()));
|
|
Ok((root, tabs))
|
|
}
|