Audio settings pop-up

This commit is contained in:
Aleksander
2025-12-04 21:04:52 +01:00
parent f54c5decb6
commit 9767940923
22 changed files with 442 additions and 99 deletions

View File

@@ -130,8 +130,12 @@
<!-- Left bottom side -->
<div margin_left="8">
<sprite src="dashboard/volume.svg" width="24" height="24" margin="8" />
<sprite src="dashboard/recenter.svg" width="24" height="24" margin="8" />
<Button id="btn_audio" color="#FFFFFF00" border_color="#FFFFFF00" tooltip="AUDIO.VOLUME" tooltip_side="top">
<sprite src="dashboard/volume.svg" width="24" height="24" margin="8" />
</Button>
<Button id="btn_recenter" color="#FFFFFF00" border_color="#FFFFFF00" tooltip="ACTIONS.RECENTER_PLAYSPACE" tooltip_side="top">
<sprite src="dashboard/recenter.svg" width="24" height="24" margin="8" />
</Button>
</div>
<!-- Right bottom side -->

View File

@@ -0,0 +1,47 @@
<layout>
<include src="../t_group_box.xml" />
<!-- device_name, device_icon -->
<template name="AudioDevice">
<rectangle macro="group_box">
<div width="100%" align_items="center" justify_content="center" gap="8">
<sprite src="${device_icon}" width="16" height="16" />
<label translation="${device_name}" margin_right="8" size="12" weight="bold" />
</div>
<div width="100%" align_items="center">
<CheckBox />
<Button>
<sprite src="dashboard/volume.svg" width="20" height="20" margin="4" margin_left="8" margin_right="8" />
</Button>
<Slider flex_grow="1" height="16" min_value="0" max_value="150" margin_left="8" />
</div>
</rectangle>
</template>
<!-- src, translation -->
<template name="BottomButton">
<Button flex_grow="1">
<sprite src="${src}"
min_width="24" min_height="24" width="24" height="24" margin="4" margin_left="16" />
<label translation="${translation}" weight="bold" margin_right="16" />
</Button>
</template>
<elements>
<AudioDevice device_name="Analog Stereo Output" device_icon="dashboard/minijack.svg" />
<AudioDevice device_name="My spectacular display" device_icon="dashboard/displayport.svg" />
<AudioDevice device_name="Foobar device" device_icon="dashboard/minijack.svg" />
<!-- bottom buttons -->
<div flex_direction="row" gap="4">
<Button tooltip="AUDIO.AUTO_SWITCH_TO_VR_AUDIO" color="#00CCFF" tooltip_side="right">
<sprite src="dashboard/magic_wand.svg"
min_width="24" width="24" height="24" margin="4" />
</Button>
<BottomButton src="dashboard/volume.svg" translation="AUDIO.SPEAKERS" />
<BottomButton src="dashboard/microphone.svg" translation="AUDIO.MICROPHONES" />
<BottomButton src="dashboard/cpu.svg" translation="AUDIO.CARDS" />
</div>
</elements>
</layout>

View File

@@ -27,5 +27,16 @@
},
"RESTART_SOFTWARE": "Software neu starten"
},
"HELLO": "Hallo!"
"HELLO": "Hallo!",
"AUDIO": {
"VOLUME": "Lautstärke",
"SETTINGS": "Audioeinstellungen",
"AUTO_SWITCH_TO_VR_AUDIO": "Automatisch auf VR-Audio umschalten",
"SPEAKERS": "Lautsprecher",
"MICROPHONES": "Mikrofone",
"CARDS": "Karten"
},
"ACTIONS": {
"RECENTER_PLAYSPACE": "Playspace neu zentrieren"
}
}

View File

@@ -27,5 +27,16 @@
"SHOW_SKYBOX": "Show skybox",
"ENABLE_PASSTHROUGH": "Enable passthrough"
}
},
"AUDIO": {
"SETTINGS": "Audio settings",
"VOLUME": "Volume",
"AUTO_SWITCH_TO_VR_AUDIO": "Auto-switch to VR audio",
"SPEAKERS": "Speakers",
"MICROPHONES": "Microphones",
"CARDS": "Cards"
},
"ACTIONS": {
"RECENTER_PLAYSPACE": "Re-center playspace"
}
}

View File

@@ -27,5 +27,16 @@
},
"RESTART_SOFTWARE": "Reiniciar software"
},
"HELLO": "¡Hola!"
"HELLO": "¡Hola!",
"AUDIO": {
"VOLUME": "Volumen",
"SETTINGS": "Configuración de audio",
"AUTO_SWITCH_TO_VR_AUDIO": "Conmutar automáticamente al audio VR",
"SPEAKERS": "Altavoces",
"MICROPHONES": "Micrófonos",
"CARDS": "Tarjetas"
},
"ACTIONS": {
"RECENTER_PLAYSPACE": "Re-centrar espacio de juego"
}
}

View File

@@ -27,5 +27,16 @@
},
"RESTART_SOFTWARE": "ソフトウェアを再起動"
},
"HELLO": "こんにちは!"
"HELLO": "こんにちは!",
"AUDIO": {
"VOLUME": "音量",
"SETTINGS": "オーディオ設定",
"AUTO_SWITCH_TO_VR_AUDIO": "VRオーディオに自動切り替え",
"SPEAKERS": "スピーカー",
"MICROPHONES": "マイク",
"CARDS": "カード"
},
"ACTIONS": {
"RECENTER_PLAYSPACE": "プレイスペースを再中央"
}
}

View File

@@ -1,31 +1,42 @@
{
"HOME_SCREEN": "Ekran główny",
"MONADO_RUNTIME": "„Monado” środowisko uruchomieniowe",
"APPLICATIONS": "Aplikacje",
"GAMES": "Gry",
"SETTINGS": "Ustawienia",
"PROCESSES": "Procesy",
"HELLO_USER": "Witaj, {USER}!",
"GENERAL_SETTINGS": "Ustawienia ogólne",
"APPLICATION_LAUNCHER": "Uruchamiacz aplikacji",
"APP_SETTINGS": {
"HIDE_USERNAME": "Ukryj nazwę użytkownika",
"OPAQUE_BACKGROUND": "Nieprzezroczysty tło",
"RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Uruchom domyślnie w trybie XWayland",
"WLX_OVERLAY_S_SETTINGS": "Ustawienia wlx-overlay-s",
"HEADSET_SETTINGS": "Ustawienia głośności",
"BRIGHTNESS": "Jasność",
"WLX": {
"NOTIFICATIONS_ENABLED": "Powiadomienia",
"NOTIFICATIONS_SOUND_ENABLED": "Dźwięk powiadomień",
"KEYBOARD_SOUND_ENABLED": "Dźwięki klawiatury",
"BLOCK_GAME_INPUT": "Zablokuj sterowanie grą podczas używania Wlx",
"SPACE_DRAG_MULTIPLIER": "Mnożnik space-drag",
"SPACE_DRAG_ROTATION_ENABLED": "Włącz rotację w space-drag",
"SHOW_SKYBOX": "Pokaż skybox",
"ENABLE_PASSTHROUGH": "Włącz passthrough"
},
"RESTART_SOFTWARE": "Uruchom ponownie oprogramowanie"
},
"HELLO": "Witaj!"
}
"HOME_SCREEN": "Ekran główny",
"MONADO_RUNTIME": "„Monado” środowisko uruchomieniowe",
"APPLICATIONS": "Aplikacje",
"GAMES": "Gry",
"SETTINGS": "Ustawienia",
"PROCESSES": "Procesy",
"HELLO_USER": "Witaj, {USER}!",
"GENERAL_SETTINGS": "Ustawienia ogólne",
"APPLICATION_LAUNCHER": "Uruchamiacz aplikacji",
"APP_SETTINGS": {
"HIDE_USERNAME": "Ukryj nazwę użytkownika",
"OPAQUE_BACKGROUND": "Nieprzezroczyste tło",
"RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Uruchom domyślnie w trybie XWayland",
"WLX_OVERLAY_S_SETTINGS": "Ustawienia wlx-overlay-s",
"HEADSET_SETTINGS": "Ustawienia HMD",
"BRIGHTNESS": "Jasność",
"WLX": {
"NOTIFICATIONS_ENABLED": "Powiadomienia",
"NOTIFICATIONS_SOUND_ENABLED": "Dźwięk powiadomień",
"KEYBOARD_SOUND_ENABLED": "Dźwięki klawiatury",
"BLOCK_GAME_INPUT": "Zablokuj sterowanie grą podczas używania Wlx",
"SPACE_DRAG_MULTIPLIER": "Mnożnik space-drag",
"SPACE_DRAG_ROTATION_ENABLED": "Włącz rotację w space-drag",
"SHOW_SKYBOX": "Pokaż skybox",
"ENABLE_PASSTHROUGH": "Włącz passthrough"
},
"RESTART_SOFTWARE": "Uruchom ponownie oprogramowanie"
},
"HELLO": "Witaj!",
"AUDIO": {
"VOLUME": "Głośność",
"SETTINGS": "Ustawienia dźwięku",
"AUTO_SWITCH_TO_VR_AUDIO": "Automatyczne przełączanie na dźwięk VR",
"SPEAKERS": "Głośniki",
"MICROPHONES": "Mikrofony",
"CARDS": "Karty"
},
"ACTIONS": {
"RECENTER_PLAYSPACE": "Wycentruj przestrzeń"
}
}

View File

@@ -8,9 +8,10 @@ use wgui::{
font_config::WguiFontConfig,
globals::WguiGlobals,
i18n::Translation,
layout::{LayoutParams, RcLayout, WidgetID},
layout::{Layout, LayoutParams, RcLayout, WidgetID},
parser::{Fetchable, ParseDocumentParams, ParserState},
widget::{label::WidgetLabel, rectangle::WidgetRectangle},
windowing::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra, WguiWindowPlacement},
};
use crate::{
@@ -20,6 +21,7 @@ use crate::{
settings::TabSettings,
},
util::popup_manager::{MountPopupParams, PopupManager, PopupManagerParams},
views,
};
pub struct FrontendWidgets {
@@ -57,6 +59,8 @@ pub struct Frontend {
widgets: FrontendWidgets,
popup_manager: PopupManager,
window_audio_settings: WguiWindow,
}
pub struct InitParams {
@@ -65,12 +69,15 @@ pub struct InitParams {
pub type RcFrontend = Rc<RefCell<Frontend>>;
#[derive(Clone)]
pub enum FrontendTask {
SetTab(TabType),
RefreshClock,
RefreshBackground,
MountPopup(MountPopupParams),
RefreshPopupManager,
ShowAudioSettings,
RecenterPlayspace,
}
impl Frontend {
@@ -129,6 +136,7 @@ impl Frontend {
},
settings: params.settings,
popup_manager,
window_audio_settings: WguiWindow::default(),
};
// init some things first
@@ -251,6 +259,8 @@ impl Frontend {
FrontendTask::RefreshBackground => self.update_background()?,
FrontendTask::MountPopup(params) => self.mount_popup(params)?,
FrontendTask::RefreshPopupManager => self.refresh_popup_manager()?,
FrontendTask::ShowAudioSettings => self.action_show_audio_settings()?,
FrontendTask::RecenterPlayspace => self.action_recenter_playspace()?,
}
Ok(())
}
@@ -284,21 +294,112 @@ impl Frontend {
Ok(())
}
pub fn register_button_task(this_rc: RcFrontend, btn: &Rc<ComponentButton>, task: FrontendTask) {
btn.on_click({
Box::new(move |_common, _evt| {
this_rc.borrow_mut().tasks.push(task.clone());
Ok(())
})
});
}
fn register_widgets(rc_this: &RcFrontend) -> anyhow::Result<()> {
let this = rc_this.borrow_mut();
let btn_home = this.state.fetch_component_as::<ComponentButton>("btn_side_home")?;
let btn_apps = this.state.fetch_component_as::<ComponentButton>("btn_side_apps")?;
let btn_games = this.state.fetch_component_as::<ComponentButton>("btn_side_games")?;
let btn_monado = this.state.fetch_component_as::<ComponentButton>("btn_side_monado")?;
let btn_processes = this.state.fetch_component_as::<ComponentButton>("btn_side_processes")?;
let btn_settings = this.state.fetch_component_as::<ComponentButton>("btn_side_settings")?;
TabType::register_button(rc_this.clone(), &btn_home, TabType::Home);
TabType::register_button(rc_this.clone(), &btn_apps, TabType::Apps);
TabType::register_button(rc_this.clone(), &btn_games, TabType::Games);
TabType::register_button(rc_this.clone(), &btn_monado, TabType::Monado);
TabType::register_button(rc_this.clone(), &btn_processes, TabType::Processes);
TabType::register_button(rc_this.clone(), &btn_settings, TabType::Settings);
// ################################
// SIDE BUTTONS
// ################################
// "Home" side button
Frontend::register_button_task(
rc_this.clone(),
&this.state.fetch_component_as::<ComponentButton>("btn_side_home")?,
FrontendTask::SetTab(TabType::Home),
);
// "Apps" side button
Frontend::register_button_task(
rc_this.clone(),
&this.state.fetch_component_as::<ComponentButton>("btn_side_apps")?,
FrontendTask::SetTab(TabType::Apps),
);
// "Games" side button
Frontend::register_button_task(
rc_this.clone(),
&this.state.fetch_component_as::<ComponentButton>("btn_side_games")?,
FrontendTask::SetTab(TabType::Games),
);
// "Monado side button"
Frontend::register_button_task(
rc_this.clone(),
&this.state.fetch_component_as::<ComponentButton>("btn_side_monado")?,
FrontendTask::SetTab(TabType::Monado),
);
// "Processes" side button
Frontend::register_button_task(
rc_this.clone(),
&this.state.fetch_component_as::<ComponentButton>("btn_side_processes")?,
FrontendTask::SetTab(TabType::Processes),
);
// "Settings" side button
Frontend::register_button_task(
rc_this.clone(),
&this.state.fetch_component_as::<ComponentButton>("btn_side_settings")?,
FrontendTask::SetTab(TabType::Settings),
);
// ################################
// BOTTOM BAR BUTTONS
// ################################
// "Audio" bottom bar button
Frontend::register_button_task(
rc_this.clone(),
&this.state.fetch_component_as::<ComponentButton>("btn_audio")?,
FrontendTask::ShowAudioSettings,
);
// "Recenter playspace" bottom bar button
Frontend::register_button_task(
rc_this.clone(),
&this.state.fetch_component_as::<ComponentButton>("btn_recenter")?,
FrontendTask::RecenterPlayspace,
);
Ok(())
}
fn action_show_audio_settings(&mut self) -> anyhow::Result<()> {
let mut layout = self.layout.borrow_mut();
self.window_audio_settings.open(&mut WguiWindowParams {
globals: self.globals.clone(),
position: Vec2::new(64.0, 64.0),
layout: &mut layout,
title: Translation::from_translation_key("AUDIO.SETTINGS"),
extra: WguiWindowParamsExtra {
fixed_width: Some(400.0),
placement: WguiWindowPlacement::BottomLeft,
..Default::default()
},
})?;
let content = self.window_audio_settings.get_content();
views::audio_settings::View::new(views::audio_settings::Params {
globals: self.globals.clone(),
layout: &mut layout,
parent_id: content.id,
})?;
Ok(())
}
fn action_recenter_playspace(&mut self) -> anyhow::Result<()> {
log::info!("todo");
Ok(())
}
}

View File

@@ -64,7 +64,7 @@ fn on_app_click(
let state = state.clone();
let entry = entry.clone();
let globals = globals.clone();
Box::new(move |data| {
Rc::new(move |data| {
let view = app_launcher::View::new(app_launcher::Params {
entry: entry.clone(),
globals: globals.clone(),

View File

@@ -9,6 +9,7 @@ use wgui::{
};
use crate::{
frontend::{Frontend, FrontendTask},
settings,
tab::{Tab, TabParams, TabType},
various,
@@ -66,11 +67,15 @@ impl TabHome {
let btn_settings = state.fetch_component_as::<ComponentButton>("btn_settings")?;
let frontend = params.frontend;
TabType::register_button(frontend.clone(), &btn_apps, TabType::Apps);
TabType::register_button(frontend.clone(), &btn_games, TabType::Games);
TabType::register_button(frontend.clone(), &btn_monado, TabType::Monado);
TabType::register_button(frontend.clone(), &btn_processes, TabType::Processes);
TabType::register_button(frontend.clone(), &btn_settings, TabType::Settings);
Frontend::register_button_task(frontend.clone(), &btn_apps, FrontendTask::SetTab(TabType::Apps));
Frontend::register_button_task(frontend.clone(), &btn_games, FrontendTask::SetTab(TabType::Games));
Frontend::register_button_task(frontend.clone(), &btn_monado, FrontendTask::SetTab(TabType::Monado));
Frontend::register_button_task(
frontend.clone(),
&btn_processes,
FrontendTask::SetTab(TabType::Processes),
);
Frontend::register_button_task(frontend.clone(), &btn_settings, FrontendTask::SetTab(TabType::Settings));
Ok(Self { state })
}

View File

@@ -38,14 +38,3 @@ pub trait Tab {
#[allow(dead_code)]
fn get_type(&self) -> TabType;
}
impl TabType {
pub fn register_button(this_rc: RcFrontend, btn: &Rc<ComponentButton>, tab: TabType) {
btn.on_click({
Box::new(move |_common, _evt| {
this_rc.borrow_mut().tasks.push(FrontendTask::SetTab(tab));
Ok(())
})
});
}
}

View File

@@ -63,9 +63,10 @@ pub struct PopupContentFuncData<'a> {
pub id_content: WidgetID,
}
#[derive(Clone)]
pub struct MountPopupParams {
pub title: Translation,
pub on_content: Box<dyn Fn(PopupContentFuncData) -> anyhow::Result<()>>,
pub on_content: Rc<dyn Fn(PopupContentFuncData) -> anyhow::Result<()>>,
}
impl Drop for MountedPopup {

View File

@@ -0,0 +1,36 @@
use std::{collections::HashMap, rc::Rc};
use wgui::{
assets::AssetPath,
globals::WguiGlobals,
i18n::Translation,
layout::{Layout, WidgetID},
parser::{Fetchable, ParseDocumentParams, ParserState},
widget::label::WidgetLabel,
};
pub struct View {
#[allow(dead_code)]
pub state: ParserState,
//entry: DesktopEntry,
}
pub struct Params<'a> {
pub globals: WguiGlobals,
pub layout: &'a mut Layout,
pub parent_id: WidgetID,
}
impl View {
pub fn new(params: Params) -> anyhow::Result<Self> {
let doc_params = &ParseDocumentParams {
globals: params.globals.clone(),
path: AssetPath::BuiltIn("gui/view/audio_settings.xml"),
extra: Default::default(),
};
let state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?;
Ok(Self { state })
}
}

View File

@@ -1 +1,2 @@
pub mod app_launcher;
pub mod audio_settings;