diff --git a/wgui/src/assets.rs b/wgui/src/assets.rs
index e1fb4511..7733b77f 100644
--- a/wgui/src/assets.rs
+++ b/wgui/src/assets.rs
@@ -2,9 +2,34 @@ use flate2::read::GzDecoder;
use std::ffi::OsStr;
use std::io::Read;
use std::path::{Component, Path, PathBuf};
+use std::rc::Rc;
use crate::i18n::LangsList;
+pub enum AssetPathRc {
+ WguiInternal(Rc
), // tied to internal wgui AssetProvider. Used internally
+ BuiltIn(Rc), // tied to user AssetProvider
+ FileOrBuiltIn(Rc), // attempts to load from a path relative to asset_folder, falls back to BuiltIn
+ File(Rc), // load from filesystem
+}
+
+impl AssetPathRc {
+ pub fn as_borrowed(&self) -> AssetPath<'_> {
+ match self {
+ Self::WguiInternal(path) => AssetPath::WguiInternal(path.as_ref()),
+ Self::BuiltIn(path) => AssetPath::BuiltIn(path.as_ref()),
+ Self::FileOrBuiltIn(path) => AssetPath::FileOrBuiltIn(path.as_ref()),
+ Self::File(path) => AssetPath::File(path.as_ref()),
+ }
+ }
+}
+
+impl<'a> From<&'a AssetPathRc> for AssetPath<'a> {
+ fn from(path: &'a AssetPathRc) -> Self {
+ path.as_borrowed()
+ }
+}
+
#[derive(Debug, Clone, Copy)]
pub enum AssetPath<'a> {
WguiInternal(&'a str), // tied to internal wgui AssetProvider. Used internally
diff --git a/wgui/src/components/tabs.rs b/wgui/src/components/tabs.rs
index 81e619ae..da26cfb0 100644
--- a/wgui/src/components/tabs.rs
+++ b/wgui/src/components/tabs.rs
@@ -1,6 +1,6 @@
use crate::{
- assets::AssetPath,
- color::WguiColorName,
+ assets::AssetPathRc,
+ color::WguiColor,
components::{
Component, ComponentBase, ComponentTrait, RefreshData,
button::{self, ComponentButton},
@@ -8,7 +8,7 @@ use crate::{
event::CallbackDataCommon,
i18n::Translation,
layout::WidgetPair,
- widget::{ConstructEssentials, div::WidgetDiv},
+ widget::{ConstructEssentials, div::WidgetDiv, util::WLength},
};
use std::{cell::RefCell, rc::Rc};
use taffy::{
@@ -16,17 +16,25 @@ use taffy::{
prelude::{length, percent},
};
-pub struct Entry<'a> {
- pub sprite_src: Option>,
+pub struct Entry {
+ pub sprite_src: Option,
pub text: Translation,
- pub name: &'a str,
+ pub name: Rc,
}
pub struct Params<'a> {
pub style: taffy::Style,
- pub entries: Vec>,
+ pub entries: Vec,
pub selected_entry_name: &'a str, // default: ""
pub on_select: Option,
+ pub round: WLength,
+ pub color: Option,
+ pub border: f32,
+ pub border_color: Option,
+ pub hover_color: Option,
+ pub hover_border_color: Option,
+ pub sticky_color: Option,
+ pub sticky_border_color: Option,
}
struct MountedEntry {
@@ -65,18 +73,10 @@ impl ComponentTrait for ComponentTabs {
}
}
-fn set_button_selected(common: &mut CallbackDataCommon, button: &Rc, selected: bool) {
- if selected {
- button.set_color(common, WguiColorName::Primary.into());
- } else {
- button.set_color(common, WguiColorName::Background.into());
- }
-}
-
impl State {
fn select_entry(&mut self, common: &mut CallbackDataCommon, name: &Rc) {
for entry in &self.mounted_entries {
- set_button_selected(common, &entry.button, *entry.name == **name);
+ entry.button.set_sticky_state(common, *entry.name == **name);
}
self.selected_entry_name = name.clone();
@@ -121,7 +121,9 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
let mut mounted_entries = Vec::::new();
// Mount entries
- for entry in params.entries {
+ for (idx, entry) in params.entries.into_iter().enumerate() {
+ let sprite_src = entry.sprite_src.as_ref().map(AssetPathRc::as_borrowed);
+
let (_, button) = button::construct(
&mut ConstructEssentials {
layout: ess.layout,
@@ -129,7 +131,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
},
button::Params {
text: Some(entry.text),
- sprite_src: entry.sprite_src,
+ sprite_src,
style: taffy::Style {
min_size: taffy::Size {
width: percent(1.0),
@@ -138,15 +140,23 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
justify_content: Some(taffy::JustifyContent::START),
..Default::default()
},
+ round: params.round,
+ color: params.color,
+ border: params.border,
+ border_color: params.border_color,
+ hover_color: params.hover_color,
+ hover_border_color: params.hover_border_color,
+ sticky_color: params.sticky_color,
+ sticky_border_color: params.sticky_border_color,
..Default::default()
},
)?;
// init colors
- set_button_selected(&mut ess.layout.common(), &button, false);
+ button.set_sticky_state(&mut ess.layout.common(), idx == 0);
mounted_entries.push(MountedEntry {
- name: Rc::from(entry.name),
+ name: entry.name,
button,
});
}
diff --git a/wgui/src/parser/component_tabs.rs b/wgui/src/parser/component_tabs.rs
index c195de7b..e3319a90 100644
--- a/wgui/src/parser/component_tabs.rs
+++ b/wgui/src/parser/component_tabs.rs
@@ -1,12 +1,20 @@
+use std::rc::Rc;
+
use crate::{
- assets::AssetPath,
+ assets::AssetPathRc,
+ color::WguiColor,
components::{Component, tabs},
i18n::Translation,
layout::WidgetID,
- parser::{AttribPair, ParserContext, get_asset_path_from_kv, process_component, style::parse_style},
+ parser::{
+ AttribPair, ParserContext, ParserFile, get_asset_path_rc_from_kv, process_attribs, process_component,
+ style::{parse_color_opt, parse_round, parse_style},
+ },
+ widget::util::WLength,
};
pub fn parse_component_tabs<'a>(
+ file: &'a ParserFile,
ctx: &mut ParserContext,
node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID,
@@ -17,25 +25,71 @@ pub fn parse_component_tabs<'a>(
let mut entries = Vec::::new();
+ let mut border = 2.0;
+ let mut color: Option = None;
+ let mut border_color: Option = None;
+ let mut hover_color: Option = None;
+ let mut hover_border_color: Option = None;
+ let mut sticky_color: Option = None;
+ let mut sticky_border_color: Option = None;
+ let mut round = WLength::Units(4.0);
+
for child in node.children() {
match child.tag_name().name() {
"" => { /* ignore */ }
"Tab" => {
- let mut name: Option<&str> = None;
+ let mut name: Option> = None;
let mut text: Option = None;
- let mut sprite_src: Option = None;
+ let mut sprite_src: Option = None;
- for attrib in child.attributes() {
- let (key, value) = (attrib.name(), attrib.value());
- match key {
- "name" => name = Some(value),
- "text" => text = Some(Translation::from_raw_text(value)),
- "translation" => text = Some(Translation::from_translation_key(value)),
+ let attribs = process_attribs(file, ctx, &child, false);
+
+ for attrib in attribs.iter() {
+ match &*attrib.attrib {
+ "name" => name = Some(attrib.value.clone()),
+ "text" => text = Some(Translation::from_raw_text(&*attrib.value)),
+ "translation" => text = Some(Translation::from_translation_key(&*attrib.value)),
"sprite_src" | "sprite_src_ext" | "sprite_src_builtin" | "sprite_src_internal" => {
- sprite_src = Some(get_asset_path_from_kv("sprite_", key, value));
+ sprite_src = Some(get_asset_path_rc_from_kv(
+ "sprite_",
+ &*attrib.attrib,
+ attrib.value.clone(),
+ ));
+ continue;
+ }
+ "round" => {
+ parse_round(
+ ctx,
+ tag_name,
+ &*attrib.attrib,
+ &*attrib.value,
+ &mut round,
+ ctx.layout.state.theme.rounding_mult,
+ );
+ }
+ "color" => {
+ parse_color_opt(ctx, tag_name, &*attrib.attrib, &*attrib.value, &mut color);
+ }
+ "border" => {
+ ctx.parse_check_f32(tag_name, &*attrib.attrib, &*attrib.value, &mut border);
+ }
+ "border_color" => {
+ parse_color_opt(ctx, tag_name, &*attrib.attrib, &*attrib.value, &mut border_color);
+ }
+ "hover_color" => {
+ parse_color_opt(ctx, tag_name, &*attrib.attrib, &*attrib.value, &mut hover_color);
+ }
+ "hover_border_color" => {
+ parse_color_opt(ctx, tag_name, &*attrib.attrib, &*attrib.value, &mut hover_border_color);
+ }
+ "sticky_color" => {
+ parse_color_opt(ctx, tag_name, &*attrib.attrib, &*attrib.value, &mut sticky_color);
+ }
+ "sticky_border_color" => {
+ parse_color_opt(ctx, tag_name, &*attrib.attrib, &*attrib.value, &mut sticky_border_color);
}
other_key => {
- ctx.print_invalid_attrib("Tab", other_key, value);
+ ctx.print_invalid_attrib("Tab", other_key, &*attrib.value);
}
}
}
@@ -59,6 +113,14 @@ pub fn parse_component_tabs<'a>(
selected_entry_name: "first",
entries,
on_select: None,
+ border,
+ color,
+ border_color,
+ hover_color,
+ hover_border_color,
+ sticky_color,
+ sticky_border_color,
+ round,
},
)?;
diff --git a/wgui/src/parser/mod.rs b/wgui/src/parser/mod.rs
index a50a60aa..0630a0e9 100644
--- a/wgui/src/parser/mod.rs
+++ b/wgui/src/parser/mod.rs
@@ -19,7 +19,7 @@ mod widget_rectangle;
mod widget_sprite;
use crate::{
- assets::{AssetPath, AssetPathOwned, normalize_path},
+ assets::{AssetPath, AssetPathOwned, AssetPathRc, normalize_path},
components::{Component, ComponentWeak},
globals::WguiGlobals,
i18n::Translation,
@@ -1082,7 +1082,9 @@ fn parse_child<'a>(
"EditBox" => new_widget_id = Some(parse_component_editbox(ctx, parent_id, &attribs, tag_name)?),
"BarGraph" => new_widget_id = Some(parse_component_bar_graph(ctx, parent_id, &attribs, tag_name)?),
"Tabs" => {
- new_widget_id = Some(parse_component_tabs(ctx, child_node, parent_id, &attribs, tag_name)?);
+ new_widget_id = Some(parse_component_tabs(
+ file, ctx, child_node, parent_id, &attribs, tag_name,
+ )?);
}
"" => { /* ignore */ }
other_tag_name => {
@@ -1327,21 +1329,26 @@ fn parse_document_root(
Ok(())
}
-fn get_asset_path_from_kv<'a>(prefix: &'static str, key: &'a str, value: &'a str) -> AssetPath<'a> {
- let key_split = match key.find(prefix) {
- Some(pos) => {
- assert!(pos == 0, "invalid split");
- key.get(prefix.len()..).unwrap()
- }
- None => key,
- };
- match key_split {
+fn get_asset_path_from_kv<'a>(prefix: &str, key: &'a str, value: &'a str) -> AssetPath<'a> {
+ let key = key.strip_prefix(prefix).unwrap_or(key);
+
+ match key {
"src" => AssetPath::FileOrBuiltIn(value),
"src_ext" => AssetPath::File(value),
"src_builtin" => AssetPath::BuiltIn(value),
"src_internal" => AssetPath::WguiInternal(value),
- other => {
- panic!("unexpected attrib {other}");
- }
+ other => panic!("unexpected attrib {other}"),
+ }
+}
+
+fn get_asset_path_rc_from_kv(prefix: &'static str, key: &str, value: Rc) -> AssetPathRc {
+ let key = key.strip_prefix(prefix).unwrap_or(key);
+
+ match key {
+ "src" => AssetPathRc::FileOrBuiltIn(value),
+ "src_ext" => AssetPathRc::File(value),
+ "src_builtin" => AssetPathRc::BuiltIn(value),
+ "src_internal" => AssetPathRc::WguiInternal(value),
+ other => panic!("unexpected attrib {other}"),
}
}