@@ -7,9 +7,9 @@ use crate::{
|
|||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use wgui::{
|
use wgui::{
|
||||||
components::{
|
components::{
|
||||||
Component,
|
|
||||||
button::{ButtonClickCallback, ComponentButton},
|
button::{ButtonClickCallback, ComponentButton},
|
||||||
checkbox::ComponentCheckbox,
|
checkbox::ComponentCheckbox,
|
||||||
|
Component,
|
||||||
},
|
},
|
||||||
drawing::Color,
|
drawing::Color,
|
||||||
event::EventListenerCollection,
|
event::EventListenerCollection,
|
||||||
@@ -66,17 +66,17 @@ impl TestbedGeneric {
|
|||||||
|
|
||||||
let extra = ParseDocumentExtra {
|
let extra = ParseDocumentExtra {
|
||||||
on_custom_attribs: Some(Box::new(move |par| {
|
on_custom_attribs: Some(Box::new(move |par| {
|
||||||
let Some(my_custom_value) = par.get_value("my_custom") else {
|
let Some(my_custom_value) = par.get_value("_my_custom") else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(mult_value) = par.get_value("mult") else {
|
let Some(mult_value) = par.get_value("_mult") else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let mult_f32 = mult_value.parse::<f32>().unwrap();
|
let mult_f32 = mult_value.parse::<f32>().unwrap();
|
||||||
|
|
||||||
let mut color = match my_custom_value {
|
let mut color = match my_custom_value.as_ref() {
|
||||||
"red" => Color::new(1.0, 0.0, 0.0, 1.0),
|
"red" => Color::new(1.0, 0.0, 0.0, 1.0),
|
||||||
"green" => Color::new(0.0, 1.0, 0.0, 1.0),
|
"green" => Color::new(0.0, 1.0, 0.0, 1.0),
|
||||||
"blue" => Color::new(0.0, 0.0, 1.0, 1.0),
|
"blue" => Color::new(0.0, 0.0, 1.0, 1.0),
|
||||||
|
|||||||
@@ -100,6 +100,16 @@ _Text size in pixel units_
|
|||||||
|
|
||||||
`weight`: "normal" | "bold"
|
`weight`: "normal" | "bold"
|
||||||
|
|
||||||
|
`shadow`: #112233 | #112233CC (default: None)
|
||||||
|
|
||||||
|
`shadow_x`: **float** (default: 1.5)
|
||||||
|
|
||||||
|
_Horizontal offset of the shadow from the original text. Positive is right._
|
||||||
|
|
||||||
|
`shadow_y`: **float** (default: 1.5)
|
||||||
|
|
||||||
|
_Vertical offset of the shadow from the original text. Positive is down._
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## rectangle widget
|
## rectangle widget
|
||||||
|
|||||||
+2
-2
@@ -7,7 +7,7 @@ use taffy::TraversePartialTree;
|
|||||||
use crate::{
|
use crate::{
|
||||||
drawing,
|
drawing,
|
||||||
layout::Widget,
|
layout::Widget,
|
||||||
renderer_vk::text::custom_glyph::CustomGlyph,
|
renderer_vk::text::{custom_glyph::CustomGlyph, TextShadow},
|
||||||
stack::{self, ScissorStack, TransformStack},
|
stack::{self, ScissorStack, TransformStack},
|
||||||
widget::{self},
|
widget::{self},
|
||||||
};
|
};
|
||||||
@@ -135,7 +135,7 @@ pub struct PrimitiveExtent {
|
|||||||
|
|
||||||
pub enum RenderPrimitive {
|
pub enum RenderPrimitive {
|
||||||
Rectangle(PrimitiveExtent, Rectangle),
|
Rectangle(PrimitiveExtent, Rectangle),
|
||||||
Text(PrimitiveExtent, Rc<RefCell<Buffer>>),
|
Text(PrimitiveExtent, Rc<RefCell<Buffer>>, Option<TextShadow>),
|
||||||
Sprite(PrimitiveExtent, Option<CustomGlyph>), //option because we want as_slice
|
Sprite(PrimitiveExtent, Option<CustomGlyph>), //option because we want as_slice
|
||||||
ScissorEnable(Boundary),
|
ScissorEnable(Boundary),
|
||||||
ScissorDisable,
|
ScissorDisable,
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
components::{Component, button},
|
components::{button, Component},
|
||||||
drawing::Color,
|
drawing::Color,
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{
|
||||||
ParserContext, ParserFile, iter_attribs, parse_children, process_component,
|
parse_children, process_component,
|
||||||
style::{parse_color_opt, parse_round, parse_style, parse_text_style},
|
style::{parse_color_opt, parse_round, parse_style, parse_text_style},
|
||||||
|
AttribPair, ParserContext, ParserFile,
|
||||||
},
|
},
|
||||||
widget::util::WLength,
|
widget::util::WLength,
|
||||||
};
|
};
|
||||||
@@ -15,6 +16,7 @@ pub fn parse_component_button<'a, U1, U2>(
|
|||||||
ctx: &mut ParserContext<U1, U2>,
|
ctx: &mut ParserContext<U1, U2>,
|
||||||
node: roxmltree::Node<'a, 'a>,
|
node: roxmltree::Node<'a, 'a>,
|
||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
) -> anyhow::Result<WidgetID> {
|
) -> anyhow::Result<WidgetID> {
|
||||||
let mut color: Option<Color> = None;
|
let mut color: Option<Color> = None;
|
||||||
let mut border_color: Option<Color> = None;
|
let mut border_color: Option<Color> = None;
|
||||||
@@ -23,11 +25,11 @@ pub fn parse_component_button<'a, U1, U2>(
|
|||||||
let mut round = WLength::Units(4.0);
|
let mut round = WLength::Units(4.0);
|
||||||
let mut translation: Option<Translation> = None;
|
let mut translation: Option<Translation> = None;
|
||||||
|
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
let text_style = parse_text_style(attribs);
|
||||||
let text_style = parse_text_style(&attribs);
|
let style = parse_style(attribs);
|
||||||
let style = parse_style(&attribs);
|
|
||||||
|
|
||||||
for (key, value) in attribs {
|
for pair in attribs {
|
||||||
|
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||||
match key.as_ref() {
|
match key.as_ref() {
|
||||||
"text" => {
|
"text" => {
|
||||||
translation = Some(Translation::from_raw_text(&value));
|
translation = Some(Translation::from_raw_text(&value));
|
||||||
@@ -73,7 +75,7 @@ pub fn parse_component_button<'a, U1, U2>(
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
process_component(file, ctx, node, Component(component), new_id);
|
process_component(ctx, Component(component), new_id, attribs);
|
||||||
parse_children(file, ctx, node, new_id)?;
|
parse_children(file, ctx, node, new_id)?;
|
||||||
|
|
||||||
Ok(new_id)
|
Ok(new_id)
|
||||||
|
|||||||
@@ -1,38 +1,35 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
components::{Component, checkbox},
|
components::{checkbox, Component},
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{parse_check_f32, parse_check_i32, process_component, style::parse_style, AttribPair, ParserContext},
|
||||||
ParserContext, ParserFile, iter_attribs, parse_check_f32, parse_check_i32, process_component, style::parse_style,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parse_component_checkbox<'a, U1, U2>(
|
pub fn parse_component_checkbox<'a, U1, U2>(
|
||||||
file: &'a ParserFile,
|
|
||||||
ctx: &mut ParserContext<U1, U2>,
|
ctx: &mut ParserContext<U1, U2>,
|
||||||
node: roxmltree::Node<'a, 'a>,
|
|
||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
) -> anyhow::Result<WidgetID> {
|
) -> anyhow::Result<WidgetID> {
|
||||||
let mut box_size = 24.0;
|
let mut box_size = 24.0;
|
||||||
let mut translation = Translation::default();
|
let mut translation = Translation::default();
|
||||||
let mut checked = 0;
|
let mut checked = 0;
|
||||||
|
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
let style = parse_style(attribs);
|
||||||
let style = parse_style(&attribs);
|
|
||||||
|
|
||||||
for (key, value) in attribs {
|
for pair in attribs {
|
||||||
match key.as_ref() {
|
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||||
|
match key {
|
||||||
"text" => {
|
"text" => {
|
||||||
translation = Translation::from_raw_text(&value);
|
translation = Translation::from_raw_text(value);
|
||||||
}
|
}
|
||||||
"translation" => {
|
"translation" => {
|
||||||
translation = Translation::from_translation_key(&value);
|
translation = Translation::from_translation_key(value);
|
||||||
}
|
}
|
||||||
"box_size" => {
|
"box_size" => {
|
||||||
parse_check_f32(value.as_ref(), &mut box_size);
|
parse_check_f32(value, &mut box_size);
|
||||||
}
|
}
|
||||||
"checked" => {
|
"checked" => {
|
||||||
parse_check_i32(value.as_ref(), &mut checked);
|
parse_check_i32(value, &mut checked);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -50,7 +47,7 @@ pub fn parse_component_checkbox<'a, U1, U2>(
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
process_component(file, ctx, node, Component(component), new_id);
|
process_component(ctx, Component(component), new_id, attribs);
|
||||||
|
|
||||||
Ok(new_id)
|
Ok(new_id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,31 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
components::{Component, slider},
|
components::{slider, Component},
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{ParserContext, ParserFile, iter_attribs, parse_check_f32, process_component, style::parse_style},
|
parser::{parse_check_f32, process_component, style::parse_style, AttribPair, ParserContext},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parse_component_slider<'a, U1, U2>(
|
pub fn parse_component_slider<'a, U1, U2>(
|
||||||
file: &'a ParserFile,
|
|
||||||
ctx: &mut ParserContext<U1, U2>,
|
ctx: &mut ParserContext<U1, U2>,
|
||||||
node: roxmltree::Node<'a, 'a>,
|
|
||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
) -> anyhow::Result<WidgetID> {
|
) -> anyhow::Result<WidgetID> {
|
||||||
let mut min_value = 0.0;
|
let mut min_value = 0.0;
|
||||||
let mut max_value = 1.0;
|
let mut max_value = 1.0;
|
||||||
let mut initial_value = 0.5;
|
let mut initial_value = 0.5;
|
||||||
|
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
let style = parse_style(attribs);
|
||||||
let style = parse_style(&attribs);
|
|
||||||
|
|
||||||
for (key, value) in attribs {
|
for pair in attribs {
|
||||||
match key.as_ref() {
|
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||||
|
match key {
|
||||||
"min_value" => {
|
"min_value" => {
|
||||||
parse_check_f32(value.as_ref(), &mut min_value);
|
parse_check_f32(value, &mut min_value);
|
||||||
}
|
}
|
||||||
"max_value" => {
|
"max_value" => {
|
||||||
parse_check_f32(value.as_ref(), &mut max_value);
|
parse_check_f32(value, &mut max_value);
|
||||||
}
|
}
|
||||||
"value" => {
|
"value" => {
|
||||||
parse_check_f32(value.as_ref(), &mut initial_value);
|
parse_check_f32(value, &mut initial_value);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -46,7 +45,7 @@ pub fn parse_component_slider<'a, U1, U2>(
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
process_component(file, ctx, node, Component(component), new_id);
|
process_component(ctx, Component(component), new_id, attribs);
|
||||||
|
|
||||||
Ok(new_id)
|
Ok(new_id)
|
||||||
}
|
}
|
||||||
|
|||||||
+88
-101
@@ -441,7 +441,7 @@ fn print_invalid_value(value: &str) {
|
|||||||
log::warn!("Invalid value \"{value}\"");
|
log::warn!("Invalid value \"{value}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_val(value: &Rc<str>) -> Option<f32> {
|
fn parse_val(value: &str) -> Option<f32> {
|
||||||
let Ok(val) = value.parse::<f32>() else {
|
let Ok(val) = value.parse::<f32>() else {
|
||||||
print_invalid_value(value);
|
print_invalid_value(value);
|
||||||
return None;
|
return None;
|
||||||
@@ -534,15 +534,16 @@ fn parse_widget_other<'a, U1, U2>(
|
|||||||
xml_tag_name: &str,
|
xml_tag_name: &str,
|
||||||
file: &'a ParserFile,
|
file: &'a ParserFile,
|
||||||
ctx: &mut ParserContext<U1, U2>,
|
ctx: &mut ParserContext<U1, U2>,
|
||||||
node: roxmltree::Node<'a, 'a>,
|
|
||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let Some(template) = ctx.get_template(xml_tag_name) else {
|
let Some(template) = ctx.get_template(xml_tag_name) else {
|
||||||
log::error!("Undefined tag named \"{xml_tag_name}\"");
|
log::error!("Undefined tag named \"{xml_tag_name}\"");
|
||||||
return Ok(()); // not critical
|
return Ok(()); // not critical
|
||||||
};
|
};
|
||||||
|
|
||||||
let template_parameters: HashMap<Rc<str>, Rc<str>> = iter_attribs(file, ctx, &node, false).collect();
|
let template_parameters: HashMap<Rc<str>, Rc<str>> =
|
||||||
|
attribs.iter().map(|a| (a.attrib.clone(), a.value.clone())).collect();
|
||||||
|
|
||||||
parse_widget_other_internal(&template, template_parameters, file, ctx, parent_id)
|
parse_widget_other_internal(&template, template_parameters, file, ctx, parent_id)
|
||||||
}
|
}
|
||||||
@@ -550,17 +551,15 @@ fn parse_widget_other<'a, U1, U2>(
|
|||||||
fn parse_tag_include<'a, U1, U2>(
|
fn parse_tag_include<'a, U1, U2>(
|
||||||
file: &ParserFile,
|
file: &ParserFile,
|
||||||
ctx: &mut ParserContext<U1, U2>,
|
ctx: &mut ParserContext<U1, U2>,
|
||||||
node: roxmltree::Node<'a, 'a>,
|
|
||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
for attrib in node.attributes() {
|
for pair in attribs {
|
||||||
let (key, value) = (attrib.name(), attrib.value());
|
|
||||||
|
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match key {
|
match pair.attrib.as_ref() {
|
||||||
"src" => {
|
"src" => {
|
||||||
let mut new_path = file.path.parent().unwrap_or_else(|| Path::new("/")).to_path_buf();
|
let mut new_path = file.path.parent().unwrap_or_else(|| Path::new("/")).to_path_buf();
|
||||||
new_path.push(value);
|
new_path.push(pair.value.as_ref());
|
||||||
let new_path = assets::normalize_path(&new_path);
|
let new_path = assets::normalize_path(&new_path);
|
||||||
|
|
||||||
let (new_file, node_layout) = get_doc_from_path(ctx, &new_path)?;
|
let (new_file, node_layout) = get_doc_from_path(ctx, &new_path)?;
|
||||||
@@ -569,7 +568,7 @@ fn parse_tag_include<'a, U1, U2>(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
print_invalid_attrib(key, value);
|
print_invalid_attrib(pair.attrib.as_ref(), pair.value.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -637,38 +636,39 @@ fn process_attrib<'a, U1, U2>(
|
|||||||
ctx: &'a ParserContext<U1, U2>,
|
ctx: &'a ParserContext<U1, U2>,
|
||||||
key: &str,
|
key: &str,
|
||||||
value: &str,
|
value: &str,
|
||||||
) -> (Rc<str>, Rc<str>) {
|
) -> AttribPair {
|
||||||
if value.starts_with('~') {
|
if value.starts_with('~') {
|
||||||
let name = &value[1..];
|
let name = &value[1..];
|
||||||
|
|
||||||
(
|
|
||||||
Rc::from(key),
|
|
||||||
match ctx.get_var(name) {
|
match ctx.get_var(name) {
|
||||||
Some(name) => name,
|
Some(name) => AttribPair::new(key, name.clone()),
|
||||||
None => Rc::from("undefined"),
|
None => AttribPair::new(key, "undefined"),
|
||||||
},
|
}
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
(Rc::from(key), replace_vars(value, &file.template_parameters))
|
AttribPair::new(key, replace_vars(value, &file.template_parameters))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_attribs<'a, U1, U2>(
|
fn raw_attribs<'a>(node: &'a roxmltree::Node<'a, 'a>) -> Vec<AttribPair> {
|
||||||
|
let mut res = vec![];
|
||||||
|
for attrib in node.attributes() {
|
||||||
|
let (key, value) = (attrib.name(), attrib.value());
|
||||||
|
res.push(AttribPair::new(key, value));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_attribs<'a, U1, U2>(
|
||||||
file: &'a ParserFile,
|
file: &'a ParserFile,
|
||||||
ctx: &'a ParserContext<U1, U2>,
|
ctx: &'a ParserContext<U1, U2>,
|
||||||
node: &'a roxmltree::Node<'a, 'a>,
|
node: &'a roxmltree::Node<'a, 'a>,
|
||||||
is_tag_macro: bool,
|
is_tag_macro: bool,
|
||||||
) -> impl Iterator<Item = (/*key*/ Rc<str>, /*value*/ Rc<str>)> + 'a {
|
) -> Vec<AttribPair> {
|
||||||
let mut res = Vec::<(Rc<str>, Rc<str>)>::new();
|
|
||||||
|
|
||||||
if is_tag_macro {
|
if is_tag_macro {
|
||||||
// return as-is, no attrib post-processing
|
// return as-is, no attrib post-processing
|
||||||
for attrib in node.attributes() {
|
return raw_attribs(node);
|
||||||
let (key, value) = (attrib.name(), attrib.value());
|
|
||||||
res.push((Rc::from(key), Rc::from(value)));
|
|
||||||
}
|
|
||||||
return res.into_iter();
|
|
||||||
}
|
}
|
||||||
|
let mut res = vec![];
|
||||||
|
|
||||||
for attrib in node.attributes() {
|
for attrib in node.attributes() {
|
||||||
let (key, value) = (attrib.name(), attrib.value());
|
let (key, value) = (attrib.name(), attrib.value());
|
||||||
@@ -686,7 +686,7 @@ fn iter_attribs<'a, U1, U2>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.into_iter()
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_tag_theme<'a, U1, U2>(ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'a, 'a>) {
|
fn parse_tag_theme<'a, U1, U2>(ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'a, 'a>) {
|
||||||
@@ -707,15 +707,15 @@ fn parse_tag_theme<'a, U1, U2>(ctx: &mut ParserContext<U1, U2>, node: roxmltree:
|
|||||||
fn parse_tag_template<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'_, '_>) {
|
fn parse_tag_template<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'_, '_>) {
|
||||||
let mut template_name: Option<Rc<str>> = None;
|
let mut template_name: Option<Rc<str>> = None;
|
||||||
|
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
let attribs = process_attribs(file, ctx, &node, false);
|
||||||
|
|
||||||
for (key, value) in attribs {
|
for pair in attribs {
|
||||||
match key.as_ref() {
|
match pair.attrib.as_ref() {
|
||||||
"name" => {
|
"name" => {
|
||||||
template_name = Some(value);
|
template_name = Some(pair.value);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
print_invalid_attrib(&key, &value);
|
print_invalid_attrib(pair.value.as_ref(), pair.value.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -737,17 +737,17 @@ fn parse_tag_template<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>
|
|||||||
fn parse_tag_macro<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'_, '_>) {
|
fn parse_tag_macro<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'_, '_>) {
|
||||||
let mut macro_name: Option<Rc<str>> = None;
|
let mut macro_name: Option<Rc<str>> = None;
|
||||||
|
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, true).collect();
|
let attribs = process_attribs(file, ctx, &node, true);
|
||||||
let mut macro_attribs = HashMap::<Rc<str>, Rc<str>>::new();
|
let mut macro_attribs = HashMap::<Rc<str>, Rc<str>>::new();
|
||||||
|
|
||||||
for (key, value) in attribs {
|
for pair in attribs {
|
||||||
match key.as_ref() {
|
match pair.attrib.as_ref() {
|
||||||
"name" => {
|
"name" => {
|
||||||
macro_name = Some(value);
|
macro_name = Some(pair.value);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if macro_attribs.insert(key.clone(), value).is_some() {
|
if macro_attribs.insert(pair.attrib.clone(), pair.value).is_some() {
|
||||||
log::warn!("macro attrib \"{key}\" already defined!");
|
log::warn!("macro attrib \"{}\" already defined!", pair.attrib);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -762,21 +762,18 @@ fn parse_tag_macro<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>, n
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn process_component<'a, U1, U2>(
|
fn process_component<'a, U1, U2>(
|
||||||
file: &'a ParserFile,
|
|
||||||
ctx: &mut ParserContext<U1, U2>,
|
ctx: &mut ParserContext<U1, U2>,
|
||||||
node: roxmltree::Node<'a, 'a>,
|
|
||||||
component: Component,
|
component: Component,
|
||||||
widget_id: WidgetID,
|
widget_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
) {
|
) {
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
|
||||||
|
|
||||||
let mut component_id: Option<Rc<str>> = None;
|
let mut component_id: Option<Rc<str>> = None;
|
||||||
|
|
||||||
for (key, value) in attribs {
|
for pair in attribs {
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match key.as_ref() {
|
match pair.attrib.as_ref() {
|
||||||
"id" => {
|
"id" => {
|
||||||
component_id = Some(value);
|
component_id = Some(pair.value.clone());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -785,20 +782,13 @@ fn process_component<'a, U1, U2>(
|
|||||||
ctx.insert_component(widget_id, component, component_id);
|
ctx.insert_component(widget_id, component, component_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_widget_universal<'a, U1, U2>(
|
fn parse_widget_universal<'a, U1, U2>(ctx: &mut ParserContext<U1, U2>, widget_id: WidgetID, attribs: &[AttribPair]) {
|
||||||
file: &'a ParserFile,
|
for pair in attribs {
|
||||||
ctx: &mut ParserContext<U1, U2>,
|
|
||||||
node: roxmltree::Node<'a, 'a>,
|
|
||||||
widget_id: WidgetID,
|
|
||||||
) {
|
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
|
||||||
|
|
||||||
for (key, value) in attribs {
|
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match key.as_ref() {
|
match pair.attrib.as_ref() {
|
||||||
"id" => {
|
"id" => {
|
||||||
// Attach a specific widget to name-ID map (just like getElementById)
|
// Attach a specific widget to name-ID map (just like getElementById)
|
||||||
ctx.insert_id(&value, widget_id);
|
ctx.insert_id(&pair.value, widget_id);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -827,36 +817,38 @@ fn parse_child<'a, U1, U2>(
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let attribs = process_attribs(file, ctx, &child_node, false);
|
||||||
|
|
||||||
let mut new_widget_id: Option<WidgetID> = None;
|
let mut new_widget_id: Option<WidgetID> = None;
|
||||||
|
|
||||||
match child_node.tag_name().name() {
|
match child_node.tag_name().name() {
|
||||||
"include" => {
|
"include" => {
|
||||||
parse_tag_include(file, ctx, child_node, parent_id)?;
|
parse_tag_include(file, ctx, parent_id, &attribs)?;
|
||||||
}
|
}
|
||||||
"div" => {
|
"div" => {
|
||||||
new_widget_id = Some(parse_widget_div(file, ctx, child_node, parent_id)?);
|
new_widget_id = Some(parse_widget_div(file, ctx, child_node, parent_id, &attribs)?);
|
||||||
}
|
}
|
||||||
"rectangle" => {
|
"rectangle" => {
|
||||||
new_widget_id = Some(parse_widget_rectangle(file, ctx, child_node, parent_id)?);
|
new_widget_id = Some(parse_widget_rectangle(file, ctx, child_node, parent_id, &attribs)?);
|
||||||
}
|
}
|
||||||
"label" => {
|
"label" => {
|
||||||
new_widget_id = Some(parse_widget_label(file, ctx, child_node, parent_id)?);
|
new_widget_id = Some(parse_widget_label(file, ctx, child_node, parent_id, &attribs)?);
|
||||||
}
|
}
|
||||||
"sprite" => {
|
"sprite" => {
|
||||||
new_widget_id = Some(parse_widget_sprite(file, ctx, child_node, parent_id)?);
|
new_widget_id = Some(parse_widget_sprite(file, ctx, child_node, parent_id, &attribs)?);
|
||||||
}
|
}
|
||||||
"Button" => {
|
"Button" => {
|
||||||
new_widget_id = Some(parse_component_button(file, ctx, child_node, parent_id)?);
|
new_widget_id = Some(parse_component_button(file, ctx, child_node, parent_id, &attribs)?);
|
||||||
}
|
}
|
||||||
"Slider" => {
|
"Slider" => {
|
||||||
new_widget_id = Some(parse_component_slider(file, ctx, child_node, parent_id)?);
|
new_widget_id = Some(parse_component_slider(ctx, parent_id, &attribs)?);
|
||||||
}
|
}
|
||||||
"CheckBox" => {
|
"CheckBox" => {
|
||||||
new_widget_id = Some(parse_component_checkbox(file, ctx, child_node, parent_id)?);
|
new_widget_id = Some(parse_component_checkbox(ctx, parent_id, &attribs)?);
|
||||||
}
|
}
|
||||||
"" => { /* ignore */ }
|
"" => { /* ignore */ }
|
||||||
other_tag_name => {
|
other_tag_name => {
|
||||||
parse_widget_other(other_tag_name, file, ctx, child_node, parent_id)?;
|
parse_widget_other(other_tag_name, file, ctx, parent_id, &attribs)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -864,20 +856,13 @@ fn parse_child<'a, U1, U2>(
|
|||||||
if let Some(widget_id) = new_widget_id
|
if let Some(widget_id) = new_widget_id
|
||||||
&& let Some(on_custom_attribs) = &ctx.doc_params.extra.on_custom_attribs
|
&& let Some(on_custom_attribs) = &ctx.doc_params.extra.on_custom_attribs
|
||||||
{
|
{
|
||||||
let mut pairs = SmallVec::<[CustomAttribPair; 4]>::new();
|
let mut pairs = SmallVec::<[AttribPair; 4]>::new();
|
||||||
|
|
||||||
for attrib in child_node.attributes() {
|
for pair in attribs {
|
||||||
let attr_name = attrib.name();
|
if !pair.attrib.starts_with('_') || pair.attrib.is_empty() {
|
||||||
if !attr_name.starts_with('_') || attr_name.is_empty() {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
pairs.push(pair.clone());
|
||||||
let attr_without_prefix = &attr_name[1..]; // safe
|
|
||||||
|
|
||||||
pairs.push(CustomAttribPair {
|
|
||||||
attrib: attr_without_prefix,
|
|
||||||
value: attrib.value(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !pairs.is_empty() {
|
if !pairs.is_empty() {
|
||||||
@@ -921,16 +906,30 @@ fn create_default_context<'a, U1, U2>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CustomAttribPair<'a> {
|
#[derive(Clone)]
|
||||||
pub attrib: &'a str, // without _ at the beginning
|
pub struct AttribPair {
|
||||||
pub value: &'a str,
|
pub attrib: Rc<str>,
|
||||||
|
pub value: Rc<str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttribPair {
|
||||||
|
fn new<A, V>(attrib: A, value: V) -> Self
|
||||||
|
where
|
||||||
|
A: Into<Rc<str>>,
|
||||||
|
V: Into<Rc<str>>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
attrib: attrib.into(),
|
||||||
|
value: value.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CustomAttribsInfo<'a> {
|
pub struct CustomAttribsInfo<'a> {
|
||||||
pub parent_id: WidgetID,
|
pub parent_id: WidgetID,
|
||||||
pub widget_id: WidgetID,
|
pub widget_id: WidgetID,
|
||||||
pub widgets: &'a WidgetMap,
|
pub widgets: &'a WidgetMap,
|
||||||
pub pairs: &'a [CustomAttribPair<'a>],
|
pub pairs: &'a [AttribPair],
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper functions
|
// helper functions
|
||||||
@@ -943,11 +942,11 @@ impl CustomAttribsInfo<'_> {
|
|||||||
self.widgets.get(self.widget_id)?.get_as_mut::<T>()
|
self.widgets.get(self.widget_id)?.get_as_mut::<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_value(&self, attrib_name: &str) -> Option<&str> {
|
pub fn get_value(&self, attrib_name: &str) -> Option<Rc<str>> {
|
||||||
// O(n) search, these pairs won't be problematically big anyways
|
// O(n) search, these pairs won't be problematically big anyways
|
||||||
for pair in self.pairs {
|
for pair in self.pairs {
|
||||||
if pair.attrib == attrib_name {
|
if *pair.attrib == *attrib_name {
|
||||||
return Some(pair.value);
|
return Some(pair.value.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,35 +957,23 @@ impl CustomAttribsInfo<'_> {
|
|||||||
CustomAttribsInfoOwned {
|
CustomAttribsInfoOwned {
|
||||||
parent_id: self.parent_id,
|
parent_id: self.parent_id,
|
||||||
widget_id: self.widget_id,
|
widget_id: self.widget_id,
|
||||||
pairs: self
|
pairs: self.pairs.iter().cloned().collect(),
|
||||||
.pairs
|
|
||||||
.iter()
|
|
||||||
.map(|p| CustomAttribPairOwned {
|
|
||||||
attrib: p.attrib.to_string(),
|
|
||||||
value: p.value.to_string(),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CustomAttribPairOwned {
|
|
||||||
pub attrib: String, // without _ at the beginning
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CustomAttribsInfoOwned {
|
pub struct CustomAttribsInfoOwned {
|
||||||
pub parent_id: WidgetID,
|
pub parent_id: WidgetID,
|
||||||
pub widget_id: WidgetID,
|
pub widget_id: WidgetID,
|
||||||
pub pairs: Vec<CustomAttribPairOwned>,
|
pub pairs: Vec<AttribPair>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomAttribsInfoOwned {
|
impl CustomAttribsInfoOwned {
|
||||||
pub fn get_value(&self, attrib_name: &str) -> Option<&str> {
|
pub fn get_value(&self, attrib_name: &str) -> Option<&str> {
|
||||||
// O(n) search, these pairs won't be problematically big anyways
|
// O(n) search, these pairs won't be problematically big anyways
|
||||||
for pair in &self.pairs {
|
for pair in &self.pairs {
|
||||||
if pair.attrib == attrib_name {
|
if pair.attrib.as_ref() == attrib_name {
|
||||||
return Some(pair.value.as_str());
|
return Some(pair.value.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1091,7 +1078,7 @@ fn parse_document_root<U1, U2>(
|
|||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match child_node.tag_name().name() {
|
match child_node.tag_name().name() {
|
||||||
/* topmost include directly in <layout> */
|
/* topmost include directly in <layout> */
|
||||||
"include" => parse_tag_include(file, ctx, child_node, parent_id)?,
|
"include" => parse_tag_include(file, ctx, parent_id, &raw_attribs(&child_node))?,
|
||||||
"theme" => parse_tag_theme(ctx, child_node),
|
"theme" => parse_tag_theme(ctx, child_node),
|
||||||
"template" => parse_tag_template(file, ctx, child_node),
|
"template" => parse_tag_template(file, ctx, child_node),
|
||||||
"macro" => parse_tag_macro(file, ctx, child_node),
|
"macro" => parse_tag_macro(file, ctx, child_node),
|
||||||
|
|||||||
+30
-11
@@ -1,15 +1,13 @@
|
|||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use taffy::{
|
use taffy::{
|
||||||
AlignContent, AlignItems, AlignSelf, BoxSizing, Display, FlexDirection, FlexWrap, JustifyContent,
|
AlignContent, AlignItems, AlignSelf, BoxSizing, Display, FlexDirection, FlexWrap, JustifyContent, JustifySelf,
|
||||||
JustifySelf, Overflow,
|
Overflow,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
drawing,
|
drawing,
|
||||||
parser::{
|
parser::{
|
||||||
is_percent, parse_color_hex, parse_f32, parse_percent, parse_size_unit, parse_val,
|
is_percent, parse_color_hex, parse_f32, parse_percent, parse_size_unit, parse_val, print_invalid_attrib,
|
||||||
print_invalid_attrib, print_invalid_value,
|
print_invalid_value, AttribPair,
|
||||||
},
|
},
|
||||||
renderer_vk::text::{FontWeight, HorizontalAlign, TextStyle},
|
renderer_vk::text::{FontWeight, HorizontalAlign, TextStyle},
|
||||||
widget::util::WLength,
|
widget::util::WLength,
|
||||||
@@ -45,11 +43,12 @@ pub fn parse_color_opt(value: &str, color: &mut Option<drawing::Color>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_text_style(attribs: &[(Rc<str>, Rc<str>)]) -> TextStyle {
|
pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle {
|
||||||
let mut style = TextStyle::default();
|
let mut style = TextStyle::default();
|
||||||
|
|
||||||
for (key, value) in attribs {
|
for pair in attribs {
|
||||||
match key.as_ref() {
|
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||||
|
match key {
|
||||||
"color" => {
|
"color" => {
|
||||||
if let Some(color) = parse_color_hex(value) {
|
if let Some(color) = parse_color_hex(value) {
|
||||||
style.color = Some(color);
|
style.color = Some(color);
|
||||||
@@ -79,6 +78,25 @@ pub fn parse_text_style(attribs: &[(Rc<str>, Rc<str>)]) -> TextStyle {
|
|||||||
print_invalid_attrib(key, value);
|
print_invalid_attrib(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"shadow" => {
|
||||||
|
if let Some(color) = parse_color_hex(value) {
|
||||||
|
style.shadow.get_or_insert_default().color = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"shadow_x" => {
|
||||||
|
if let Ok(x) = value.parse::<f32>() {
|
||||||
|
style.shadow.get_or_insert_default().x = x;
|
||||||
|
} else {
|
||||||
|
print_invalid_attrib(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"shadow_y" => {
|
||||||
|
if let Ok(y) = value.parse::<f32>() {
|
||||||
|
style.shadow.get_or_insert_default().y = y;
|
||||||
|
} else {
|
||||||
|
print_invalid_attrib(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,10 +106,11 @@ pub fn parse_text_style(attribs: &[(Rc<str>, Rc<str>)]) -> TextStyle {
|
|||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
pub fn parse_style(attribs: &[(Rc<str>, Rc<str>)]) -> taffy::Style {
|
pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
|
||||||
let mut style = taffy::Style::default();
|
let mut style = taffy::Style::default();
|
||||||
|
|
||||||
for (key, value) in attribs {
|
for pair in attribs {
|
||||||
|
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||||
match key.as_ref() {
|
match key.as_ref() {
|
||||||
"display" => match value.as_ref() {
|
"display" => match value.as_ref() {
|
||||||
"flex" => style.display = Display::Flex,
|
"flex" => style.display = Display::Flex,
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{parse_children, parse_widget_universal, style::parse_style, AttribPair, ParserContext, ParserFile},
|
||||||
ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal,
|
|
||||||
style::parse_style,
|
|
||||||
},
|
|
||||||
widget::div::WidgetDiv,
|
widget::div::WidgetDiv,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -12,15 +9,13 @@ pub fn parse_widget_div<'a, U1, U2>(
|
|||||||
ctx: &mut ParserContext<U1, U2>,
|
ctx: &mut ParserContext<U1, U2>,
|
||||||
node: roxmltree::Node<'a, 'a>,
|
node: roxmltree::Node<'a, 'a>,
|
||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
) -> anyhow::Result<WidgetID> {
|
) -> anyhow::Result<WidgetID> {
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
let style = parse_style(attribs);
|
||||||
let style = parse_style(&attribs);
|
|
||||||
|
|
||||||
let (new_id, _) = ctx
|
let (new_id, _) = ctx.layout.add_child(parent_id, WidgetDiv::create(), style)?;
|
||||||
.layout
|
|
||||||
.add_child(parent_id, WidgetDiv::create(), style)?;
|
|
||||||
|
|
||||||
parse_widget_universal(file, ctx, node, new_id);
|
parse_widget_universal(ctx, new_id, attribs);
|
||||||
parse_children(file, ctx, node, new_id)?;
|
parse_children(file, ctx, node, new_id)?;
|
||||||
|
|
||||||
Ok(new_id)
|
Ok(new_id)
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ use crate::{
|
|||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{
|
||||||
ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal,
|
parse_children, parse_widget_universal,
|
||||||
style::{parse_style, parse_text_style},
|
style::{parse_style, parse_text_style},
|
||||||
|
AttribPair, ParserContext, ParserFile,
|
||||||
},
|
},
|
||||||
widget::label::{WidgetLabel, WidgetLabelParams},
|
widget::label::{WidgetLabel, WidgetLabelParams},
|
||||||
};
|
};
|
||||||
@@ -13,23 +14,24 @@ pub fn parse_widget_label<'a, U1, U2>(
|
|||||||
ctx: &mut ParserContext<U1, U2>,
|
ctx: &mut ParserContext<U1, U2>,
|
||||||
node: roxmltree::Node<'a, 'a>,
|
node: roxmltree::Node<'a, 'a>,
|
||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
) -> anyhow::Result<WidgetID> {
|
) -> anyhow::Result<WidgetID> {
|
||||||
let mut params = WidgetLabelParams::default();
|
let mut params = WidgetLabelParams::default();
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
|
||||||
|
|
||||||
let style = parse_style(&attribs);
|
let style = parse_style(attribs);
|
||||||
params.style = parse_text_style(&attribs);
|
params.style = parse_text_style(attribs);
|
||||||
|
|
||||||
for (key, value) in attribs {
|
for pair in attribs {
|
||||||
match &*key {
|
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||||
|
match key {
|
||||||
"text" => {
|
"text" => {
|
||||||
if !value.is_empty() {
|
if !value.is_empty() {
|
||||||
params.content = Translation::from_raw_text(&value);
|
params.content = Translation::from_raw_text(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"translation" => {
|
"translation" => {
|
||||||
if !value.is_empty() {
|
if !value.is_empty() {
|
||||||
params.content = Translation::from_translation_key(&value);
|
params.content = Translation::from_translation_key(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -42,7 +44,7 @@ pub fn parse_widget_label<'a, U1, U2>(
|
|||||||
.layout
|
.layout
|
||||||
.add_child(parent_id, WidgetLabel::create(&mut globals.get(), params), style)?;
|
.add_child(parent_id, WidgetLabel::create(&mut globals.get(), params), style)?;
|
||||||
|
|
||||||
parse_widget_universal(file, ctx, node, new_id);
|
parse_widget_universal(ctx, new_id, attribs);
|
||||||
parse_children(file, ctx, node, new_id)?;
|
parse_children(file, ctx, node, new_id)?;
|
||||||
|
|
||||||
Ok(new_id)
|
Ok(new_id)
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ use crate::{
|
|||||||
drawing::GradientMode,
|
drawing::GradientMode,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{
|
||||||
ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal,
|
parse_children, parse_widget_universal, print_invalid_attrib,
|
||||||
print_invalid_attrib,
|
|
||||||
style::{parse_color, parse_round, parse_style},
|
style::{parse_color, parse_round, parse_style},
|
||||||
|
AttribPair, ParserContext, ParserFile,
|
||||||
},
|
},
|
||||||
widget::rectangle::{WidgetRectangle, WidgetRectangleParams},
|
widget::rectangle::{WidgetRectangle, WidgetRectangleParams},
|
||||||
};
|
};
|
||||||
@@ -14,42 +14,43 @@ pub fn parse_widget_rectangle<'a, U1, U2>(
|
|||||||
ctx: &mut ParserContext<U1, U2>,
|
ctx: &mut ParserContext<U1, U2>,
|
||||||
node: roxmltree::Node<'a, 'a>,
|
node: roxmltree::Node<'a, 'a>,
|
||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
) -> anyhow::Result<WidgetID> {
|
) -> anyhow::Result<WidgetID> {
|
||||||
let mut params = WidgetRectangleParams::default();
|
let mut params = WidgetRectangleParams::default();
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
|
||||||
let style = parse_style(&attribs);
|
let style = parse_style(&attribs);
|
||||||
|
|
||||||
for (key, value) in attribs {
|
for pair in attribs {
|
||||||
match &*key {
|
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||||
|
match key {
|
||||||
"color" => {
|
"color" => {
|
||||||
parse_color(&value, &mut params.color);
|
parse_color(value, &mut params.color);
|
||||||
}
|
}
|
||||||
"color2" => {
|
"color2" => {
|
||||||
parse_color(&value, &mut params.color2);
|
parse_color(value, &mut params.color2);
|
||||||
}
|
}
|
||||||
"gradient" => {
|
"gradient" => {
|
||||||
params.gradient = match &*value {
|
params.gradient = match value {
|
||||||
"horizontal" => GradientMode::Horizontal,
|
"horizontal" => GradientMode::Horizontal,
|
||||||
"vertical" => GradientMode::Vertical,
|
"vertical" => GradientMode::Vertical,
|
||||||
"radial" => GradientMode::Radial,
|
"radial" => GradientMode::Radial,
|
||||||
"none" => GradientMode::None,
|
"none" => GradientMode::None,
|
||||||
_ => {
|
_ => {
|
||||||
print_invalid_attrib(&key, &value);
|
print_invalid_attrib(key, value);
|
||||||
GradientMode::None
|
GradientMode::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"round" => {
|
"round" => {
|
||||||
parse_round(&value, &mut params.round);
|
parse_round(value, &mut params.round);
|
||||||
}
|
}
|
||||||
"border" => {
|
"border" => {
|
||||||
params.border = value.parse().unwrap_or_else(|_| {
|
params.border = value.parse().unwrap_or_else(|_| {
|
||||||
print_invalid_attrib(&key, &value);
|
print_invalid_attrib(key, value);
|
||||||
0.0
|
0.0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
"border_color" => {
|
"border_color" => {
|
||||||
parse_color(&value, &mut params.border_color);
|
parse_color(value, &mut params.border_color);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -59,7 +60,7 @@ pub fn parse_widget_rectangle<'a, U1, U2>(
|
|||||||
.layout
|
.layout
|
||||||
.add_child(parent_id, WidgetRectangle::create(params), style)?;
|
.add_child(parent_id, WidgetRectangle::create(params), style)?;
|
||||||
|
|
||||||
parse_widget_universal(file, ctx, node, new_id);
|
parse_widget_universal(ctx, new_id, attribs);
|
||||||
parse_children(file, ctx, node, new_id)?;
|
parse_children(file, ctx, node, new_id)?;
|
||||||
|
|
||||||
Ok(new_id)
|
Ok(new_id)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal, style::parse_style},
|
parser::{parse_children, parse_widget_universal, style::parse_style, AttribPair, ParserContext, ParserFile},
|
||||||
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
||||||
widget::sprite::{WidgetSprite, WidgetSpriteParams},
|
widget::sprite::{WidgetSprite, WidgetSpriteParams},
|
||||||
};
|
};
|
||||||
@@ -12,17 +12,18 @@ pub fn parse_widget_sprite<'a, U1, U2>(
|
|||||||
ctx: &mut ParserContext<U1, U2>,
|
ctx: &mut ParserContext<U1, U2>,
|
||||||
node: roxmltree::Node<'a, 'a>,
|
node: roxmltree::Node<'a, 'a>,
|
||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
) -> anyhow::Result<WidgetID> {
|
) -> anyhow::Result<WidgetID> {
|
||||||
let mut params = WidgetSpriteParams::default();
|
let mut params = WidgetSpriteParams::default();
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
|
||||||
let style = parse_style(&attribs);
|
let style = parse_style(&attribs);
|
||||||
|
|
||||||
let mut glyph = None;
|
let mut glyph = None;
|
||||||
for (key, value) in attribs {
|
for pair in attribs {
|
||||||
match key.as_ref() {
|
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||||
|
match key {
|
||||||
"src" => {
|
"src" => {
|
||||||
if !value.is_empty() {
|
if !value.is_empty() {
|
||||||
glyph = match CustomGlyphContent::from_assets(&mut ctx.layout.state.globals.assets(), &value) {
|
glyph = match CustomGlyphContent::from_assets(&mut ctx.layout.state.globals.assets(), value) {
|
||||||
Ok(glyph) => Some(glyph),
|
Ok(glyph) => Some(glyph),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("failed to load {value}: {e}");
|
log::warn!("failed to load {value}: {e}");
|
||||||
@@ -32,15 +33,15 @@ pub fn parse_widget_sprite<'a, U1, U2>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"src_ext" => {
|
"src_ext" => {
|
||||||
if !value.is_empty() && std::fs::exists(value.as_ref()).unwrap_or(false) {
|
if !value.is_empty() && std::fs::exists(value).unwrap_or(false) {
|
||||||
glyph = CustomGlyphContent::from_file(&value).ok();
|
glyph = CustomGlyphContent::from_file(value).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"color" => {
|
"color" => {
|
||||||
if let Some(color) = parse_color_hex(&value) {
|
if let Some(color) = parse_color_hex(value) {
|
||||||
params.color = Some(color);
|
params.color = Some(color);
|
||||||
} else {
|
} else {
|
||||||
print_invalid_attrib(&key, &value);
|
print_invalid_attrib(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -55,7 +56,7 @@ pub fn parse_widget_sprite<'a, U1, U2>(
|
|||||||
|
|
||||||
let (new_id, _) = ctx.layout.add_child(parent_id, WidgetSprite::create(params), style)?;
|
let (new_id, _) = ctx.layout.add_child(parent_id, WidgetSprite::create(params), style)?;
|
||||||
|
|
||||||
parse_widget_universal(file, ctx, node, new_id);
|
parse_widget_universal(ctx, new_id, attribs);
|
||||||
parse_children(file, ctx, node, new_id)?;
|
parse_children(file, ctx, node, new_id)?;
|
||||||
|
|
||||||
Ok(new_id)
|
Ok(new_id)
|
||||||
|
|||||||
@@ -2,20 +2,20 @@ use std::{cell::RefCell, rc::Rc, sync::Arc};
|
|||||||
|
|
||||||
use cosmic_text::Buffer;
|
use cosmic_text::Buffer;
|
||||||
use glam::{Mat4, Vec2, Vec3};
|
use glam::{Mat4, Vec2, Vec3};
|
||||||
use slotmap::{SlotMap, new_key_type};
|
use slotmap::{new_key_type, SlotMap};
|
||||||
use vulkano::pipeline::graphics::viewport;
|
use vulkano::pipeline::graphics::viewport;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
drawing::{self},
|
drawing::{self},
|
||||||
gfx::{WGfx, cmd::GfxCommandBuffer},
|
gfx::{cmd::GfxCommandBuffer, WGfx},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
rect::{RectPipeline, RectRenderer},
|
rect::{RectPipeline, RectRenderer},
|
||||||
text::{
|
text::{
|
||||||
DEFAULT_METRICS, FONT_SYSTEM, SWASH_CACHE, TextArea, TextBounds,
|
|
||||||
text_atlas::{TextAtlas, TextPipeline},
|
text_atlas::{TextAtlas, TextPipeline},
|
||||||
text_renderer::TextRenderer,
|
text_renderer::TextRenderer,
|
||||||
|
TextArea, TextBounds, DEFAULT_METRICS, FONT_SYSTEM, SWASH_CACHE,
|
||||||
},
|
},
|
||||||
viewport::Viewport,
|
viewport::Viewport,
|
||||||
};
|
};
|
||||||
@@ -248,7 +248,20 @@ impl Context {
|
|||||||
.rect_renderer
|
.rect_renderer
|
||||||
.add_rect(extent.boundary, *rectangle, &extent.transform);
|
.add_rect(extent.boundary, *rectangle, &extent.transform);
|
||||||
}
|
}
|
||||||
drawing::RenderPrimitive::Text(extent, text) => {
|
drawing::RenderPrimitive::Text(extent, text, shadow) => {
|
||||||
|
if let Some(shadow) = shadow {
|
||||||
|
pass.text_areas.push(TextArea {
|
||||||
|
buffer: text.clone(),
|
||||||
|
left: (extent.boundary.pos.x + shadow.x) * self.pixel_scale,
|
||||||
|
top: (extent.boundary.pos.y + shadow.y) * self.pixel_scale,
|
||||||
|
bounds: TextBounds::default(), //FIXME: just using boundary coords here doesn't work
|
||||||
|
scale: self.pixel_scale,
|
||||||
|
default_color: cosmic_text::Color::rgb(0, 0, 0),
|
||||||
|
override_color: Some(shadow.color.into()),
|
||||||
|
custom_glyphs: &[],
|
||||||
|
transform: extent.transform,
|
||||||
|
});
|
||||||
|
}
|
||||||
pass.text_areas.push(TextArea {
|
pass.text_areas.push(TextArea {
|
||||||
buffer: text.clone(),
|
buffer: text.clone(),
|
||||||
left: extent.boundary.pos.x * self.pixel_scale,
|
left: extent.boundary.pos.x * self.pixel_scale,
|
||||||
@@ -256,6 +269,7 @@ impl Context {
|
|||||||
bounds: TextBounds::default(), //FIXME: just using boundary coords here doesn't work
|
bounds: TextBounds::default(), //FIXME: just using boundary coords here doesn't work
|
||||||
scale: self.pixel_scale,
|
scale: self.pixel_scale,
|
||||||
default_color: cosmic_text::Color::rgb(0, 0, 0),
|
default_color: cosmic_text::Color::rgb(0, 0, 0),
|
||||||
|
override_color: None,
|
||||||
custom_glyphs: &[],
|
custom_glyphs: &[],
|
||||||
transform: extent.transform,
|
transform: extent.transform,
|
||||||
});
|
});
|
||||||
@@ -269,6 +283,7 @@ impl Context {
|
|||||||
scale: self.pixel_scale,
|
scale: self.pixel_scale,
|
||||||
custom_glyphs: sprites.as_slice(),
|
custom_glyphs: sprites.as_slice(),
|
||||||
default_color: cosmic_text::Color::rgb(255, 0, 255),
|
default_color: cosmic_text::Color::rgb(255, 0, 255),
|
||||||
|
override_color: None,
|
||||||
transform: extent.transform,
|
transform: extent.transform,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,23 @@ const DEFAULT_LINE_HEIGHT_RATIO: f32 = 1.43;
|
|||||||
pub(crate) const DEFAULT_METRICS: Metrics =
|
pub(crate) const DEFAULT_METRICS: Metrics =
|
||||||
Metrics::new(DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE * DEFAULT_LINE_HEIGHT_RATIO);
|
Metrics::new(DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE * DEFAULT_LINE_HEIGHT_RATIO);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TextShadow {
|
||||||
|
pub y: f32,
|
||||||
|
pub x: f32,
|
||||||
|
pub color: drawing::Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TextShadow {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
y: 1.5,
|
||||||
|
x: 1.5,
|
||||||
|
color: drawing::Color::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct TextStyle {
|
pub struct TextStyle {
|
||||||
pub size: Option<f32>,
|
pub size: Option<f32>,
|
||||||
@@ -34,6 +51,7 @@ pub struct TextStyle {
|
|||||||
pub weight: Option<FontWeight>,
|
pub weight: Option<FontWeight>,
|
||||||
pub align: Option<HorizontalAlign>,
|
pub align: Option<HorizontalAlign>,
|
||||||
pub wrap: bool,
|
pub wrap: bool,
|
||||||
|
pub shadow: Option<TextShadow>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&TextStyle> for Attrs<'_> {
|
impl From<&TextStyle> for Attrs<'_> {
|
||||||
@@ -60,7 +78,11 @@ impl From<&TextStyle> for Metrics {
|
|||||||
|
|
||||||
impl From<&TextStyle> for Wrap {
|
impl From<&TextStyle> for Wrap {
|
||||||
fn from(value: &TextStyle) -> Self {
|
fn from(value: &TextStyle) -> Self {
|
||||||
if value.wrap { Self::WordOrGlyph } else { Self::None }
|
if value.wrap {
|
||||||
|
Self::WordOrGlyph
|
||||||
|
} else {
|
||||||
|
Self::None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,6 +221,8 @@ pub struct TextArea<'a> {
|
|||||||
pub bounds: TextBounds,
|
pub bounds: TextBounds,
|
||||||
/// The default color of the text area.
|
/// The default color of the text area.
|
||||||
pub default_color: Color,
|
pub default_color: Color,
|
||||||
|
/// Override text color. Used for shadow.
|
||||||
|
pub override_color: Option<Color>,
|
||||||
/// Additional custom glyphs to render.
|
/// Additional custom glyphs to render.
|
||||||
pub custom_glyphs: &'a [CustomGlyph],
|
pub custom_glyphs: &'a [CustomGlyph],
|
||||||
/// Text transformation
|
/// Text transformation
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ContentType, FontSystem, GlyphDetails, GpuCacheStatus, SwashCache, TextArea,
|
|
||||||
custom_glyph::{CustomGlyphCacheKey, RasterizeCustomGlyphRequest, RasterizedCustomGlyph},
|
custom_glyph::{CustomGlyphCacheKey, RasterizeCustomGlyphRequest, RasterizedCustomGlyph},
|
||||||
text_atlas::{GlyphVertex, TextAtlas, TextPipeline},
|
text_atlas::{GlyphVertex, TextAtlas, TextPipeline},
|
||||||
|
ContentType, FontSystem, GlyphDetails, GpuCacheStatus, SwashCache, TextArea,
|
||||||
};
|
};
|
||||||
use cosmic_text::{Color, SubpixelBin, SwashContent};
|
use cosmic_text::{Color, SubpixelBin, SwashContent};
|
||||||
use glam::{Mat4, Vec2, Vec3};
|
use glam::{Mat4, Vec2, Vec3};
|
||||||
@@ -96,7 +96,10 @@ impl TextRenderer {
|
|||||||
y_bin,
|
y_bin,
|
||||||
});
|
});
|
||||||
|
|
||||||
let color = glyph.color.unwrap_or(text_area.default_color);
|
let color = text_area
|
||||||
|
.override_color
|
||||||
|
.or(glyph.color)
|
||||||
|
.unwrap_or(text_area.default_color);
|
||||||
|
|
||||||
if let Some(glyph_to_render) = prepare_glyph(
|
if let Some(glyph_to_render) = prepare_glyph(
|
||||||
&mut PrepareGlyphParams {
|
&mut PrepareGlyphParams {
|
||||||
@@ -168,10 +171,10 @@ impl TextRenderer {
|
|||||||
for glyph in run.glyphs {
|
for glyph in run.glyphs {
|
||||||
let physical_glyph = glyph.physical((text_area.left, text_area.top), text_area.scale);
|
let physical_glyph = glyph.physical((text_area.left, text_area.top), text_area.scale);
|
||||||
|
|
||||||
let color = match glyph.color_opt {
|
let color = text_area
|
||||||
Some(some) => some,
|
.override_color
|
||||||
None => text_area.default_color,
|
.or(glyph.color_opt)
|
||||||
};
|
.unwrap_or(text_area.default_color);
|
||||||
|
|
||||||
if let Some(glyph_to_render) = prepare_glyph(
|
if let Some(glyph_to_render) = prepare_glyph(
|
||||||
&mut PrepareGlyphParams {
|
&mut PrepareGlyphParams {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use crate::{
|
|||||||
globals::Globals,
|
globals::Globals,
|
||||||
i18n::{I18n, Translation},
|
i18n::{I18n, Translation},
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
renderer_vk::text::{FONT_SYSTEM, TextStyle},
|
renderer_vk::text::{TextStyle, FONT_SYSTEM},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{WidgetObj, WidgetState};
|
use super::{WidgetObj, WidgetState};
|
||||||
@@ -124,6 +124,7 @@ impl WidgetObj for WidgetLabel {
|
|||||||
transform: state.transform_stack.get().transform,
|
transform: state.transform_stack.get().transform,
|
||||||
},
|
},
|
||||||
self.buffer.clone(),
|
self.buffer.clone(),
|
||||||
|
self.params.style.shadow.clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ use crate::{
|
|||||||
drawing::{self, PrimitiveExtent},
|
drawing::{self, PrimitiveExtent},
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
renderer_vk::text::{
|
renderer_vk::text::{
|
||||||
DEFAULT_METRICS, FONT_SYSTEM,
|
|
||||||
custom_glyph::{CustomGlyph, CustomGlyphData},
|
custom_glyph::{CustomGlyph, CustomGlyphData},
|
||||||
|
DEFAULT_METRICS, FONT_SYSTEM,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,6 +81,7 @@ impl WidgetObj for WidgetSprite {
|
|||||||
transform: state.transform_stack.get().transform,
|
transform: state.transform_stack.get().transform,
|
||||||
},
|
},
|
||||||
Rc::new(RefCell::new(buffer)),
|
Rc::new(RefCell::new(buffer)),
|
||||||
|
None,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,13 +20,16 @@
|
|||||||
|
|
||||||
<template name="Device">
|
<template name="Device">
|
||||||
<sprite color="~device_color" width="${size}" height="${size}" src="${src}" />
|
<sprite color="~device_color" width="${size}" height="${size}" src="${src}" />
|
||||||
|
<div position="absolute" margin_top="10" margin_left="9">
|
||||||
|
<label _source="battery" _device="${device}" size="18" shadow="#000000" weight="bold" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="Set">
|
<template name="Set">
|
||||||
<Button macro="button_style" _press="::OverlayToggle ${handle}">
|
<Button macro="button_style" _press="::OverlayToggle ${handle}">
|
||||||
<sprite width="40" height="40" color="~set_color" src="watch/set2.svg" />
|
<sprite width="40" height="40" color="~set_color" src="watch/set2.svg" />
|
||||||
<div position="absolute" margin_top="11">
|
<div position="absolute" margin_top="9">
|
||||||
<label text="${display}" size="24" color="#000000" weight="bold" />
|
<label text="${display}" size="24" color="#00050F" weight="bold" />
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
@@ -35,12 +38,12 @@
|
|||||||
<div width="400" height="200">
|
<div width="400" height="200">
|
||||||
<rectangle width="100%" height="100%" padding="4" box_sizing="content_box" flex_wrap="wrap" flex_direction="column" gap="4" color="~bg_color">
|
<rectangle width="100%" height="100%" padding="4" box_sizing="content_box" flex_wrap="wrap" flex_direction="column" gap="4" color="~bg_color">
|
||||||
<div width="100%" flex_direction="row">
|
<div width="100%" flex_direction="row">
|
||||||
<Device src="watch/hmd.svg" size="40" />
|
<Device src="watch/hmd.svg" size="40" device="0" />
|
||||||
<Device src="watch/controller_l.svg" size="36" />
|
<Device src="watch/controller_l.svg" size="36" device="1" />
|
||||||
<Device src="watch/controller_r.svg" size="36" />
|
<Device src="watch/controller_r.svg" size="36" device="2" />
|
||||||
<Device src="watch/track3.svg" size="40" />
|
<Device src="watch/track3.svg" size="40" device="3" />
|
||||||
<Device src="watch/track3.svg" size="40" />
|
<Device src="watch/track3.svg" size="40" device="4" />
|
||||||
<Device src="watch/track3.svg" size="40" />
|
<Device src="watch/track3.svg" size="40" device="5" />
|
||||||
</div>
|
</div>
|
||||||
<div flex_direction="row">
|
<div flex_direction="row">
|
||||||
<div flex_direction="column" padding="4">
|
<div flex_direction="column" padding="4">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use wgui::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{common::OverlaySelector, overlay::OverlayID, task::TaskType, wayvr::WayVRAction},
|
backend::{common::OverlaySelector, overlay::OverlayID, task::TaskType, wayvr::WayVRAction},
|
||||||
config::{AStrSetExt, save_layout},
|
config::{save_layout, AStrSetExt},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,8 +24,8 @@ pub(super) fn setup_custom_button<S>(
|
|||||||
_app: &AppState,
|
_app: &AppState,
|
||||||
) {
|
) {
|
||||||
const EVENTS: [(&str, EventListenerKind); 2] = [
|
const EVENTS: [(&str, EventListenerKind); 2] = [
|
||||||
("press", EventListenerKind::MousePress),
|
("_press", EventListenerKind::MousePress),
|
||||||
("release", EventListenerKind::MouseRelease),
|
("_release", EventListenerKind::MouseRelease),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (name, kind) in &EVENTS {
|
for (name, kind) in &EVENTS {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use wgui::{
|
|||||||
event::{self, EventCallback, EventListenerCollection, ListenerHandleVec},
|
event::{self, EventCallback, EventListenerCollection, ListenerHandleVec},
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::Layout,
|
layout::Layout,
|
||||||
parser::{CustomAttribsInfoOwned, parse_color_hex},
|
parser::{parse_color_hex, CustomAttribsInfoOwned},
|
||||||
widget::label::WidgetLabel,
|
widget::label::WidgetLabel,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,14 +31,14 @@ pub(super) fn setup_custom_label<S>(
|
|||||||
listener_handles: &mut ListenerHandleVec,
|
listener_handles: &mut ListenerHandleVec,
|
||||||
app: &AppState,
|
app: &AppState,
|
||||||
) {
|
) {
|
||||||
let Some(source) = attribs.get_value("source") else {
|
let Some(source) = attribs.get_value("_source") else {
|
||||||
log::warn!("custom label with no source!");
|
log::warn!("custom label with no source!");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let callback: EventCallback<AppState, S> = match source {
|
let callback: EventCallback<AppState, S> = match source {
|
||||||
"shell" => {
|
"shell" => {
|
||||||
let Some(exec) = attribs.get_value("exec") else {
|
let Some(exec) = attribs.get_value("_exec") else {
|
||||||
log::warn!("label with shell source but no exec attribute!");
|
log::warn!("label with shell source but no exec attribute!");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -57,7 +57,7 @@ pub(super) fn setup_custom_label<S>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
"fifo" => {
|
"fifo" => {
|
||||||
let Some(path) = attribs.get_value("path") else {
|
let Some(path) = attribs.get_value("_path") else {
|
||||||
log::warn!("label with fifo source but no path attribute!");
|
log::warn!("label with fifo source but no path attribute!");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -76,7 +76,7 @@ pub(super) fn setup_custom_label<S>(
|
|||||||
}
|
}
|
||||||
"battery" => {
|
"battery" => {
|
||||||
let Some(device) = attribs
|
let Some(device) = attribs
|
||||||
.get_value("device")
|
.get_value("_device")
|
||||||
.and_then(|s| s.parse::<usize>().ok())
|
.and_then(|s| s.parse::<usize>().ok())
|
||||||
else {
|
else {
|
||||||
log::warn!("label with battery source but no device attribute!");
|
log::warn!("label with battery source but no device attribute!");
|
||||||
@@ -85,19 +85,19 @@ pub(super) fn setup_custom_label<S>(
|
|||||||
|
|
||||||
let state = BatteryLabelState {
|
let state = BatteryLabelState {
|
||||||
low_color: attribs
|
low_color: attribs
|
||||||
.get_value("low_color")
|
.get_value("_low_color")
|
||||||
.and_then(parse_color_hex)
|
.and_then(parse_color_hex)
|
||||||
.unwrap_or(BAT_LOW),
|
.unwrap_or(BAT_LOW),
|
||||||
normal_color: attribs
|
normal_color: attribs
|
||||||
.get_value("normal_color")
|
.get_value("_normal_color")
|
||||||
.and_then(parse_color_hex)
|
.and_then(parse_color_hex)
|
||||||
.unwrap_or(BAT_NORMAL),
|
.unwrap_or(BAT_NORMAL),
|
||||||
charging_color: attribs
|
charging_color: attribs
|
||||||
.get_value("charging_color")
|
.get_value("_charging_color")
|
||||||
.and_then(parse_color_hex)
|
.and_then(parse_color_hex)
|
||||||
.unwrap_or(BAT_CHARGING),
|
.unwrap_or(BAT_CHARGING),
|
||||||
low_threshold: attribs
|
low_threshold: attribs
|
||||||
.get_value("low_threshold")
|
.get_value("_low_threshold")
|
||||||
.and_then(|s| s.parse().ok())
|
.and_then(|s| s.parse().ok())
|
||||||
.unwrap_or(BAT_LOW_THRESHOLD),
|
.unwrap_or(BAT_LOW_THRESHOLD),
|
||||||
device,
|
device,
|
||||||
@@ -108,7 +108,7 @@ pub(super) fn setup_custom_label<S>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
"clock" => {
|
"clock" => {
|
||||||
let Some(display) = attribs.get_value("display") else {
|
let Some(display) = attribs.get_value("_display") else {
|
||||||
log::warn!("label with clock source but no display attribute!");
|
log::warn!("label with clock source but no display attribute!");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -116,7 +116,7 @@ pub(super) fn setup_custom_label<S>(
|
|||||||
let format = match display {
|
let format = match display {
|
||||||
"name" => {
|
"name" => {
|
||||||
let maybe_pretty_tz = attribs
|
let maybe_pretty_tz = attribs
|
||||||
.get_value("timezone")
|
.get_value("_timezone")
|
||||||
.and_then(|tz| tz.parse::<usize>().ok())
|
.and_then(|tz| tz.parse::<usize>().ok())
|
||||||
.and_then(|tz_idx| app.session.config.timezones.get(tz_idx))
|
.and_then(|tz_idx| app.session.config.timezones.get(tz_idx))
|
||||||
.and_then(|tz_name| {
|
.and_then(|tz_name| {
|
||||||
@@ -152,7 +152,7 @@ pub(super) fn setup_custom_label<S>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let tz_str = attribs
|
let tz_str = attribs
|
||||||
.get_value("timezone")
|
.get_value("_timezone")
|
||||||
.and_then(|tz| tz.parse::<usize>().ok())
|
.and_then(|tz| tz.parse::<usize>().ok())
|
||||||
.and_then(|tz_idx| app.session.config.timezones.get(tz_idx));
|
.and_then(|tz_idx| app.session.config.timezones.get(tz_idx));
|
||||||
|
|
||||||
@@ -376,16 +376,13 @@ fn battery_on_tick(
|
|||||||
app: &AppState,
|
app: &AppState,
|
||||||
) {
|
) {
|
||||||
let device = app.input_state.devices.get(state.device);
|
let device = app.input_state.devices.get(state.device);
|
||||||
|
|
||||||
let tags = ["", "H", "L", "R", "T"];
|
|
||||||
|
|
||||||
let label = data.obj.get_as_mut::<WidgetLabel>().unwrap();
|
let label = data.obj.get_as_mut::<WidgetLabel>().unwrap();
|
||||||
|
|
||||||
if let Some(device) = device
|
if let Some(device) = device
|
||||||
&& let Some(soc) = device.soc
|
&& let Some(soc) = device.soc
|
||||||
{
|
{
|
||||||
let soc = (soc * 100.).min(99.) as u32;
|
let soc = (soc * 100.).min(99.) as u32;
|
||||||
let text = format!("{}{}", tags[device.role as usize], soc);
|
let text = soc.to_string();
|
||||||
let color = if device.charging {
|
let color = if device.charging {
|
||||||
state.charging_color
|
state.charging_color
|
||||||
} else if soc < state.low_threshold {
|
} else if soc < state.low_threshold {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::{collections::HashMap, rc::Rc};
|
use std::{collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use glam::{Mat4, Vec2, Vec3, vec2, vec3a};
|
use glam::{vec2, vec3a, Mat4, Vec2, Vec3};
|
||||||
use wgui::{
|
use wgui::{
|
||||||
animation::{Animation, AnimationEasing},
|
animation::{Animation, AnimationEasing},
|
||||||
drawing::Color,
|
drawing::Color,
|
||||||
@@ -20,13 +20,13 @@ use crate::{
|
|||||||
backend::overlay::{OverlayData, OverlayState, Positioning},
|
backend::overlay::{OverlayData, OverlayState, Positioning},
|
||||||
gui::panel::GuiPanel,
|
gui::panel::GuiPanel,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::hid::{ALT, CTRL, META, SHIFT, SUPER, XkbKeymap},
|
subsystem::hid::{XkbKeymap, ALT, CTRL, META, SHIFT, SUPER},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
KEYBOARD_NAME, KeyButtonData, KeyState, KeyboardBackend, KeyboardState, handle_press,
|
handle_press, handle_release,
|
||||||
handle_release,
|
|
||||||
layout::{self, AltModifier, KeyCapType},
|
layout::{self, AltModifier, KeyCapType},
|
||||||
|
KeyButtonData, KeyState, KeyboardBackend, KeyboardState, KEYBOARD_NAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
const BACKGROUND_PADDING: f32 = 4.;
|
const BACKGROUND_PADDING: f32 = 4.;
|
||||||
|
|||||||
Reference in New Issue
Block a user