keycaps theming

This commit is contained in:
galister 2026-07-13 13:48:01 +09:00
parent d311813899
commit 4e9b893304
5 changed files with 135 additions and 31 deletions

View File

@ -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<S>(
panel: &GuiPanel<S>,
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::<WidgetSprite>(child) {
sprites.push(ChildWidget {
id: child,
base_color: widget.get_color(),
});
} else if let Some(widget) = panel.layout.state.widgets.get_as::<WidgetLabel>(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::<WidgetRectangle>().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::<WidgetLabel>(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::<WidgetSprite>(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::<WidgetRectangle>().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::<WidgetLabel>(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::<WidgetSprite>(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::<WidgetRectangle>().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);

View File

@ -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<WguiColor>,
border: f32,
drawn_state: Cell<bool>,
labels: SmallVec<[ChildWidget; 3]>,
sprites: SmallVec<[ChildWidget; 1]>,
}
#[derive(Debug)]

View File

@ -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;
};

View File

@ -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(&params.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
}

View File

@ -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<WguiColor> {
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<CustomGlyphData>) {
@ -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,
};