dash-frontend: finish theming, wayvr: refactoring, compilable version

This commit is contained in:
Aleksander 2026-07-11 17:33:01 +02:00
parent a00f2ab8db
commit 155340b78d
28 changed files with 359 additions and 248 deletions

View File

@ -12,10 +12,8 @@
round="100%"
width="~side_button_size"
height="~side_button_size"
color="#44444400"
hover_color="#333333ff"
border_color="#00000000"
hover_border_color="#555555ff"
color="background_contrast"
border_color="background_contrast"
tooltip="${tooltip}"
tooltip_side="${tooltip_side}"
>
@ -37,7 +35,7 @@
<rectangle
width="100%"
round="100%"
color="background"
color="background_contrast"
flex_direction="column"
justify_content="center"
align_items="center"
@ -63,13 +61,13 @@
<div position="relative" width="100%" height="~top_size" min_height="~top_size" max_height="~top_size" justify_content="end">
<!-- Title bar -->
<div width="100%" align_items="center" justify_content="center">
<rectangle min_width="300" height="100%" consume_mouse_events="1" round="100%" color="background" align_items="center" justify_content="center" gap="8">
<rectangle min_width="300" height="100%" consume_mouse_events="1" round="100%" color="background_contrast" align_items="center" justify_content="center" gap="8">
<sprite id="sprite_titlebar_icon" width="24" height="24" />
<label id="label_titlebar_title" weight="bold" size="16" text="Title" />
</rectangle>
</div>
<!-- Close button -->
<Button position="absolute" id="btn_close" color="background" round="100%" width="~top_size" height="~top_size" tooltip="CLOSE_WINDOW" tooltip_side="left">
<Button position="absolute" id="btn_close" color="background_contrast" border_color="background_contrast" round="100%" width="~top_size" height="~top_size" tooltip="CLOSE_WINDOW" tooltip_side="left">
<sprite src_builtin="dashboard/close.svg" width="32" height="32" />
</Button>
</div>
@ -78,8 +76,8 @@
<!-- color and color2 alpha will be modified at runtime -->
<rectangle
id="rect_content"
color2="#0d131a00"
color="#252f5300"
color2="background_contrast"
color="background"
gradient="vertical"
round="8"
flex_grow="1"
@ -90,7 +88,7 @@
<!-- radial gradient -->
<rectangle
position="absolute" width="100%" height="100%"
gradient="radial" color="#44BBFF11" color2="#00000000" />
gradient="radial" color="background_variant(opacity-0.5)" color2="background_variant(transparent)" />
<div overflow_x="scroll" overflow_y="scroll" width="100%" height="100%">
<div
@ -118,16 +116,16 @@
flex_direction="row"
align_items="center"
justify_content="space_between"
color="background_variant"
color="background"
>
<!-- left gradient -->
<div position="absolute" width="100%" height="100%" justify_content="start">
<rectangle position="absolute" width="40%" height="100%" color="#0a0a0a" color2="#0a0a0a00" round="8" gradient="horizontal" />
<rectangle position="absolute" width="40%" height="100%" color="shadow" color2="shadow(transparent)" round="8" gradient="horizontal" />
</div>
<!-- right gradient -->
<div position="absolute" width="100%" height="100%" justify_content="end">
<rectangle position="absolute" width="40%" height="100%" color2="#0a0a0a" color="#0a0a0a00" round="8" gradient="horizontal" />
<rectangle position="absolute" width="40%" height="100%" color2="shadow" color="shadow(transparent)" round="8" gradient="horizontal" />
</div>
<!-- top shine -->

View File

@ -1,5 +1,5 @@
<layout>
<macro name="home_button" round="8" color="#00000033" border_color="#FFFFFF77" hover_color="primary" hover_border_color="#FFFFFF" />
<macro name="home_button" round="8" hover_color="primary" hover_border_color="on_primary" />
<template name="MenuButton">
<Button

View File

@ -39,7 +39,7 @@
</template>
<template name="DangerButton">
<Button id="${id}" color="#AA3333" height="32" tooltip="${translation}_HELP" padding="4" gap="8">
<Button id="${id}" border_color="danger" color="danger(rgb-mult-0.5)" height="32" tooltip="${translation}_HELP" padding="4" gap="8">
<sprite src_builtin="${icon}" height="24" width="24" />
<label align="left" translation="${translation}" weight="bold" min_width="200" />
</Button>

View File

@ -15,7 +15,7 @@
<elements>
<div flex_direction="row" gap="16" flex_grow="1">
<rectangle macro="group_box" id="icon_parent" padding="16" color="#0033aa66" color2="#00000022" gradient="vertical" justify_content="center"/>
<rectangle macro="group_box" id="icon_parent" padding="16" color="background_variant(opacity-0.5)" color2="background(opacity-0.5)" gradient="vertical" justify_content="center"/>
<div flex_direction="column" gap="8" flex_grow="1">
<label id="label_title" weight="bold" size="32" overflow="hidden" />
<Subtext label_id="label_exec"/>
@ -55,11 +55,11 @@
<sprite src_builtin="dashboard/heart_minus.svg" width="32" height="32" />
<label translation="REMOVE_FROM_FAVOURITES" weight="bold" size="17" shadow="#00000099" />
</Button>
<Button id="btn_pin" color="#ce4477" padding_left="8" padding_right="8" round="8" min_height="40" tooltip="ADD_TO_FAVOURITES_HINT">
<Button id="btn_pin" color="#ce4477" border_color="#ce4477" padding_left="8" padding_right="8" round="8" min_height="40" tooltip="ADD_TO_FAVOURITES_HINT">
<sprite src_builtin="dashboard/heart_plus.svg" width="32" height="32" />
<label translation="ADD_TO_FAVOURITES" weight="bold" size="17" shadow="#00000099" />
</Button>
<Button id="btn_launch" color="#44ce22" padding_left="8" padding_right="8" round="8" min_width="200" min_height="40">
<Button id="btn_launch" color="#44ce22" border_color="#44ce22" padding_left="8" padding_right="8" round="8" min_width="200" min_height="40">
<sprite src_builtin="dashboard/play.svg" width="32" height="32" />
<label translation="APP_LAUNCHER.LAUNCH" weight="bold" size="17" shadow="#00000099" />
</Button>

View File

@ -9,11 +9,11 @@
<label weight="bold" id="label_author" text="Unknown" />
</div>
<label id="label_description" wrap="1" text="No description available" />
<Button id="btn_launch" align_self="baseline" color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_right="12" min_width="200" min_height="40">
<Button id="btn_launch" align_self="baseline" color="#44ce22ff" border_color="#44ce22ff" padding_top="4" padding_bottom="4" round="8" padding_right="12" min_width="200" min_height="40">
<sprite src_builtin="dashboard/play.svg" width="32" height="32" />
<label text="Launch" weight="bold" size="17" shadow="#00000099" />
</Button>
</div>
</div>
</elements>
</layout>
</layout>

View File

@ -9,33 +9,33 @@
flex_direction="column"
position="absolute"
>
<!-- Top black bar -->
<!-- Top bar -->
<rectangle
position="relative"
color="#000000"
color="background_contrast"
width="100%" height="48"
>
<!-- Shine effect at the top -->
<rectangle position="absolute" width="100%" height="2" round="4" color="#ffffff22" />
<rectangle position="absolute" width="100%" height="3" round="4" color="highlight(opacity-0.1)" />
<!-- Top bar contents -->
<div gap="16" align_items="center">
<!-- Back button -->
<Button id="but_back" width="48" height="48" color="#ffffff00" border_color="#ffffff00">
<sprite src_builtin="dashboard/back.svg" width="24" height="24" />
<Button id="but_back" width="48" height="48" color="on_background_contrast(transparent)" border_color="on_background_contrast(transparent)">
<sprite src_builtin="dashboard/back.svg" color="on_background_contrast" width="24" height="24" />
</Button>
<!-- Title -->
<label id="popup_title" weight="bold" size="18" />
<label id="popup_title" color="on_background_contrast" weight="bold" size="18" />
</div>
</rectangle>
<rectangle
width="100%"
height="100%"
color="#010310fe"
color2="#051c55fc"
color="background_contrast(opacity-0.98)"
color2="background(opacity-0.99)"
gradient="vertical"
position="relative"
>

View File

@ -11,7 +11,7 @@ use wgui::{
layout::{Layout, WidgetID},
log::LogErr,
parser::{Fetchable, ParseDocumentParams, ParserState},
renderer_vk::text::{FontWeight, TextStyle},
renderer_vk::text::{FontWeight, TextStyle, WguiTextShadow},
taffy::{self, prelude::length},
task::Tasks,
widget::{
@ -600,6 +600,11 @@ fn mount_requires_restart(layout: &mut Layout, parent: WidgetID) -> anyhow::Resu
color: Some(WguiColorName::Danger.into()),
weight: Some(FontWeight::Bold),
size: Some(10.0),
shadow: Some(WguiTextShadow {
x: 2.0,
y: 2.0,
..Default::default()
}),
..Default::default()
},
},

View File

@ -4,6 +4,7 @@ use wgui::{
components::button::ComponentButton, globals::WguiGlobals, layout::WidgetID, parser::Fetchable, task::Tasks,
};
use crate::tab::settings::{horiz_cell, mount_requires_restart};
use crate::util::popup_manager::PopupHolder;
use crate::{
@ -56,13 +57,15 @@ fn create_input_profiles_button(
let id = mp.idx.to_string();
mp.idx += 1;
let id_cell = horiz_cell(mp.layout, parent)?;
let mut params = TemplateParams::new();
params.insert("id", &id);
params.insert("translation", "APP_SETTINGS.INPUT_PROFILES");
params.insert("icon", "dashboard/controller.svg");
mp.parser_state
.instantiate_template(mp.doc_params, "ButtonText", mp.layout, parent, params)?;
.instantiate_template(mp.doc_params, "ButtonText", mp.layout, id_cell, params)?;
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
btn.on_click(Rc::new({
@ -73,6 +76,8 @@ fn create_input_profiles_button(
}
}));
mount_requires_restart(mp.layout, id_cell)?;
Ok(())
}

View File

@ -137,7 +137,7 @@ impl ToastManager {
let mut label = common.state.widgets.get_as::<WidgetLabel>(label.id).unwrap();
label.set_color(
common,
WguiColor::from(WguiColorName::BackgroundVariant).with_alpha(opacity),
WguiColor::from(WguiColorName::OnBackgroundVariant).with_alpha(opacity),
true,
);
common.alterables.mark_redraw();

View File

@ -11,7 +11,7 @@ use wgui::{
globals::WguiGlobals,
i18n::Translation,
layout::{Layout, WidgetID},
renderer_vk::text::{FontWeight, HorizontalAlign, TextShadow, TextStyle, custom_glyph::CustomGlyphData},
renderer_vk::text::{FontWeight, HorizontalAlign, TextStyle, WguiTextShadow, custom_glyph::CustomGlyphData},
taffy::{
self, AlignItems, AlignSelf, JustifyContent, JustifySelf,
prelude::{auto, length, percent},
@ -110,11 +110,7 @@ impl View {
wrap: true,
size: Some(16.0),
align: Some(HorizontalAlign::Center),
shadow: Some(TextShadow {
color: drawing::Color::new(0.0, 0.0, 0.0, 1.0),
x: 2.0,
y: 2.0,
}),
shadow: Some(WguiTextShadow::default()),
..Default::default()
},
},

View File

@ -211,7 +211,7 @@ fn battery_on_tick(
} else {
state.normal_color
};
label.set_color(common, color, false);
label.set_color(common, color.into(), false);
label.set_text(common, Translation::from_raw_text(&text));
return;
}

View File

@ -536,11 +536,11 @@ pub fn apply_custom_command<T>(
if let Ok(pair) = panel.parser_state.fetch_widget(com.state, element) {
if let Some(mut rect) = pair.widget.get_as::<WidgetRectangle>() {
rect.set_color(&mut com, color);
rect.set_color(&mut com, color.into());
} else if let Some(mut label) = pair.widget.get_as::<WidgetLabel>() {
label.set_color(&mut com, color, true);
label.set_color(&mut com, color.into(), true);
} else if let Some(mut sprite) = pair.widget.get_as::<WidgetSprite>() {
sprite.set_color(&mut com, color);
sprite.set_color(&mut com, color.into());
} else {
anyhow::bail!("No <rectangle> or <label> or <sprite> with such id.");
}

View File

@ -1,9 +1,9 @@
use std::rc::Rc;
use anyhow::Context;
use glam::FloatExt;
use wgui::{
animation::{Animation, AnimationEasing},
color::{WguiColor, WguiColorName, WguiColorPalette},
components::button::ComponentButton,
event::CallbackDataCommon,
layout::WidgetID,
@ -16,7 +16,7 @@ use crate::{backend::task::ModifyOverlayTask, overlays::edit::EditModeWrapPanel}
#[derive(Default)]
pub(super) struct InteractLockHandler {
id: WidgetID,
color: wgui::drawing::Color,
color: WguiColor,
interactable: bool,
button: Option<Rc<ComponentButton>>,
}
@ -53,10 +53,25 @@ impl InteractLockHandler {
button.set_sticky_state(common, !interactable);
}
let globals = common.globals();
let palette = &globals.palette;
if interactable {
set_anim_color(&mut rect, 0.0, self.color, common.state.theme.danger_color);
set_anim_color(
palette,
&mut rect,
0.0,
self.color,
WguiColorName::Danger.into(),
);
} else {
set_anim_color(&mut rect, 0.2, self.color, common.state.theme.danger_color);
set_anim_color(
palette,
&mut rect,
0.2,
self.color,
WguiColorName::Danger.into(),
);
}
}
@ -80,10 +95,11 @@ impl InteractLockHandler {
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
set_anim_color(
&common.globals().palette,
rect,
0.2 - (data.pos * 0.2),
rect_color,
common.state.theme.danger_color,
WguiColorName::Danger.into(),
);
common.alterables.mark_redraw();
}),
@ -96,10 +112,11 @@ impl InteractLockHandler {
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
set_anim_color(
&common.globals().palette,
rect,
data.pos * 0.2,
rect_color,
common.state.theme.danger_color,
WguiColorName::Danger.into(),
);
common.alterables.mark_redraw();
}),
@ -117,13 +134,12 @@ impl InteractLockHandler {
}
fn set_anim_color(
palette: &WguiColorPalette,
rect: &mut WidgetRectangle,
pos: f32,
rect_color: wgui::drawing::Color,
target_color: wgui::drawing::Color,
rect_color: WguiColor,
target_color: WguiColor,
) {
// rect to target_color
rect.params.color.r = rect_color.r.lerp(target_color.r, pos);
rect.params.color.g = rect_color.g.lerp(target_color.g, pos);
rect.params.color.b = rect_color.b.lerp(target_color.b, pos);
rect.params.color = rect_color.lerp(palette, &target_color, pos);
}

View File

@ -16,7 +16,8 @@ use glam::{FloatExt, Mat4, Vec2, vec2, vec3};
use wgui::{
animation::{Animation, AnimationEasing},
assets::AssetPath,
drawing::{self, Color},
color::{WguiColorName, WguiColorPalette},
drawing::{self},
event::{self, CallbackMetadata, EventListenerKind},
layout::LayoutUpdateParams,
log::LogErr,
@ -53,10 +54,7 @@ pub(super) fn create_keyboard_panel(
let doc_params = new_doc_params(&mut panel);
let (accent_color, anim_mult) = {
let theme = &app.wgui_theme;
(theme.accent_color, theme.animation_mult)
};
let anim_mult = app.wgui_theme.animation_mult;
let root = panel
.parser_state
@ -178,14 +176,7 @@ pub(super) fn create_keyboard_panel(
let k = key_state.clone();
move |common, data, _app, _state| {
common.alterables.trigger_haptics();
on_enter_anim(
k.clone(),
common,
data,
accent_color,
anim_mult,
width_mul,
);
on_enter_anim(k.clone(), common, data, anim_mult, width_mul);
Ok(EventResult::Pass)
}
}),
@ -197,14 +188,7 @@ pub(super) fn create_keyboard_panel(
let k = key_state.clone();
move |common, data, _app, _state| {
common.alterables.trigger_haptics();
on_leave_anim(
k.clone(),
common,
data,
accent_color,
anim_mult,
width_mul,
);
on_leave_anim(k.clone(), common, data, anim_mult, width_mul);
Ok(EventResult::Pass)
}
}),
@ -350,27 +334,28 @@ fn get_anim_transform(pos: f32, widget_size: Vec2, width_mult: f32) -> Mat4 {
}
fn set_anim_color(
palette: &WguiColorPalette,
key_state: &KeyState,
rect: &mut WidgetRectangle,
pos: f32,
accent_color: drawing::Color,
) {
// fade to accent color
rect.params.color.r = key_state.color.r.lerp(accent_color.r, pos);
rect.params.color.g = key_state.color.g.lerp(accent_color.g, pos);
rect.params.color.b = key_state.color.b.lerp(accent_color.b, pos);
rect.params.color = key_state
.color
.lerp(palette, &WguiColorName::Primary.into(), pos);
// fade to accent color
rect.params.color2.r = key_state.color2.r.lerp(accent_color.r, pos);
rect.params.color2.g = key_state.color2.g.lerp(accent_color.g, pos);
rect.params.color2.b = key_state.color2.b.lerp(accent_color.b, pos);
rect.params.color2 = key_state
.color2
.lerp(palette, &WguiColorName::Primary.into(), pos);
// fade to white
let cur_border_color = key_state.cur_border_color.get();
rect.params.border_color.r = cur_border_color.r.lerp(1.0, pos);
rect.params.border_color.g = cur_border_color.g.lerp(1.0, pos);
rect.params.border_color.b = cur_border_color.b.lerp(1.0, pos);
rect.params.border_color.a = cur_border_color.a.lerp(1.0, pos);
rect.params.border_color = cur_border_color.lerp(
palette,
&drawing::Color::new(1.0, 1.0, 1.0, 1.0).into(),
pos,
);
rect.params.border = key_state.border.lerp(key_state.border * 1.5, pos);
}
@ -379,7 +364,6 @@ fn on_enter_anim(
key_state: Rc<KeyState>,
common: &mut event::CallbackDataCommon,
data: &event::CallbackData,
accent_color: drawing::Color,
anim_mult: f32,
width_mult: f32,
) {
@ -389,7 +373,7 @@ fn on_enter_anim(
AnimationEasing::OutBack,
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
set_anim_color(&key_state, rect, data.pos, accent_color);
set_anim_color(&common.globals().palette, &key_state, rect, data.pos);
data.data.transform =
get_anim_transform(data.pos, data.widget_boundary.size, width_mult);
common.alterables.mark_redraw();
@ -401,7 +385,6 @@ fn on_leave_anim(
key_state: Rc<KeyState>,
common: &mut event::CallbackDataCommon,
data: &event::CallbackData,
accent_color: drawing::Color,
anim_mult: f32,
width_mult: f32,
) {
@ -411,7 +394,7 @@ fn on_leave_anim(
AnimationEasing::OutQuad,
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
set_anim_color(&key_state, rect, 1.0 - data.pos, accent_color);
set_anim_color(&common.globals().palette, &key_state, rect, 1.0 - data.pos);
data.data.transform =
get_anim_transform(1.0 - data.pos, data.widget_boundary.size, width_mult);
common.alterables.mark_redraw();
@ -430,7 +413,7 @@ fn on_press_anim(
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
key_state
.cur_border_color
.set(Color::new(1.0, 1.0, 1.0, 1.0));
.set(drawing::Color::new(1.0, 1.0, 1.0, 1.0).into());
rect.params.border_color = key_state.cur_border_color.get();
common.alterables.mark_redraw();
key_state.drawn_state.set(true);

View File

@ -31,7 +31,7 @@ use glam::{Affine3A, Quat, Vec3, vec3};
use regex::Regex;
use slotmap::{SlotMap, new_key_type};
use wgui::{
drawing,
color::WguiColor,
event::{InternalStateChangeEvent, MouseButtonEvent, MouseButtonIndex},
};
use wlx_common::windowing::{OverlayWindowState, Positioning};
@ -357,10 +357,10 @@ fn play_key_click(app: &mut AppState) {
struct KeyState {
button_state: KeyButtonData,
color: drawing::Color,
color2: drawing::Color,
base_border_color: drawing::Color,
cur_border_color: Cell<drawing::Color>,
color: WguiColor,
color2: WguiColor,
base_border_color: WguiColor,
cur_border_color: Cell<WguiColor>,
border: f32,
drawn_state: Cell<bool>,
}

View File

@ -9,7 +9,7 @@ use std::sync::Arc;
use wgui::log::LogErr;
use wgui::theme::WguiTheme;
use wgui::{
drawing, font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals,
font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals,
renderer_vk::context::SharedContext as WSharedContext,
};
#[cfg(feature = "pipewire")]
@ -147,21 +147,6 @@ impl AppState {
let mut theme = WguiTheme::default();
{
#[allow(clippy::ref_option)]
fn apply_color(default: &mut drawing::Color, value: &Option<String>) {
if let Some(parsed) = value.as_ref().and_then(|c| drawing::Color::from_hex(c)) {
*default = parsed;
}
}
apply_color(&mut theme.text_color, &session.config.color_text);
apply_color(&mut theme.accent_color, &session.config.color_accent);
apply_color(&mut theme.danger_color, &session.config.color_danger);
apply_color(&mut theme.faded_color, &session.config.color_faded);
apply_color(&mut theme.bg_color, &session.config.color_background);
}
theme.animation_mult = 1. / session.config.ui_animation_speed;
theme.rounding_mult = session.config.ui_round_multiplier;

View File

@ -1,15 +1,15 @@
<layout>
<!-- text: str -->
<template name="Cell">
<Button id="button" text="${text}" tooltip_str="${tooltip_str}" weight="bold" border="0" padding="4" color="#FFFFFF00" height="24" justify_content="start" />
<Button id="button" text="${text}" tooltip_str="${tooltip_str}" weight="bold" border="0" padding="4" color="background(transparent)" hover_color="primary" height="24" justify_content="start" />
</template>
<template name="Separator">
<rectangle width="100%" height="2" color="primary" />
<rectangle width="100%" height="2" color="outline" />
</template>
<elements>
<rectangle consume_mouse_events="1" color="#000000fa" round="8" border="2" border_color="primary" gap="4" padding="8" id="buttons" flex_direction="column">
<rectangle consume_mouse_events="1" color="background(opacity-0.98)" round="8" border="2" border_color="outline" gap="4" padding="8" id="buttons" flex_direction="column">
</rectangle>
</elements>

View File

@ -6,20 +6,21 @@
and WINDOW_DECORATION_HEADER_HEIGHT in the source code accordingly
-->
<rectangle
position="absolute"
consume_mouse_events="1"
flex_direction="column"
round="16"
round="8"
border="2"
border_color="#ffffff33"
color="#080c20fa"
border_color="outline"
color="background"
padding="2">
<!-- window title -->
<div width="100%" height="32" align_items="center" position="relative" justify_content="end">
<div width="100%" justify_content="center">
<label id="text_window_title" weight="bold" />
<label id="text_window_title" weight="bold" color="on_background" />
</div>
<Button id="but_close" border="0" round="16" color="#FFFFFF00" position="absolute">
<sprite src_internal="wgui/close.svg" width="32" height="32" />
<Button id="but_close" border="0" round="4" color="on_background(transparent)" hover_color="primary" position="absolute">
<sprite src_internal="wgui/close.svg" width="32" height="32" color="on_background" />
</Button>
</div>
@ -27,6 +28,7 @@
<div id="content" padding="8" padding_top="0" gap="4" flex_direction="column">
</div>
</rectangle>
</elements>
</layout>
</layout>

View File

@ -17,9 +17,13 @@ pub enum WguiColorName {
OnDanger,
Background,
OnBackground,
Outline,
BackgroundVariant,
OnBackgroundVariant,
BackgroundContrast,
OnBackgroundContrast,
Outline,
Shadow,
Highlight,
}
pub struct WguiColorPalette {
@ -50,34 +54,70 @@ impl WguiColorPalette {
colors[WguiColorName::OnPrimary as usize] = (drawing::Color::from_hex("#eaf7ff").unwrap(), "on_primary");
colors[WguiColorName::Secondary as usize] = (drawing::Color::from_hex("#424b56").unwrap(), "secondary");
colors[WguiColorName::OnSecondary as usize] = (drawing::Color::from_hex("#d2e6ff").unwrap(), "on_secondary");
colors[WguiColorName::Tertiary as usize] = (drawing::Color::from_hex("#1aedcd").unwrap(), "tertiary");
colors[WguiColorName::Tertiary as usize] = (drawing::Color::from_hex("#10d0b3").unwrap(), "tertiary");
colors[WguiColorName::OnTertiary as usize] = (drawing::Color::from_hex("#d1fff8").unwrap(), "on_tertiary");
colors[WguiColorName::Danger as usize] = (drawing::Color::from_hex("#ea0c76").unwrap(), "danger");
colors[WguiColorName::Danger as usize] = (drawing::Color::from_hex("#f7469a").unwrap(), "danger");
colors[WguiColorName::OnDanger as usize] = (drawing::Color::from_hex("#ffebf5").unwrap(), "on_danger");
colors[WguiColorName::Background as usize] = (drawing::Color::from_hex("#00121a").unwrap(), "background");
colors[WguiColorName::Background as usize] = (drawing::Color::from_hex("#002e43").unwrap(), "background");
colors[WguiColorName::OnBackground as usize] = (drawing::Color::from_hex("#e4f5f6").unwrap(), "on_background");
colors[WguiColorName::Outline as usize] = (drawing::Color::from_hex("#204353").unwrap(), "outline");
colors[WguiColorName::BackgroundVariant as usize] =
(drawing::Color::from_hex("#031e2a").unwrap(), "background_variant");
(drawing::Color::from_hex("#0c5170").unwrap(), "background_variant");
colors[WguiColorName::OnBackgroundVariant as usize] =
(drawing::Color::from_hex("#e2fdff").unwrap(), "on_background_variant");
colors[WguiColorName::BackgroundContrast as usize] =
(drawing::Color::from_hex("#00131c").unwrap(), "background_contrast");
colors[WguiColorName::OnBackgroundContrast as usize] =
(drawing::Color::from_hex("#e4edf6").unwrap(), "on_background_contrast");
colors[WguiColorName::Outline as usize] = (drawing::Color::from_hex("#1c6788").unwrap(), "outline");
colors[WguiColorName::Shadow as usize] = (drawing::Color::from_hex("#000000").unwrap(), "shadow");
colors[WguiColorName::Highlight as usize] = (drawing::Color::from_hex("#ffffff").unwrap(), "highlight");
WguiColorPalette { colors }
}
pub fn find(&self, in_name: &str) -> Option<WguiColor> {
fn resolve_name(&self, in_name: &str) -> Option<WguiColorName> {
for (idx, (_, name)) in self.colors.iter().enumerate() {
if in_name == *name {
return Some(
WguiColorName::try_from(idx as u8)
.unwrap() /* this never fails */
.into(),
);
return WguiColorName::try_from(idx as u8).ok();
}
}
None
}
fn apply_modifier(color: WguiColor, modifier: &str) -> Option<WguiColor> {
match modifier {
"transparent" => Some(color.with_alpha(0.0)),
"opaque" => Some(color.with_alpha(1.0)),
other => {
let (prefix, val_str) = other.rsplit_once('-')?;
let val = val_str.parse::<f32>().ok()?;
match prefix {
"opacity" => Some(color.with_alpha(val)),
"rgb-mult" => Some(color.mult_rgb(val)),
"rgb-add" => Some(color.add_rgb(val)),
_ => None,
}
}
}
}
pub fn find(&self, in_name: &str) -> Option<WguiColor> {
if let Some((base, modifiers_str)) = in_name.split_once('(') {
let base_name = base.trim();
let modifiers = modifiers_str.trim_end_matches(')');
let name = self.resolve_name(base_name)?;
let mut color = name.to_wgui_color();
for mod_str in modifiers.split(',') {
color = WguiColorPalette::apply_modifier(color, mod_str.trim())?;
}
Some(color)
} else {
self.resolve_name(in_name).map(|n| n.to_wgui_color())
}
}
}
impl Default for WguiColorPalette {

View File

@ -10,11 +10,10 @@ use crate::{
animation::{Animation, AnimationEasing},
color::{WguiColor, WguiColorName},
components::{Component, ComponentBase, ComponentTrait, FocusChangeData, RefreshData},
drawing::Color,
event::{self, CallbackDataCommon, CallbackMetadata, EventListenerCollection, EventListenerKind, StyleSetRequest},
i18n::Translation,
layout::{WidgetID, WidgetPair},
renderer_vk::text::{TextShadow, TextStyle},
renderer_vk::text::{TextStyle, WguiTextShadow},
widget::{
ConstructEssentials, EventResult,
div::WidgetDiv,
@ -342,10 +341,10 @@ pub fn construct(
WidgetLabelParams {
content: Translation::from_raw_text(&params.initial_text),
style: TextStyle {
shadow: Some(TextShadow {
shadow: Some(WguiTextShadow {
x: 1.0,
y: 1.0,
color: Color::new(0.0, 0.0, 0.0, 1.0),
..Default::default()
}),
..Default::default()
},

View File

@ -4,7 +4,7 @@ use glam::{Mat4, Vec2, Vec3};
use taffy::prelude::{length, percent};
use crate::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationCallback, AnimationEasing},
color::{WguiColor, WguiColorName, WguiColorPalette},
components::{
Component, ComponentBase, ComponentTrait, RefreshData,
@ -115,8 +115,8 @@ impl TooltipTrait for State {
#[allow(clippy::struct_field_names)]
struct SliderHandleData {
id_handle_rect: WidgetID, // Rectangle
id_text: Option<WidgetID>, // Text
id_handle_rect: WidgetID, // Rectangle
id_label: Option<WidgetID>, // Label
id_handle: WidgetID,
}
@ -328,8 +328,8 @@ impl State {
common.alterables.mark_dirty(handle_data.id_handle);
common.alterables.mark_redraw();
if let Some(id_text) = handle_data.id_text
&& let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(id_text)
if let Some(id_label) = handle_data.id_label
&& let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(id_label)
{
Self::update_text(common, &mut label, value.get());
}
@ -346,52 +346,104 @@ impl State {
}
}
const BODY_COLOR: WguiColor = WguiColorName::BackgroundVariant.to_wgui_color();
const BODY_COLOR: WguiColor = WguiColorName::Background.to_wgui_color();
const BODY_BORDER_COLOR: WguiColor = WguiColorName::Outline.to_wgui_color();
const HANDLE_COLOR: WguiColorName = WguiColorName::Secondary;
const HANDLE_COLOR_HOVERED: WguiColor = WguiColorName::Secondary.to_wgui_color().add_rgb(0.1);
const HANDLE_BORDER_COLOR: WguiColor = WguiColorName::Primary.to_wgui_color();
const HANDLE_COLOR: WguiColorName = WguiColorName::BackgroundVariant;
const HANDLE_COLOR_HOVERED: WguiColor = WguiColorName::Secondary.to_wgui_color();
const HANDLE_BORDER_COLOR: WguiColor = WguiColorName::Outline.to_wgui_color();
const HANDLE_BORDER_COLOR_HOVERED: WguiColor = WguiColorName::Primary.to_wgui_color().mult_rgb(1.25);
const HANDLE_TEXT_COLOR: WguiColorName = WguiColorName::OnBackgroundVariant;
const HANDLE_TEXT_COLOR_HOVERED: WguiColorName = WguiColorName::OnSecondary;
const SLIDER_HOVER_SCALE: f32 = 0.25;
fn get_anim_transform(pos: f32, widget_size: Vec2) -> Mat4 {
util::centered_matrix(
widget_size,
&Mat4::from_scale(Vec3::splat(SLIDER_HOVER_SCALE.mul_add(pos, 1.0))),
)
}
fn anim_rect(rect: &mut WidgetRectangle, palette: &WguiColorPalette, pos: f32) {
rect.params.color = WguiColor::lerp(&HANDLE_COLOR.into(), palette, &HANDLE_COLOR_HOVERED, pos);
rect.params.border_color = WguiColor::lerp(&HANDLE_BORDER_COLOR, palette, &HANDLE_BORDER_COLOR_HOVERED, pos);
}
fn on_enter_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID, anim_mult: f32) {
common.alterables.animate(Animation::new(
handle_id,
(20. * anim_mult) as _,
AnimationEasing::OutBack,
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
data.data.transform = get_anim_transform(data.pos, data.widget_boundary.size);
anim_rect(rect, &common.globals().palette, data.pos);
common.alterables.mark_redraw();
}),
));
fn get_callback_rect(inverse: bool) -> AnimationCallback {
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
let pos = if inverse { 1.0 - data.pos } else { data.pos };
data.data.transform = util::centered_matrix(
data.widget_boundary.size,
&Mat4::from_scale(Vec3::splat(SLIDER_HOVER_SCALE.mul_add(pos, 1.0))),
);
anim_rect(rect, &common.globals().palette, pos);
common.alterables.mark_redraw();
})
}
fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID, anim_mult: f32) {
fn get_callback_label(inverse: bool) -> AnimationCallback {
Box::new(move |common, data| {
let label = data.obj.get_as_mut::<WidgetLabel>().unwrap();
let pos = if inverse { 1.0 - data.pos } else { data.pos };
data.data.transform = util::centered_matrix(
data.widget_boundary.size,
&Mat4::from_scale(Vec3::splat(1.0 + pos * 0.25)),
);
let text_color = HANDLE_TEXT_COLOR.to_wgui_color().lerp(
&common.globals().palette,
&HANDLE_TEXT_COLOR_HOVERED.to_wgui_color(),
pos,
);
label.set_color(common, text_color, true);
common.alterables.mark_redraw();
})
}
fn on_enter_anim(
common: &mut event::CallbackDataCommon,
id_handle: WidgetID,
id_label: Option<WidgetID>,
anim_mult: f32,
) {
let duration = 20. * anim_mult;
common.alterables.animate(Animation::new(
handle_id,
(10. * anim_mult) as _,
AnimationEasing::OutQuad,
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
data.data.transform = get_anim_transform(1.0 - data.pos, data.widget_boundary.size);
anim_rect(rect, &common.globals().palette, 1.0 - data.pos);
common.alterables.mark_redraw();
}),
id_handle,
duration as _,
AnimationEasing::OutBack,
get_callback_rect(false),
));
if let Some(id_label) = id_label {
common.alterables.animate(Animation::new(
id_label,
duration as _,
AnimationEasing::OutBack,
get_callback_label(false),
));
}
}
fn on_leave_anim(
common: &mut event::CallbackDataCommon,
id_handle: WidgetID,
id_label: Option<WidgetID>,
anim_mult: f32,
) {
let duration = 10. * anim_mult;
common.alterables.animate(Animation::new(
id_handle,
duration as _,
AnimationEasing::OutQuad,
get_callback_rect(true),
));
if let Some(id_label) = id_label {
common.alterables.animate(Animation::new(
id_label,
duration as _,
AnimationEasing::OutQuad,
get_callback_label(true),
));
}
}
fn register_event_mouse_enter(
@ -476,9 +528,9 @@ fn update_handle_hovers(
// hover state changed, run animations
if state.hovered1 != hovered1_prev {
if state.hovered1 && !hovered1_prev {
on_enter_anim(common, data.handle1.id_handle_rect, anim_mult);
on_enter_anim(common, data.handle1.id_handle_rect, data.handle1.id_label, anim_mult);
} else {
on_leave_anim(common, data.handle1.id_handle_rect, anim_mult);
on_leave_anim(common, data.handle1.id_handle_rect, data.handle1.id_label, anim_mult);
}
}
@ -486,9 +538,9 @@ fn update_handle_hovers(
&& let Some(handle2) = data.handle2.as_ref()
{
if state.hovered2 && !hovered2_prev {
on_enter_anim(common, handle2.id_handle_rect, anim_mult);
on_enter_anim(common, handle2.id_handle_rect, handle2.id_label, anim_mult);
} else {
on_leave_anim(common, handle2.id_handle_rect, anim_mult);
on_leave_anim(common, handle2.id_handle_rect, handle2.id_label, anim_mult);
}
}
}
@ -628,7 +680,7 @@ fn mount_slider_handle(
WidgetLabelParams {
content: Translation::default(),
style: TextStyle {
color: Some(WguiColorName::OnPrimary.into()),
color: Some(HANDLE_TEXT_COLOR.into()),
weight: Some(FontWeight::Bold),
align: Some(HorizontalAlign::Center),
..Default::default()
@ -642,7 +694,7 @@ fn mount_slider_handle(
Ok(SliderHandleData {
id_handle_rect: slider_handle_rect.id,
id_text: slider_text.map(|s| s.0.id),
id_label: slider_text.map(|s| s.0.id),
id_handle: slider_handle.id,
})
}

View File

@ -153,7 +153,7 @@ impl Drop for ComponentTooltip {
}
}
pub const TOOLTIP_COLOR: WguiColorName = WguiColorName::BackgroundVariant;
pub const TOOLTIP_COLOR: WguiColorName = WguiColorName::BackgroundContrast;
pub const TOOLTIP_BORDER_COLOR: WguiColorName = WguiColorName::Outline;
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentTooltip>)> {

View File

@ -34,7 +34,7 @@ pub fn parse_component_button<'a>(
let mut translation: Option<Translation> = None;
let text_style = parse_text_style(ctx, attribs, tag_name);
let text_style = parse_text_style(ctx, attribs, tag_name, "text_");
let style = parse_style(ctx, attribs, tag_name);
for pair in attribs {

View File

@ -60,67 +60,71 @@ pub fn parse_color_opt(
}
}
pub fn parse_text_style(ctx: &ParserContext<'_>, attribs: &[AttribPair], tag_name: &str) -> TextStyle {
pub fn parse_text_style(ctx: &ParserContext<'_>, attribs: &[AttribPair], tag_name: &str, prefix: &str) -> TextStyle {
let mut style = TextStyle::default();
for pair in attribs {
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
match key {
"color" => {
parse_color_opt(ctx, tag_name, key, value, &mut style.color);
if key.starts_with(prefix) {
let suffix = &key[prefix.len()..];
match suffix {
"color" => {
parse_color_opt(ctx, tag_name, key, value, &mut style.color);
}
"align" => match value {
"left" => style.align = Some(HorizontalAlign::Left),
"right" => style.align = Some(HorizontalAlign::Right),
"center" => style.align = Some(HorizontalAlign::Center),
"justified" => style.align = Some(HorizontalAlign::Justified),
"end" => style.align = Some(HorizontalAlign::End),
_ => {
ctx.print_invalid_attrib(tag_name, key, value);
}
},
"weight" => match value {
"light" => style.weight = Some(FontWeight::Light),
"normal" => style.weight = Some(FontWeight::Normal),
"bold" => style.weight = Some(FontWeight::Bold),
_ => {
ctx.print_invalid_attrib(tag_name, key, value);
}
},
"size" => {
if let Ok(size) = value.parse::<f32>() {
style.size = Some(size);
} else {
ctx.print_invalid_attrib(tag_name, key, value);
}
}
"shadow" => {
let mut color = WguiColor::default();
if parse_color(ctx, tag_name, key, value, &mut color) {
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 {
ctx.print_invalid_attrib(tag_name, key, value);
}
}
"shadow_y" => {
if let Ok(y) = value.parse::<f32>() {
style.shadow.get_or_insert_default().y = y;
} else {
ctx.print_invalid_attrib(tag_name, key, value);
}
}
"wrap" => {
if let Ok(y) = value.parse::<i32>() {
style.wrap = y == 1;
} else {
ctx.print_invalid_attrib(tag_name, key, value);
}
}
_ => {}
}
"align" => match value {
"left" => style.align = Some(HorizontalAlign::Left),
"right" => style.align = Some(HorizontalAlign::Right),
"center" => style.align = Some(HorizontalAlign::Center),
"justified" => style.align = Some(HorizontalAlign::Justified),
"end" => style.align = Some(HorizontalAlign::End),
_ => {
ctx.print_invalid_attrib(tag_name, key, value);
}
},
"weight" => match value {
"light" => style.weight = Some(FontWeight::Light),
"normal" => style.weight = Some(FontWeight::Normal),
"bold" => style.weight = Some(FontWeight::Bold),
_ => {
ctx.print_invalid_attrib(tag_name, key, value);
}
},
"size" => {
if let Ok(size) = value.parse::<f32>() {
style.size = Some(size);
} else {
ctx.print_invalid_attrib(tag_name, key, value);
}
}
"shadow" => {
if let Some(color) = drawing::Color::from_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 {
ctx.print_invalid_attrib(tag_name, key, value);
}
}
"shadow_y" => {
if let Ok(y) = value.parse::<f32>() {
style.shadow.get_or_insert_default().y = y;
} else {
ctx.print_invalid_attrib(tag_name, key, value);
}
}
"wrap" => {
if let Ok(y) = value.parse::<i32>() {
style.wrap = y == 1;
} else {
ctx.print_invalid_attrib(tag_name, key, value);
}
}
_ => {}
}
}

View File

@ -19,7 +19,7 @@ pub fn parse_widget_label<'a>(
let mut params = WidgetLabelParams::default();
let style = parse_style(ctx, attribs, tag_name);
params.style = parse_text_style(ctx, attribs, tag_name);
params.style = parse_text_style(ctx, attribs, tag_name, "");
for pair in attribs {
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());

View File

@ -12,7 +12,7 @@ use glam::Mat4;
use parking_lot::Mutex;
use crate::{
color::{WguiColor, WguiColorPalette},
color::{WguiColor, WguiColorName, WguiColorPalette},
drawing::{self},
};
@ -44,6 +44,23 @@ impl Default for TextShadow {
}
}
#[derive(Debug, Clone)]
pub struct WguiTextShadow {
pub y: f32,
pub x: f32,
pub color: WguiColor,
}
impl WguiTextShadow {
pub fn to_text_shadow(&self, palette: &WguiColorPalette) -> TextShadow {
TextShadow {
x: self.x,
y: self.y,
color: self.color.resolve(palette),
}
}
}
#[derive(Debug, Default, Clone)]
pub struct TextStyle {
pub size: Option<f32>,
@ -53,7 +70,17 @@ pub struct TextStyle {
pub weight: Option<FontWeight>,
pub align: Option<HorizontalAlign>,
pub wrap: bool,
pub shadow: Option<TextShadow>,
pub shadow: Option<WguiTextShadow>,
}
impl Default for WguiTextShadow {
fn default() -> Self {
Self {
y: 1.5,
x: 1.5,
color: WguiColorName::Shadow.into(),
}
}
}
impl TextStyle {

View File

@ -140,7 +140,12 @@ impl WidgetObj for WidgetLabel {
transform: state.transform_stack.get().transform,
},
self.buffer.clone(),
self.params.style.shadow.clone(),
self
.params
.style
.shadow
.clone()
.map(|s| s.to_text_shadow(&state.globals.palette)),
));
}

View File

@ -262,12 +262,6 @@ pub struct GeneralConfig {
#[serde(default = "def_theme_path")]
pub theme_path: Arc<str>,
pub color_text: Option<String>,
pub color_accent: Option<String>,
pub color_danger: Option<String>,
pub color_faded: Option<String>,
pub color_background: Option<String>,
pub language: Option<locale::Language>, // auto-detected at runtime if unset
#[serde(default = "def_one")]