From 4e9b89330484246c9c569e181a16033d0d43838b Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Mon, 13 Jul 2026 13:48:01 +0900 Subject: [PATCH] keycaps theming --- wayvr/src/overlays/keyboard/builder.rs | 130 ++++++++++++++++++++----- wayvr/src/overlays/keyboard/mod.rs | 9 ++ wgui/src/layout.rs | 2 +- wgui/src/widget/label.rs | 8 +- wgui/src/widget/sprite.rs | 17 +++- 5 files changed, 135 insertions(+), 31 deletions(-) diff --git a/wayvr/src/overlays/keyboard/builder.rs b/wayvr/src/overlays/keyboard/builder.rs index dc99dd6d..4436933b 100644 --- a/wayvr/src/overlays/keyboard/builder.rs +++ b/wayvr/src/overlays/keyboard/builder.rs @@ -6,26 +6,29 @@ use crate::{ panel::{GuiPanel, NewGuiPanelParams, apply_custom_command}, timer::GuiTimer, }, - overlays::keyboard::alt_modifier_to_key, + overlays::keyboard::{ChildWidget, alt_modifier_to_key}, state::AppState, subsystem::hid::XkbKeymap, windowing::backend::OverlayEventData, }; use anyhow::Context; use glam::{FloatExt, Mat4, Vec2, vec2, vec3}; +use smallvec::{SmallVec, smallvec}; use wgui::{ animation::{Animation, AnimationEasing}, assets::AssetPath, - color::WguiColorName, - drawing::{self}, + color::{WguiColor, WguiColorName}, event::{self, CallbackMetadata, EventListenerKind}, - layout::LayoutUpdateParams, + layout::{LayoutUpdateParams, WidgetID}, log::LogErr, palette::WguiColorPalette, parser::{Fetchable, ParseDocumentParams, TemplateParams}, renderer_vk::util, taffy::{self, prelude::length}, - widget::{EventResult, div::WidgetDiv, rectangle::WidgetRectangle}, + widget::{ + EventResult, div::WidgetDiv, label::WidgetLabel, rectangle::WidgetRectangle, + sprite::WidgetSprite, + }, }; use super::{ @@ -149,6 +152,8 @@ pub(super) fn create_keyboard_panel( )?; if let Ok(widget_id) = panel.parser_state.get_widget_id(&my_id) { + let (labels, sprites) = find_children(&panel, widget_id); + let key_state = { let rect = panel .layout @@ -165,6 +170,8 @@ pub(super) fn create_keyboard_panel( cur_border_color: rect.params.border_color.into(), border: rect.params.border, drawn_state: false.into(), + labels, + sprites, }) }; @@ -322,6 +329,35 @@ pub(super) fn create_keyboard_panel( Ok(panel) } +fn find_children( + panel: &GuiPanel, + widget_id: WidgetID, +) -> (SmallVec<[ChildWidget; 3]>, SmallVec<[ChildWidget; 1]>) { + let mut labels = smallvec![]; + let mut sprites = smallvec![]; + + let mut children = vec![]; + panel + .layout + .collect_children_ids_recursive(widget_id, &mut children); + + for (child, _) in children { + if let Some(widget) = panel.layout.state.widgets.get_as::(child) { + sprites.push(ChildWidget { + id: child, + base_color: widget.get_color(), + }); + } else if let Some(widget) = panel.layout.state.widgets.get_as::(child) { + labels.push(ChildWidget { + id: child, + base_color: widget.get_color(), + }); + } + } + + (labels, sprites) +} + const BUTTON_HOVER_SCALE: f32 = 0.1; fn get_anim_transform(pos: f32, widget_size: Vec2, width_mult: f32) -> Mat4 { @@ -334,30 +370,22 @@ fn get_anim_transform(pos: f32, widget_size: Vec2, width_mult: f32) -> Mat4 { util::centered_matrix(widget_size, &Mat4::from_scale(scale)) } +const HOVER_COLOR: WguiColor = WguiColorName::Tertiary.to_wgui_color(); +const HOVER_BORDER_COLOR: WguiColor = WguiColorName::Tertiary.to_wgui_color().mult_rgb(0.5); +const HOVER_TEXT_COLOR: WguiColor = WguiColorName::OnTertiary.to_wgui_color(); +const PRESS_BORDER_COLOR: WguiColor = WguiColorName::Highlight.to_wgui_color(); + fn set_anim_color( palette: &WguiColorPalette, key_state: &KeyState, rect: &mut WidgetRectangle, pos: f32, ) { - // fade to accent color - rect.params.color = key_state - .color - .lerp(palette, &WguiColorName::Primary.into(), pos); + rect.params.color = key_state.color.lerp(palette, &HOVER_COLOR, pos); + rect.params.color2 = key_state.color2.lerp(palette, &HOVER_COLOR, pos); - // fade to accent color - 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 = cur_border_color.lerp( - palette, - &drawing::Color::new(1.0, 1.0, 1.0, 1.0).into(), - pos, - ); - + rect.params.border_color = cur_border_color.lerp(palette, &HOVER_BORDER_COLOR, pos); rect.params.border = key_state.border.lerp(key_state.border * 1.5, pos); } @@ -375,6 +403,33 @@ fn on_enter_anim( Box::new(move |common, data| { let rect = data.obj.get_as_mut::().unwrap(); set_anim_color(&common.globals().palette, &key_state, rect, data.pos); + + for child in key_state.labels.iter() { + let mut widget = common + .state + .widgets + .get_as::(child.id) + .unwrap(); + let color = + child + .base_color + .lerp(&common.globals().palette, &HOVER_TEXT_COLOR, data.pos); + widget.set_color(common, color, true); + } + + for child in key_state.sprites.iter() { + let mut widget = common + .state + .widgets + .get_as::(child.id) + .unwrap(); + let color = + child + .base_color + .lerp(&common.globals().palette, &HOVER_TEXT_COLOR, data.pos); + widget.set_color(common, color); + } + data.data.transform = get_anim_transform(data.pos, data.widget_boundary.size, width_mult); common.alterables.mark_redraw(); @@ -396,6 +451,35 @@ fn on_leave_anim( Box::new(move |common, data| { let rect = data.obj.get_as_mut::().unwrap(); set_anim_color(&common.globals().palette, &key_state, rect, 1.0 - data.pos); + + for child in key_state.labels.iter() { + let color = child.base_color.lerp( + &common.globals().palette, + &HOVER_TEXT_COLOR, + 1.0 - data.pos, + ); + let mut widget = common + .state + .widgets + .get_as::(child.id) + .unwrap(); + widget.set_color(common, color, true); + } + + for child in key_state.sprites.iter() { + let color = child.base_color.lerp( + &common.globals().palette, + &HOVER_TEXT_COLOR, + 1.0 - data.pos, + ); + let mut widget = common + .state + .widgets + .get_as::(child.id) + .unwrap(); + widget.set_color(common, color); + } + data.data.transform = get_anim_transform(1.0 - data.pos, data.widget_boundary.size, width_mult); common.alterables.mark_redraw(); @@ -412,9 +496,7 @@ fn on_press_anim( return; } let rect = data.obj.get_as_mut::().unwrap(); - key_state - .cur_border_color - .set(drawing::Color::new(1.0, 1.0, 1.0, 1.0).into()); + key_state.cur_border_color.set(PRESS_BORDER_COLOR); rect.params.border_color = key_state.cur_border_color.get(); common.alterables.mark_redraw(); key_state.drawn_state.set(true); diff --git a/wayvr/src/overlays/keyboard/mod.rs b/wayvr/src/overlays/keyboard/mod.rs index 44f9601e..709c1c85 100644 --- a/wayvr/src/overlays/keyboard/mod.rs +++ b/wayvr/src/overlays/keyboard/mod.rs @@ -30,9 +30,11 @@ use anyhow::Context; use glam::{Affine3A, Quat, Vec3, vec3}; use regex::Regex; use slotmap::{SlotMap, new_key_type}; +use smallvec::SmallVec; use wgui::{ color::WguiColor, event::{InternalStateChangeEvent, MouseButtonEvent, MouseButtonIndex}, + layout::WidgetID, }; use wlx_common::windowing::{OverlayWindowState, Positioning}; use wlx_common::{ @@ -355,6 +357,11 @@ fn play_key_click(app: &mut AppState) { .play_sample(&mut app.audio_system, "key_click"); } +struct ChildWidget { + id: WidgetID, + base_color: WguiColor, +} + struct KeyState { button_state: KeyButtonData, color: WguiColor, @@ -363,6 +370,8 @@ struct KeyState { cur_border_color: Cell, border: f32, drawn_state: Cell, + labels: SmallVec<[ChildWidget; 3]>, + sprites: SmallVec<[ChildWidget; 1]>, } #[derive(Debug)] diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index d4646952..afab62a5 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -282,7 +282,7 @@ impl Layout { }) } - pub(crate) fn collect_children_ids_recursive(&self, widget_id: WidgetID, out: &mut Vec<(WidgetID, taffy::NodeId)>) { + pub fn collect_children_ids_recursive(&self, widget_id: WidgetID, out: &mut Vec<(WidgetID, taffy::NodeId)>) { let Some(node_id) = self.state.nodes.get(widget_id) else { return; }; diff --git a/wgui/src/widget/label.rs b/wgui/src/widget/label.rs index 068d9a8d..3c3e1561 100644 --- a/wgui/src/widget/label.rs +++ b/wgui/src/widget/label.rs @@ -25,6 +25,8 @@ pub struct WidgetLabelParams { pub parent_color: ParentColor, } +const DEFAULT_COLOR: WguiColor = WguiColorName::OnBackground.to_wgui_color(); + pub struct WidgetLabel { id: WidgetID, @@ -39,7 +41,7 @@ impl WidgetLabel { pub fn create_ex(globals: &mut Globals, mut params: WidgetLabelParams) -> WidgetState { if params.style.color.is_none() { - params.style.color = Some(WguiColorName::OnPrimary.into()); + params.style.color = Some(DEFAULT_COLOR); } let metrics = Metrics::from(¶ms.style); @@ -127,6 +129,10 @@ impl WidgetLabel { } } + pub fn get_color(&self) -> WguiColor { + self.params.style.color.unwrap() // create_ex populates this + } + pub fn parent_color(&self) -> ParentColor { self.params.parent_color } diff --git a/wgui/src/widget/sprite.rs b/wgui/src/widget/sprite.rs index 1596df54..0c3530ce 100644 --- a/wgui/src/widget/sprite.rs +++ b/wgui/src/widget/sprite.rs @@ -25,6 +25,8 @@ pub struct WidgetSpriteParams { pub parent_color: ParentColor, } +const DEFAULT_COLOR: WguiColor = WguiColor::Raw(drawing::Color::from_hex("#ffffff").unwrap()); + #[derive(Debug, Default)] pub struct WidgetSprite { params: WidgetSpriteParams, @@ -47,8 +49,8 @@ impl WidgetSprite { common.mark_widget_dirty(self.id); } - pub const fn get_color(&self) -> Option { - self.params.color + pub fn get_color(&self) -> WguiColor { + self.params.color.unwrap_or(DEFAULT_COLOR) } pub fn set_content(&mut self, alterables: &mut EventAlterables, content: Option) { @@ -80,9 +82,14 @@ impl WidgetObj for WidgetSprite { top: 0.0, width: boundary.size.x, height: boundary.size.y, - color: Some(self.params.color.map_or(cosmic_text::Color::rgb(255, 255, 255), |c| { - c.resolve(&state.globals.palette).into() - })), + color: Some( + self + .params + .color + .unwrap_or(DEFAULT_COLOR) + .resolve(&state.globals.palette) + .into(), + ), snap_to_physical_pixel: true, };