Tabbed settings (Closes #355)
This commit is contained in:
@@ -66,11 +66,12 @@ impl Default for Params<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ButtonClickEvent {
|
||||
pub mouse_pos_absolute: Option<Vec2>,
|
||||
pub boundary: Boundary,
|
||||
}
|
||||
pub type ButtonClickCallback = Box<dyn Fn(&mut CallbackDataCommon, ButtonClickEvent) -> anyhow::Result<()>>;
|
||||
pub type ButtonClickCallback = Rc<dyn Fn(&mut CallbackDataCommon, ButtonClickEvent) -> anyhow::Result<()>>;
|
||||
|
||||
pub struct Colors {
|
||||
pub color: drawing::Color,
|
||||
@@ -111,7 +112,6 @@ impl ComponentTrait for ComponentButton {
|
||||
}
|
||||
|
||||
fn refresh(&self, data: &mut RefreshData) {
|
||||
// nothing to do
|
||||
let mut state = self.state.borrow_mut();
|
||||
|
||||
if state.active_tooltip.is_some() {
|
||||
@@ -362,7 +362,6 @@ fn register_event_mouse_release(
|
||||
Box::new(move |common, event_data, (), ()| {
|
||||
let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
let mut state = state.borrow_mut();
|
||||
|
||||
if data.sticky {
|
||||
state.sticky_down = !state.sticky_down;
|
||||
}
|
||||
@@ -384,17 +383,18 @@ fn register_event_mouse_release(
|
||||
state.sticky_down,
|
||||
);
|
||||
|
||||
if let Some(on_click) = &state.on_click {
|
||||
on_click(
|
||||
common,
|
||||
ButtonClickEvent {
|
||||
mouse_pos_absolute: event_data.metadata.get_mouse_pos_absolute(),
|
||||
boundary: event_data.widget_data.cached_absolute_boundary,
|
||||
},
|
||||
)?;
|
||||
if let Some(on_click) = state.on_click.clone() {
|
||||
let evt = ButtonClickEvent {
|
||||
mouse_pos_absolute: event_data.metadata.get_mouse_pos_absolute(),
|
||||
boundary: event_data.widget_data.cached_absolute_boundary,
|
||||
};
|
||||
|
||||
common.alterables.dispatch(Box::new(move |common| {
|
||||
(*on_click)(common, evt)?;
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(EventResult::Consumed)
|
||||
} else {
|
||||
Ok(EventResult::Pass)
|
||||
@@ -403,14 +403,15 @@ fn register_event_mouse_release(
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentButton>)> {
|
||||
let globals = ess.layout.state.globals.clone();
|
||||
let mut style = params.style;
|
||||
|
||||
// force-override style
|
||||
style.align_items = Some(AlignItems::Center);
|
||||
style.justify_content = Some(JustifyContent::Center);
|
||||
if style.justify_content.is_none() {
|
||||
style.justify_content = Some(JustifyContent::Center);
|
||||
}
|
||||
style.overflow.x = taffy::Overflow::Hidden;
|
||||
style.overflow.y = taffy::Overflow::Hidden;
|
||||
|
||||
|
||||
@@ -284,7 +284,6 @@ fn register_event_mouse_release(
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentCheckbox>)> {
|
||||
let mut style = params.style;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ pub mod button;
|
||||
pub mod checkbox;
|
||||
pub mod radio_group;
|
||||
pub mod slider;
|
||||
pub mod tabs;
|
||||
pub mod tooltip;
|
||||
|
||||
pub struct RefreshData<'a> {
|
||||
|
||||
@@ -398,7 +398,6 @@ fn register_event_mouse_release(
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentSlider>)> {
|
||||
let mut style = params.style;
|
||||
style.position = taffy::Position::Relative;
|
||||
|
||||
176
wgui/src/components/tabs.rs
Normal file
176
wgui/src/components/tabs.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
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))
|
||||
}
|
||||
@@ -89,7 +89,6 @@ impl Drop for ComponentTooltip {
|
||||
pub const TOOLTIP_COLOR: Color = Color::new(0.02, 0.02, 0.02, 0.95);
|
||||
pub const TOOLTIP_BORDER_COLOR: Color = Color::new(0.4, 0.4, 0.4, 1.0);
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentTooltip>)> {
|
||||
let absolute_boundary = {
|
||||
let widget_to_watch = ess
|
||||
|
||||
Reference in New Issue
Block a user