diff --git a/Cargo.lock b/Cargo.lock index aa3d800..01f674b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6801,6 +6801,7 @@ dependencies = [ "serde", "smol", "wayvr-ipc", + "wgui", "xdg 3.0.0", ] diff --git a/dash-frontend/assets/sound/app_start.mp3 b/dash-frontend/assets/sound/app_start.mp3 new file mode 100644 index 0000000..155f110 Binary files /dev/null and b/dash-frontend/assets/sound/app_start.mp3 differ diff --git a/dash-frontend/assets/sound/startup.mp3 b/dash-frontend/assets/sound/startup.mp3 new file mode 100644 index 0000000..2d121ea Binary files /dev/null and b/dash-frontend/assets/sound/startup.mp3 differ diff --git a/dash-frontend/src/frontend.rs b/dash-frontend/src/frontend.rs index 6e4e555..f99ac0d 100644 --- a/dash-frontend/src/frontend.rs +++ b/dash-frontend/src/frontend.rs @@ -8,19 +8,20 @@ use wgui::{ font_config::WguiFontConfig, globals::WguiGlobals, i18n::Translation, - layout::{Layout, LayoutParams, WidgetID}, + layout::{Layout, LayoutParams, LayoutUpdateParams, LayoutUpdateResult, WidgetID}, parser::{Fetchable, ParseDocumentParams, ParserState}, + sound::WguiSoundType, task::Tasks, widget::{label::WidgetLabel, rectangle::WidgetRectangle}, windowing::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra, WguiWindowPlacement}, }; -use wlx_common::{dash_interface::BoxDashInterface, timestep::Timestep}; +use wlx_common::{audio, dash_interface::BoxDashInterface, timestep::Timestep}; use crate::{ assets, settings, tab::{ - apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses, settings::TabSettings, - Tab, TabType, + Tab, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses, + settings::TabSettings, }, util::{ desktop_finder::DesktopFinder, @@ -68,6 +69,13 @@ pub struct Frontend { pub(crate) desktop_finder: DesktopFinder, } +pub struct FrontendUpdateParams<'a, T> { + pub data: &'a mut T, + pub width: f32, + pub height: f32, + pub timestep_alpha: f32, +} + pub struct InitParams { pub settings: Box, pub interface: BoxDashInterface, @@ -164,15 +172,27 @@ impl Frontend { Ok(frontend) } - pub fn update(&mut self, data: &mut T, width: f32, height: f32, timestep_alpha: f32) -> anyhow::Result<()> { + pub fn play_startup_sound( + &mut self, + audio_system: &mut audio::AudioSystem, + audio_sample_player: &mut audio::SamplePlayer, + ) -> anyhow::Result<()> { + // play startup sound + let mut assets = self.globals.assets_builtin(); + audio_sample_player.register_mp3_sample_from_assets("dash_startup", assets.as_mut(), "sound/startup.mp3")?; + audio_sample_player.play_sample(audio_system, "dash_startup"); + Ok(()) + } + + pub fn update(&mut self, params: FrontendUpdateParams) -> anyhow::Result { let mut tasks = self.tasks.drain(); while let Some(task) = tasks.pop_front() { - self.process_task(data, task)?; + self.process_task(params.data, task)?; } if let Some(mut tab) = self.current_tab.take() { - tab.update(self, data)?; + tab.update(self, params.data)?; self.current_tab = Some(tab); } @@ -180,13 +200,14 @@ impl Frontend { // process async runtime tasks while self.executor.try_tick() {} - self.tick(width, height, timestep_alpha)?; + let res = self.tick(params)?; + self.ticks += 1; - Ok(()) + Ok(res) } - fn tick(&mut self, width: f32, height: f32, timestep_alpha: f32) -> anyhow::Result<()> { + fn tick(&mut self, params: FrontendUpdateParams) -> anyhow::Result { // fixme: timer events instead of this thing if self.ticks.is_multiple_of(1000) { self.update_time()?; @@ -197,11 +218,12 @@ impl Frontend { while self.timestep.on_tick() { self.toast_manager.tick(&self.globals, &mut self.layout)?; } - - self.layout.update(Vec2::new(width, height), timestep_alpha)?; } - Ok(()) + self.layout.update(&mut LayoutUpdateParams { + size: Vec2::new(params.width, params.height), + timestep_alpha: params.timestep_alpha, + }) } fn update_time(&mut self) -> anyhow::Result<()> { diff --git a/dash-frontend/src/util/pactl_wrapper.rs b/dash-frontend/src/util/pactl_wrapper.rs index 6fe9f2c..e1d5495 100644 --- a/dash-frontend/src/util/pactl_wrapper.rs +++ b/dash-frontend/src/util/pactl_wrapper.rs @@ -68,7 +68,7 @@ pub struct CardProperties { pub device_name: String, // alsa_card.pci-0000_0c_00.4 #[serde(rename = "device.nick")] - pub device_nick: String, // HD-Audio Generic + pub device_nick: Option, // HD-Audio Generic } #[derive(Clone, Serialize, Deserialize, Debug)] @@ -296,7 +296,12 @@ pub fn list_cards() -> anyhow::Result> { let mut cards: Vec = serde_json::from_str(json_str)?; // exclude card which has "Loopback" in name - cards.retain(|card| card.properties.device_nick != "Loopback"); + cards.retain(|card| { + if let Some(nick) = &card.properties.device_nick { + return nick != "Loopback"; + }; + true + }); Ok(cards) } diff --git a/uidev/assets/sound/wgui_button_press.mp3 b/uidev/assets/sound/wgui_button_press.mp3 new file mode 120000 index 0000000..2425ade --- /dev/null +++ b/uidev/assets/sound/wgui_button_press.mp3 @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/sound/wgui_button_press.mp3 \ No newline at end of file diff --git a/uidev/assets/sound/wgui_button_release.mp3 b/uidev/assets/sound/wgui_button_release.mp3 new file mode 120000 index 0000000..7511fdc --- /dev/null +++ b/uidev/assets/sound/wgui_button_release.mp3 @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/sound/wgui_button_release.mp3 \ No newline at end of file diff --git a/uidev/assets/sound/wgui_mouse_enter.mp3 b/uidev/assets/sound/wgui_mouse_enter.mp3 new file mode 120000 index 0000000..6971fa1 --- /dev/null +++ b/uidev/assets/sound/wgui_mouse_enter.mp3 @@ -0,0 +1 @@ +../../../wlx-overlay-s/src/assets/sound/wgui_mouse_enter.mp3 \ No newline at end of file diff --git a/uidev/src/main.rs b/uidev/src/main.rs index ba3b1a5..489031b 100644 --- a/uidev/src/main.rs +++ b/uidev/src/main.rs @@ -18,6 +18,7 @@ use vulkano::{ sync::GpuFuture, }; use wgui::{ + assets::AssetProvider, event::{MouseButtonIndex, MouseDownEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent}, gfx::{WGfx, cmd::WGfxClearMode}, renderer_vk::{self}, @@ -27,7 +28,7 @@ use winit::{ event_loop::ControlFlow, keyboard::{KeyCode, PhysicalKey}, }; -use wlx_common::timestep::Timestep; +use wlx_common::{audio, timestep::Timestep}; use crate::{ rate_limiter::RateLimiter, @@ -59,12 +60,15 @@ fn init_logging() { .init(); } -fn load_testbed() -> anyhow::Result> { +fn load_testbed(audio_sample_player: &mut audio::SamplePlayer) -> anyhow::Result> { + let mut assets = Box::new(assets::Asset {}); + audio_sample_player.register_wgui_samples(assets.as_mut())?; + let name = std::env::var("TESTBED").unwrap_or_default(); Ok(match name.as_str() { "dashboard" => Box::new(TestbedDashboard::new()?), - "" => Box::new(TestbedGeneric::new()?), - _ => Box::new(TestbedAny::new(&name)?), + "" => Box::new(TestbedGeneric::new(assets)?), + _ => Box::new(TestbedAny::new(assets, &name)?), }) } @@ -98,7 +102,9 @@ fn main() -> Result<(), Box> { let mut scale = window.scale_factor() as f32; - let mut testbed = load_testbed()?; + let mut audio_system = audio::AudioSystem::new(); + let mut audio_sample_player = audio::SamplePlayer::new(); + let mut testbed = load_testbed(&mut audio_sample_player)?; let mut mouse = Vec2::ZERO; @@ -291,6 +297,8 @@ fn main() -> Result<(), Box> { width: (swapchain_size[0] as f32 / scale) as _, height: (swapchain_size[1] as f32 / scale) as _, timestep_alpha: timestep.alpha, + audio_system: &mut audio_system, + audio_sample_player: &mut audio_sample_player, }) .unwrap(); diff --git a/uidev/src/testbed/mod.rs b/uidev/src/testbed/mod.rs index 2bde392..e0cd548 100644 --- a/uidev/src/testbed/mod.rs +++ b/uidev/src/testbed/mod.rs @@ -1,13 +1,24 @@ -use wgui::layout::Layout; +use wgui::layout::{Layout, LayoutUpdateResult}; +use wlx_common::audio; pub mod testbed_any; pub mod testbed_dashboard; pub mod testbed_generic; -pub struct TestbedUpdateParams { +pub struct TestbedUpdateParams<'a> { pub width: f32, pub height: f32, pub timestep_alpha: f32, + pub audio_system: &'a mut audio::AudioSystem, + pub audio_sample_player: &'a mut audio::SamplePlayer, +} + +impl<'a> TestbedUpdateParams<'a> { + pub fn process_layout_result(&mut self, res: LayoutUpdateResult) { + self + .audio_sample_player + .play_wgui_samples(self.audio_system, res.sounds_to_play); + } } pub trait Testbed { diff --git a/uidev/src/testbed/testbed_any.rs b/uidev/src/testbed/testbed_any.rs index 5380762..ab182ea 100644 --- a/uidev/src/testbed/testbed_any.rs +++ b/uidev/src/testbed/testbed_any.rs @@ -9,7 +9,7 @@ use wgui::{ assets::AssetPath, font_config::WguiFontConfig, globals::WguiGlobals, - layout::{Layout, LayoutParams}, + layout::{Layout, LayoutParams, LayoutUpdateParams}, parser::{ParseDocumentParams, ParserState}, }; @@ -21,7 +21,7 @@ pub struct TestbedAny { } impl TestbedAny { - pub fn new(name: &str) -> anyhow::Result { + pub fn new(assets: Box, name: &str) -> anyhow::Result { let path = if name.ends_with(".xml") { AssetPath::FileOrBuiltIn(name) } else { @@ -29,7 +29,7 @@ impl TestbedAny { }; let globals = WguiGlobals::new( - Box::new(assets::Asset {}), + assets, wgui::globals::Defaults::default(), &WguiFontConfig::default(), PathBuf::new(), // cwd @@ -48,11 +48,12 @@ impl TestbedAny { } impl Testbed for TestbedAny { - fn update(&mut self, params: TestbedUpdateParams) -> anyhow::Result<()> { - self.layout.update( - Vec2::new(params.width, params.height), - params.timestep_alpha, - )?; + fn update(&mut self, mut params: TestbedUpdateParams) -> anyhow::Result<()> { + let res = self.layout.update(&mut LayoutUpdateParams { + size: Vec2::new(params.width, params.height), + timestep_alpha: params.timestep_alpha, + })?; + params.process_layout_result(res); Ok(()) } diff --git a/uidev/src/testbed/testbed_dashboard.rs b/uidev/src/testbed/testbed_dashboard.rs index 76e29f6..2a67350 100644 --- a/uidev/src/testbed/testbed_dashboard.rs +++ b/uidev/src/testbed/testbed_dashboard.rs @@ -1,6 +1,6 @@ use crate::testbed::{Testbed, TestbedUpdateParams}; use dash_frontend::{ - frontend, + frontend::{self, FrontendUpdateParams}, settings::{self, SettingsIO}, }; use wgui::layout::Layout; @@ -70,13 +70,15 @@ impl TestbedDashboard { } impl Testbed for TestbedDashboard { - fn update(&mut self, params: TestbedUpdateParams) -> anyhow::Result<()> { - self.frontend.update( - &mut (), /* nothing */ - params.width, - params.height, - params.timestep_alpha, - ) + fn update(&mut self, mut params: TestbedUpdateParams) -> anyhow::Result<()> { + let res = self.frontend.update(FrontendUpdateParams { + data: &mut (), /* nothing */ + width: params.width, + height: params.height, + timestep_alpha: params.timestep_alpha, + })?; + params.process_layout_result(res); + Ok(()) } fn layout(&mut self) -> &mut Layout { diff --git a/uidev/src/testbed/testbed_generic.rs b/uidev/src/testbed/testbed_generic.rs index b0f7116..bfa5ee2 100644 --- a/uidev/src/testbed/testbed_generic.rs +++ b/uidev/src/testbed/testbed_generic.rs @@ -17,7 +17,7 @@ use wgui::{ font_config::WguiFontConfig, globals::WguiGlobals, i18n::Translation, - layout::{Layout, LayoutParams, Widget}, + layout::{Layout, LayoutParams, LayoutUpdateParams, Widget}, parser::{Fetchable, ParseDocumentExtra, ParseDocumentParams, ParserState}, taffy, task::Tasks, @@ -73,11 +73,11 @@ fn handle_button_click(button: Rc, label: Widget, text: &'stati } impl TestbedGeneric { - pub fn new() -> anyhow::Result { + pub fn new(assets: Box) -> anyhow::Result { const XML_PATH: AssetPath = AssetPath::BuiltIn("gui/various_widgets.xml"); let globals = WguiGlobals::new( - Box::new(assets::Asset {}), + assets, wgui::globals::Defaults::default(), &WguiFontConfig::default(), PathBuf::new(), // cwd @@ -223,10 +223,12 @@ impl Testbed for TestbedGeneric { let data = self.data.clone(); let mut data = data.borrow_mut(); - self.layout.update( - Vec2::new(params.width, params.height), - params.timestep_alpha, - )?; + let res = self.layout.update(&mut LayoutUpdateParams { + size: Vec2::new(params.width, params.height), + timestep_alpha: params.timestep_alpha, + })?; + + params.process_layout_result(res); for task in self.tasks.drain() { self.process_task(&task, &mut params, &mut data)?; diff --git a/wgui/src/components/button.rs b/wgui/src/components/button.rs index 43df154..772a3b4 100644 --- a/wgui/src/components/button.rs +++ b/wgui/src/components/button.rs @@ -10,6 +10,7 @@ use crate::{ text::{FontWeight, TextStyle, custom_glyph::CustomGlyphData}, util::centered_matrix, }, + sound::WguiSoundType, widget::{ self, ConstructEssentials, EventResult, WidgetData, label::{WidgetLabel, WidgetLabelParams}, @@ -270,6 +271,7 @@ fn register_event_mouse_enter( listeners.register( EventListenerKind::MouseEnter, Box::new(move |common, event_data, (), ()| { + common.alterables.play_sound(WguiSoundType::ButtonMouseEnter); common.alterables.trigger_haptics(); common.alterables.mark_redraw(); common @@ -332,6 +334,7 @@ fn register_event_mouse_press(state: Rc>, listeners: &mut EventLi ); common.alterables.trigger_haptics(); + common.alterables.play_sound(WguiSoundType::ButtonPress); common.alterables.mark_redraw(); if state.hovered { @@ -362,6 +365,7 @@ fn register_event_mouse_release( } common.alterables.trigger_haptics(); + common.alterables.play_sound(WguiSoundType::ButtonRelease); common.alterables.mark_redraw(); if state.down { diff --git a/wgui/src/event.rs b/wgui/src/event.rs index 83d8725..431b35b 100644 --- a/wgui/src/event.rs +++ b/wgui/src/event.rs @@ -11,6 +11,7 @@ use crate::{ animation::{self, Animation}, i18n::I18n, layout::{LayoutState, LayoutTask, WidgetID}, + sound::WguiSoundType, stack::{ScissorStack, Transform, TransformStack}, widget::{EventResult, WidgetData, WidgetObj}, }; @@ -140,6 +141,10 @@ impl EventAlterables { pub fn animate(&mut self, animation: Animation) { self.animations.push(animation); } + + pub fn play_sound(&mut self, sound_type: WguiSoundType) { + self.tasks.push(LayoutTask::PlaySound(sound_type)); + } } pub struct CallbackDataCommon<'a> { diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index 6e01962..5d2643c 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -1,6 +1,6 @@ use std::{ cell::{RefCell, RefMut}, - collections::{HashMap, HashSet, VecDeque}, + collections::{HashMap, HashSet}, io::Write, rc::{Rc, Weak}, }; @@ -11,6 +11,8 @@ use crate::{ drawing::{self, ANSI_BOLD_CODE, ANSI_RESET_CODE, Boundary, push_scissor_stack, push_transform_stack}, event::{self, CallbackDataCommon, EventAlterables}, globals::WguiGlobals, + sound::WguiSoundType, + task::Tasks, widget::{self, EventParams, EventResult, WidgetObj, WidgetState, WidgetStateFlags, div::WidgetDiv}, }; @@ -119,25 +121,24 @@ pub struct ModifyLayoutStateData<'a> { pub layout: &'a mut Layout, } +pub struct LayoutUpdateParams { + pub size: Vec2, // logical resolution + pub timestep_alpha: f32, +} + +pub struct LayoutUpdateResult { + pub sounds_to_play: Vec, +} + pub type ModifyLayoutStateFunc = Box anyhow::Result<()>>; pub enum LayoutTask { RemoveWidget(WidgetID), ModifyLayoutState(ModifyLayoutStateFunc), + PlaySound(WguiSoundType), } -#[derive(Clone)] -pub struct LayoutTasks(pub Rc>>); - -impl LayoutTasks { - fn new() -> Self { - Self(Rc::new(RefCell::new(VecDeque::new()))) - } - - pub fn push(&self, task: LayoutTask) { - self.0.borrow_mut().push_back(task); - } -} +pub type LayoutTasks = Tasks; pub struct Layout { pub state: LayoutState, @@ -146,6 +147,7 @@ pub struct Layout { components_to_refresh_once: HashSet, registered_components_to_refresh: HashMap, + sounds_to_play_once: Vec, pub widgets_to_tick: Vec, @@ -266,15 +268,12 @@ impl Layout { } pub fn get_parent(&self, widget_id: WidgetID) -> Option<(WidgetID, taffy::NodeId)> { - let Some(node_id) = self.state.nodes.get(widget_id) else { - return None; - }; + let node_id = self.state.nodes.get(widget_id)?; self.state.tree.parent(*node_id).map(|parent_id| { - let parent_widget_id = self.state.tree.get_node_context(parent_id).unwrap(); - (*parent_widget_id, parent_id) - } - ) + let parent_widget_id = self.state.tree.get_node_context(parent_id).unwrap(); + (*parent_widget_id, parent_id) + }) } fn collect_children_ids_recursive(&self, widget_id: WidgetID, out: &mut Vec<(WidgetID, taffy::NodeId)>) { @@ -575,6 +574,7 @@ impl Layout { registered_components_to_refresh: HashMap::new(), widgets_to_tick: Vec::new(), tasks: LayoutTasks::new(), + sounds_to_play_once: Vec::new(), }) } @@ -659,12 +659,17 @@ impl Layout { Ok(()) } - pub fn update(&mut self, size: Vec2, timestep_alpha: f32) -> anyhow::Result<()> { + pub fn update(&mut self, params: &mut LayoutUpdateParams) -> anyhow::Result { let mut alterables = EventAlterables::default(); - self.animations.process(&self.state, &mut alterables, timestep_alpha); + self + .animations + .process(&self.state, &mut alterables, params.timestep_alpha); self.process_alterables(alterables)?; - self.try_recompute_layout(size)?; - Ok(()) + self.try_recompute_layout(params.size)?; + + Ok(LayoutUpdateResult { + sounds_to_play: std::mem::take(&mut self.sounds_to_play_once), + }) } pub fn tick(&mut self) -> anyhow::Result<()> { @@ -677,8 +682,7 @@ impl Layout { } pub fn process_tasks(&mut self) -> anyhow::Result<()> { - let tasks = self.tasks.clone(); - let mut tasks = tasks.0.borrow_mut(); + let mut tasks = self.tasks.drain(); while let Some(task) = tasks.pop_front() { match task { LayoutTask::RemoveWidget(widget_id) => { @@ -687,6 +691,11 @@ impl Layout { LayoutTask::ModifyLayoutState(callback) => { (*callback)(ModifyLayoutStateData { layout: self })?; } + LayoutTask::PlaySound(sound) => { + if !self.sounds_to_play_once.contains(&sound) { + self.sounds_to_play_once.push(sound); + } + } } } diff --git a/wgui/src/lib.rs b/wgui/src/lib.rs index 218b95c..24c12fb 100644 --- a/wgui/src/lib.rs +++ b/wgui/src/lib.rs @@ -34,6 +34,7 @@ pub mod i18n; pub mod layout; pub mod parser; pub mod renderer_vk; +pub mod sound; pub mod stack; pub mod task; pub mod widget; diff --git a/wgui/src/sound.rs b/wgui/src/sound.rs new file mode 100644 index 0000000..389265d --- /dev/null +++ b/wgui/src/sound.rs @@ -0,0 +1,6 @@ +#[derive(Clone, Eq, PartialEq)] +pub enum WguiSoundType { + ButtonMouseEnter, + ButtonPress, + ButtonRelease, +} diff --git a/wlx-common/Cargo.toml b/wlx-common/Cargo.toml index 61c78db..75bedef 100644 --- a/wlx-common/Cargo.toml +++ b/wlx-common/Cargo.toml @@ -16,6 +16,7 @@ serde = { workspace = true, features = ["rc"] } xdg.workspace = true chrono = "0.4.42" smol = "2.0.2" +wgui = { path = "../wgui/" } rodio = { version = "0.21.1", default-features = false, features = [ "playback", "mp3", diff --git a/wlx-common/src/audio.rs b/wlx-common/src/audio.rs index 50394c8..257e705 100644 --- a/wlx-common/src/audio.rs +++ b/wlx-common/src/audio.rs @@ -1,6 +1,10 @@ use std::{collections::HashMap, io::Cursor}; use rodio::Source; +use wgui::{ + assets::{self, AssetProvider}, + sound::WguiSoundType, +}; pub struct AudioSystem { audio_stream: Option, @@ -15,6 +19,14 @@ pub struct SamplePlayer { samples: HashMap, } +fn get_sample_name_from_wgui_sound_type(sound: WguiSoundType) -> &'static str { + match sound { + WguiSoundType::ButtonMouseEnter => "wgui_mouse_enter", + WguiSoundType::ButtonPress => "wgui_button_press", + WguiSoundType::ButtonRelease => "wgui_button_release", + } +} + impl SamplePlayer { pub fn new() -> Self { Self { @@ -23,9 +35,39 @@ impl SamplePlayer { } pub fn register_sample(&mut self, sample_name: &str, sample: AudioSample) { + log::debug!("registering audio sample \"{sample_name}\""); self.samples.insert(String::from(sample_name), sample); } + pub fn register_mp3_sample_from_assets( + &mut self, + sample_name: &str, + assets: &mut dyn AssetProvider, + path: &str, + ) -> anyhow::Result<()> { + // load only once + if self.samples.contains_key(sample_name) { + return Ok(()); + } + + let data = assets.load_from_path(path)?; + self.register_sample(sample_name, AudioSample::from_mp3(&data)?); + + Ok(()) + } + + pub fn register_wgui_samples(&mut self, assets: &mut dyn AssetProvider) -> anyhow::Result<()> { + let mut load = |sound: WguiSoundType| -> anyhow::Result<()> { + let sample_name = get_sample_name_from_wgui_sound_type(sound); + self.register_mp3_sample_from_assets(sample_name, assets, &format!("sound/{}.mp3", sample_name)) + }; + + load(WguiSoundType::ButtonPress)?; + load(WguiSoundType::ButtonRelease)?; + load(WguiSoundType::ButtonMouseEnter)?; + Ok(()) + } + pub fn play_sample(&mut self, system: &mut AudioSystem, sample_name: &str) { let Some(sample) = self.samples.get(sample_name) else { log::error!("failed to play sample by name {}", sample_name); @@ -34,6 +76,12 @@ impl SamplePlayer { system.play_sample(sample); } + + pub fn play_wgui_samples(&mut self, system: &mut AudioSystem, samples: Vec) { + for sample in samples { + self.play_sample(system, get_sample_name_from_wgui_sound_type(sample)); + } + } } impl Default for SamplePlayer { diff --git a/wlx-overlay-s/src/app_misc.rs b/wlx-overlay-s/src/app_misc.rs new file mode 100644 index 0000000..8d854e2 --- /dev/null +++ b/wlx-overlay-s/src/app_misc.rs @@ -0,0 +1,8 @@ +use wgui::layout::LayoutUpdateResult; + +use crate::state::AppState; + +pub fn process_layout_result(app: &mut AppState, res: LayoutUpdateResult) { + app.audio_sample_player + .play_wgui_samples(&mut app.audio_system, res.sounds_to_play); +} diff --git a/wlx-overlay-s/src/assets/sound/wgui_button_press.mp3 b/wlx-overlay-s/src/assets/sound/wgui_button_press.mp3 new file mode 100644 index 0000000..ad20dad Binary files /dev/null and b/wlx-overlay-s/src/assets/sound/wgui_button_press.mp3 differ diff --git a/wlx-overlay-s/src/assets/sound/wgui_button_release.mp3 b/wlx-overlay-s/src/assets/sound/wgui_button_release.mp3 new file mode 100644 index 0000000..b3f1b7f Binary files /dev/null and b/wlx-overlay-s/src/assets/sound/wgui_button_release.mp3 differ diff --git a/wlx-overlay-s/src/assets/sound/wgui_mouse_enter.mp3 b/wlx-overlay-s/src/assets/sound/wgui_mouse_enter.mp3 new file mode 100644 index 0000000..5ad3a82 Binary files /dev/null and b/wlx-overlay-s/src/assets/sound/wgui_mouse_enter.mp3 differ diff --git a/wlx-overlay-s/src/gui/panel/mod.rs b/wlx-overlay-s/src/gui/panel/mod.rs index cbc3a49..10c5b84 100644 --- a/wlx-overlay-s/src/gui/panel/mod.rs +++ b/wlx-overlay-s/src/gui/panel/mod.rs @@ -13,7 +13,7 @@ use wgui::{ MouseMotionEvent, MouseUpEvent, MouseWheelEvent, }, gfx::cmd::WGfxClearMode, - layout::{Layout, LayoutParams, WidgetID}, + layout::{Layout, LayoutParams, LayoutUpdateParams, WidgetID}, parser::{CustomAttribsInfoOwned, Fetchable, ParserState}, renderer_vk::context::Context as WguiContext, widget::{EventResult, label::WidgetLabel}, @@ -22,6 +22,7 @@ use wlx_common::overlays::{BackendAttrib, BackendAttribValue}; use wlx_common::timestep::Timestep; use crate::{ + app_misc, backend::input::{Haptics, HoverResult, PointerHit, PointerMode}, state::AppState, subsystem::hid::WheelDelta, @@ -211,8 +212,15 @@ impl GuiPanel { }) } - pub fn update_layout(&mut self) -> anyhow::Result<()> { - self.layout.update(self.max_size, 0.0) + pub fn update_layout(&mut self, app: &mut AppState) -> anyhow::Result<()> { + app_misc::process_layout_result( + app, + self.layout.update(&mut LayoutUpdateParams { + size: self.max_size / self.gui_scale, + timestep_alpha: self.timestep.alpha, + })?, + ); + Ok(()) } pub fn push_event(&mut self, app: &mut AppState, event: &WguiEvent) -> EventResult { @@ -236,9 +244,9 @@ impl GuiPanel { } impl OverlayBackend for GuiPanel { - fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> { + fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> { if self.layout.content_size.x * self.layout.content_size.y != 0.0 { - self.update_layout()?; + self.update_layout(app)?; self.interaction_transform = Some(ui_transform([ self.layout.content_size.x as _, self.layout.content_size.y as _, @@ -297,9 +305,7 @@ impl OverlayBackend for GuiPanel { fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> { self.context .update_viewport(&mut app.wgui_shared, rdr.extent, self.gui_scale)?; - self.layout - .update(self.max_size / self.gui_scale, self.timestep.alpha)?; - + self.update_layout(app)?; let globals = self.layout.state.globals.clone(); // sorry let mut globals = globals.get(); diff --git a/wlx-overlay-s/src/main.rs b/wlx-overlay-s/src/main.rs index c04fe0a..8b4e5e3 100644 --- a/wlx-overlay-s/src/main.rs +++ b/wlx-overlay-s/src/main.rs @@ -17,6 +17,7 @@ clippy::cargo_common_metadata, clippy::option_if_let_else )] +mod app_misc; mod backend; mod config; mod config_io; diff --git a/wlx-overlay-s/src/overlays/anchor.rs b/wlx-overlay-s/src/overlays/anchor.rs index a6e1307..529c168 100644 --- a/wlx-overlay-s/src/overlays/anchor.rs +++ b/wlx-overlay-s/src/overlays/anchor.rs @@ -16,7 +16,7 @@ pub static ANCHOR_NAME: LazyLock> = LazyLock::new(|| Arc::from("anchor" pub fn create_anchor(app: &mut AppState) -> anyhow::Result { let mut panel = GuiPanel::new_from_template(app, "gui/anchor.xml", (), Default::default())?; - panel.update_layout()?; + panel.update_layout(app)?; Ok(OverlayWindowConfig { name: ANCHOR_NAME.clone(), @@ -41,7 +41,7 @@ pub static GRAB_HELP_NAME: LazyLock> = LazyLock::new(|| Arc::from("grab pub fn create_grab_help(app: &mut AppState) -> anyhow::Result { let mut panel = GuiPanel::new_from_template(app, "gui/grab-help.xml", (), Default::default())?; - panel.update_layout()?; + panel.update_layout(app)?; let id_watch = panel.parser_state.data.get_widget_id("grabbing_watch")?; let id_static = panel.parser_state.data.get_widget_id("grabbing_static")?; diff --git a/wlx-overlay-s/src/overlays/custom.rs b/wlx-overlay-s/src/overlays/custom.rs index 933311b..862761c 100644 --- a/wlx-overlay-s/src/overlays/custom.rs +++ b/wlx-overlay-s/src/overlays/custom.rs @@ -42,7 +42,7 @@ pub fn create_custom(app: &mut AppState, name: Arc) -> Option anyhow::Result<()> { - self.inner.update( - app, - DASH_RES_VEC2.x / GUI_SCALE, - DASH_RES_VEC2.y / GUI_SCALE, + let res = self.inner.update(FrontendUpdateParams { + data: app, + width: DASH_RES_VEC2.x / GUI_SCALE, + height: DASH_RES_VEC2.y / GUI_SCALE, timestep_alpha, - ) + })?; + app_misc::process_layout_result(app, res); + Ok(()) } fn push_event(&mut self, event: &WguiEvent) -> EventResult { diff --git a/wlx-overlay-s/src/overlays/edit/mod.rs b/wlx-overlay-s/src/overlays/edit/mod.rs index f53d567..7aa8ebe 100644 --- a/wlx-overlay-s/src/overlays/edit/mod.rs +++ b/wlx-overlay-s/src/overlays/edit/mod.rs @@ -167,7 +167,7 @@ impl OverlayBackend for EditModeBackendWrapper { let gui_scale = (new_size.x / 750.0).min(new_size.y / 300.0); self.panel.gui_scale = (gui_scale * 4.0).round() / 4.0; - self.panel.update_layout()?; + self.panel.update_layout(app)?; } self.can_render_inner = true; diff --git a/wlx-overlay-s/src/overlays/keyboard/builder.rs b/wlx-overlay-s/src/overlays/keyboard/builder.rs index ffe1253..17ad37e 100644 --- a/wlx-overlay-s/src/overlays/keyboard/builder.rs +++ b/wlx-overlay-s/src/overlays/keyboard/builder.rs @@ -1,13 +1,13 @@ use std::{collections::HashMap, rc::Rc}; -use crate::{gui::panel::GuiPanel, state::AppState, subsystem::hid::XkbKeymap}; +use crate::{app_misc, gui::panel::GuiPanel, state::AppState, subsystem::hid::XkbKeymap}; use glam::{FloatExt, Mat4, Vec2, vec2, vec3}; use wgui::{ animation::{Animation, AnimationEasing}, assets::AssetPath, drawing::{self, Color}, event::{self, CallbackMetadata, EventListenerKind}, - layout::LayoutParams, + layout::{LayoutParams, LayoutUpdateParams}, parser::Fetchable, renderer_vk::util, taffy::{self, prelude::length}, @@ -264,7 +264,13 @@ pub(super) fn create_keyboard_panel( } } - panel.layout.update(vec2(2048., 2048.), 0.0)?; + app_misc::process_layout_result( + app, + panel.layout.update(&mut LayoutUpdateParams { + size: vec2(2048., 2048.), + timestep_alpha: 0.0, + })?, + ); panel.parser_state = gui_state_key; Ok(panel) diff --git a/wlx-overlay-s/src/overlays/toast.rs b/wlx-overlay-s/src/overlays/toast.rs index 6e4bb1a..7db4fb4 100644 --- a/wlx-overlay-s/src/overlays/toast.rs +++ b/wlx-overlay-s/src/overlays/toast.rs @@ -179,7 +179,10 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option { .inspect_err(|e| log::error!("Could not create toast: {e:?}")) .ok()?; - panel.update_layout().context("layout update failed").ok()?; + panel + .update_layout(app) + .context("layout update failed") + .ok()?; Some(OverlayWindowConfig { name: TOAST_NAME.clone(), diff --git a/wlx-overlay-s/src/overlays/watch.rs b/wlx-overlay-s/src/overlays/watch.rs index a1723f5..29985fb 100644 --- a/wlx-overlay-s/src/overlays/watch.rs +++ b/wlx-overlay-s/src/overlays/watch.rs @@ -551,7 +551,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result { align_to_hmd: false, }; - panel.update_layout()?; + panel.update_layout(app)?; Ok(OverlayWindowConfig { name: WATCH_NAME.into(), diff --git a/wlx-overlay-s/src/state.rs b/wlx-overlay-s/src/state.rs index 16efb0f..a8f62f8 100644 --- a/wlx-overlay-s/src/state.rs +++ b/wlx-overlay-s/src/state.rs @@ -109,6 +109,9 @@ impl AppState { audio::AudioSample::from_mp3(include_bytes!("res/toast.mp3"))?, ); + let mut assets = Box::new(gui::asset::GuiAsset {}); + audio_sample_player.register_wgui_samples(assets.as_mut())?; + let mut defaults = wgui::globals::Defaults::default(); { @@ -147,7 +150,7 @@ impl AppState { anchor: Affine3A::IDENTITY, anchor_grabbed: false, wgui_globals: WguiGlobals::new( - Box::new(gui::asset::GuiAsset {}), + assets, defaults, &WguiFontConfig::default(), get_config_file_path(&theme),