diff --git a/dash-frontend/assets/gui/dashboard.xml b/dash-frontend/assets/gui/dashboard.xml index 59b624f..db10112 100644 --- a/dash-frontend/assets/gui/dashboard.xml +++ b/dash-frontend/assets/gui/dashboard.xml @@ -130,8 +130,12 @@
- - + +
diff --git a/dash-frontend/assets/gui/view/audio_settings.xml b/dash-frontend/assets/gui/view/audio_settings.xml new file mode 100644 index 0000000..f2e4c8d --- /dev/null +++ b/dash-frontend/assets/gui/view/audio_settings.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + +
+ + + + + +
+
+
\ No newline at end of file diff --git a/dash-frontend/assets/lang/de.json b/dash-frontend/assets/lang/de.json index ed946d6..adc0585 100644 --- a/dash-frontend/assets/lang/de.json +++ b/dash-frontend/assets/lang/de.json @@ -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" + } } \ No newline at end of file diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json index bca60ef..5fbeaab 100644 --- a/dash-frontend/assets/lang/en.json +++ b/dash-frontend/assets/lang/en.json @@ -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" } } diff --git a/dash-frontend/assets/lang/es.json b/dash-frontend/assets/lang/es.json index ebe3812..e6c3f41 100644 --- a/dash-frontend/assets/lang/es.json +++ b/dash-frontend/assets/lang/es.json @@ -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" + } } \ No newline at end of file diff --git a/dash-frontend/assets/lang/ja.json b/dash-frontend/assets/lang/ja.json index 1cf540c..4ce2bdf 100644 --- a/dash-frontend/assets/lang/ja.json +++ b/dash-frontend/assets/lang/ja.json @@ -27,5 +27,16 @@ }, "RESTART_SOFTWARE": "ソフトウェアを再起動" }, - "HELLO": "こんにちは!" + "HELLO": "こんにちは!", + "AUDIO": { + "VOLUME": "音量", + "SETTINGS": "オーディオ設定", + "AUTO_SWITCH_TO_VR_AUDIO": "VRオーディオに自動切り替え", + "SPEAKERS": "スピーカー", + "MICROPHONES": "マイク", + "CARDS": "カード" + }, + "ACTIONS": { + "RECENTER_PLAYSPACE": "プレイスペースを再中央" + } } \ No newline at end of file diff --git a/dash-frontend/assets/lang/pl.json b/dash-frontend/assets/lang/pl.json index 707c059..c7dcb81 100644 --- a/dash-frontend/assets/lang/pl.json +++ b/dash-frontend/assets/lang/pl.json @@ -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ń" + } +} \ No newline at end of file diff --git a/dash-frontend/src/frontend.rs b/dash-frontend/src/frontend.rs index a657782..f4894c5 100644 --- a/dash-frontend/src/frontend.rs +++ b/dash-frontend/src/frontend.rs @@ -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>; +#[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, 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::("btn_side_home")?; - let btn_apps = this.state.fetch_component_as::("btn_side_apps")?; - let btn_games = this.state.fetch_component_as::("btn_side_games")?; - let btn_monado = this.state.fetch_component_as::("btn_side_monado")?; - let btn_processes = this.state.fetch_component_as::("btn_side_processes")?; - let btn_settings = this.state.fetch_component_as::("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::("btn_side_home")?, + FrontendTask::SetTab(TabType::Home), + ); + + // "Apps" side button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_side_apps")?, + FrontendTask::SetTab(TabType::Apps), + ); + + // "Games" side button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_side_games")?, + FrontendTask::SetTab(TabType::Games), + ); + + // "Monado side button" + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_side_monado")?, + FrontendTask::SetTab(TabType::Monado), + ); + + // "Processes" side button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("btn_side_processes")?, + FrontendTask::SetTab(TabType::Processes), + ); + + // "Settings" side button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("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::("btn_audio")?, + FrontendTask::ShowAudioSettings, + ); + + // "Recenter playspace" bottom bar button + Frontend::register_button_task( + rc_this.clone(), + &this.state.fetch_component_as::("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(()) } } diff --git a/dash-frontend/src/tab/apps.rs b/dash-frontend/src/tab/apps.rs index a91b791..fc7c1c7 100644 --- a/dash-frontend/src/tab/apps.rs +++ b/dash-frontend/src/tab/apps.rs @@ -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(), diff --git a/dash-frontend/src/tab/home.rs b/dash-frontend/src/tab/home.rs index 2ea9df0..ffb4084 100644 --- a/dash-frontend/src/tab/home.rs +++ b/dash-frontend/src/tab/home.rs @@ -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::("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 }) } diff --git a/dash-frontend/src/tab/mod.rs b/dash-frontend/src/tab/mod.rs index 921314e..81991f3 100644 --- a/dash-frontend/src/tab/mod.rs +++ b/dash-frontend/src/tab/mod.rs @@ -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, tab: TabType) { - btn.on_click({ - Box::new(move |_common, _evt| { - this_rc.borrow_mut().tasks.push(FrontendTask::SetTab(tab)); - Ok(()) - }) - }); - } -} diff --git a/dash-frontend/src/util/popup_manager.rs b/dash-frontend/src/util/popup_manager.rs index df94617..95e711a 100644 --- a/dash-frontend/src/util/popup_manager.rs +++ b/dash-frontend/src/util/popup_manager.rs @@ -63,9 +63,10 @@ pub struct PopupContentFuncData<'a> { pub id_content: WidgetID, } +#[derive(Clone)] pub struct MountPopupParams { pub title: Translation, - pub on_content: Box anyhow::Result<()>>, + pub on_content: Rc anyhow::Result<()>>, } impl Drop for MountedPopup { diff --git a/dash-frontend/src/views/audio_settings.rs b/dash-frontend/src/views/audio_settings.rs new file mode 100644 index 0000000..a4f5239 --- /dev/null +++ b/dash-frontend/src/views/audio_settings.rs @@ -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 { + 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 }) + } +} diff --git a/dash-frontend/src/views/mod.rs b/dash-frontend/src/views/mod.rs index 5eb7e4e..8fe26e0 100644 --- a/dash-frontend/src/views/mod.rs +++ b/dash-frontend/src/views/mod.rs @@ -1 +1,2 @@ pub mod app_launcher; +pub mod audio_settings; diff --git a/scripts/translator/description.txt b/scripts/translator/description.txt index 1522ed6..1774ac5 100644 --- a/scripts/translator/description.txt +++ b/scripts/translator/description.txt @@ -2,14 +2,15 @@ You are a translator for the VR application which translates strings from Englis Info: This program is intended to be used as an utility to easily access your desktop display from within VR. Glossary: -wlx-overlay-s: The name of this software (also called WlxOverlay-S) -WayVR: A Wayland compositor intended to be used in VR -WayVR Dashboard: An application (and game) launcher which is displayed in front of the user -OpenVR: API made by Valve -OpenXR: API made by Khronos -OSC: OpenSoundControl -Space-drag: A feature which allows the user to move their HMD origin position via a controller -Passthrough: Some headsets have built-in cameras for displaying image on the HMD screen +- wlx-overlay-s: The name of this software (also called WlxOverlay-S) +- WayVR: A Wayland compositor intended to be used in VR +- WayVR Dashboard: An application (and game) launcher which is displayed in front of the user +- OpenVR: API made by Valve +- OpenXR: API made by Khronos +- OSC: OpenSoundControl +- Playspace: A designated real area where users can interact with the virtual environment +- Space-drag: A feature which allows the user to move their HMD origin position via a controller +- Passthrough: Some headsets have built-in cameras for displaying image on the HMD screen +End of glossary; You will be given the input in the code blocks which needs to be translated to {TARGET_LANG} language. Write only the result in codeblocks, do not explain. Keep any newlines and other important formatting-required identifiers as-is. - diff --git a/uidev/src/testbed/testbed_generic.rs b/uidev/src/testbed/testbed_generic.rs index 83dfe39..edf65bf 100644 --- a/uidev/src/testbed/testbed_generic.rs +++ b/uidev/src/testbed/testbed_generic.rs @@ -21,7 +21,7 @@ use wgui::{ parser::{Fetchable, ParseDocumentExtra, ParseDocumentParams, ParserState}, taffy, widget::{label::WidgetLabel, rectangle::WidgetRectangle}, - windowing::{WguiWindow, WguiWindowParams}, + windowing::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra}, }; pub enum TestbedTask { @@ -215,6 +215,8 @@ impl TestbedGeneric { globals: self.globals.clone(), position: Vec2::new(128.0, 128.0), layout, + title: Translation::from_raw_text("foo"), + extra: Default::default(), })?; Ok(()) diff --git a/wgui/assets/wgui/window_frame.xml b/wgui/assets/wgui/window_frame.xml index 6f07a75..8fdb7cf 100644 --- a/wgui/assets/wgui/window_frame.xml +++ b/wgui/assets/wgui/window_frame.xml @@ -1,23 +1,29 @@ + - - + -
+
diff --git a/wgui/src/components/tooltip.rs b/wgui/src/components/tooltip.rs index e2be282..f270675 100644 --- a/wgui/src/components/tooltip.rs +++ b/wgui/src/components/tooltip.rs @@ -103,7 +103,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul let transform = Mat4::from_translation(Vec3::new(-0.5, 0.0, 0.0)); - let (pin_left, pin_top, pin_align_items, pin_justify_content) = match params.info.side { + let (mut pin_left, mut pin_top, pin_align_items, pin_justify_content) = match params.info.side { TooltipSide::Left => ( absolute_boundary.left() - spacing, absolute_boundary.top() + absolute_boundary.size.y / 2.0, diff --git a/wgui/src/event.rs b/wgui/src/event.rs index 05d3223..15874bd 100644 --- a/wgui/src/event.rs +++ b/wgui/src/event.rs @@ -95,6 +95,8 @@ impl Event { pub enum StyleSetRequest { Display(taffy::Display), Margin(taffy::Rect), + Width(taffy::Dimension), + Height(taffy::Dimension), } // alterables which will be dispatched in the next loop iteration phase diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index b5ea5bc..17caee9 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -744,6 +744,12 @@ impl Layout { event::StyleSetRequest::Margin(margin) => { cur_style.margin = margin; } + event::StyleSetRequest::Width(val) => { + cur_style.size.width = val; + } + event::StyleSetRequest::Height(val) => { + cur_style.size.height = val; + } } if let Err(e) = self.state.tree.set_style(*node_id, cur_style) { diff --git a/wgui/src/parser/component_button.rs b/wgui/src/parser/component_button.rs index 17c971d..6e63d6e 100644 --- a/wgui/src/parser/component_button.rs +++ b/wgui/src/parser/component_button.rs @@ -1,12 +1,12 @@ use crate::{ - components::{button, tooltip, Component}, + components::{Component, button, tooltip}, drawing::Color, i18n::Translation, layout::WidgetID, parser::{ - parse_check_f32, parse_check_i32, parse_children, print_invalid_attrib, process_component, + AttribPair, ParserContext, ParserFile, parse_check_f32, parse_check_i32, parse_children, print_invalid_attrib, + process_component, style::{parse_color_opt, parse_round, parse_style, parse_text_style}, - AttribPair, ParserContext, ParserFile, }, widget::util::WLength, }; @@ -94,7 +94,7 @@ pub fn parse_component_button<'a>( text_style, round, tooltip: tooltip.map(|t| tooltip::TooltipInfo { - side: tooltip_side.map_or(tooltip::TooltipSide::Bottom, |f| f), + side: tooltip_side.map_or(tooltip::TooltipSide::Top, |f| f), text: Translation::from_translation_key(&t), }), sticky, diff --git a/wgui/src/windowing.rs b/wgui/src/windowing.rs index c805e3d..9fb72b8 100644 --- a/wgui/src/windowing.rs +++ b/wgui/src/windowing.rs @@ -1,15 +1,17 @@ use std::{cell::RefCell, rc::Rc}; use glam::Vec2; -use taffy::prelude::length; +use taffy::prelude::{length, percent}; use crate::{ assets::AssetPath, components::button::ComponentButton, + event::StyleSetRequest, globals::WguiGlobals, + i18n::Translation, layout::{Layout, LayoutTask, LayoutTasks, WidgetPair}, parser::{self, Fetchable, ParserState}, - widget::div::WidgetDiv, + widget::{div::WidgetDiv, label::WidgetLabel}, }; struct OpenedWindow { @@ -38,10 +40,28 @@ pub struct OnContentData { pub widget: WidgetPair, } +#[derive(Default)] +pub enum WguiWindowPlacement { + #[default] + TopLeft, + BottomLeft, + TopRight, + BottomRight, +} + +#[derive(Default)] +pub struct WguiWindowParamsExtra { + pub fixed_width: Option, + pub fixed_height: Option, + pub placement: WguiWindowPlacement, +} + pub struct WguiWindowParams<'a> { pub position: Vec2, pub globals: WguiGlobals, pub layout: &'a mut Layout, + pub title: Translation, + pub extra: WguiWindowParamsExtra, } impl Default for WguiWindow { @@ -50,6 +70,9 @@ impl Default for WguiWindow { } } +const WINDOW_DECORATION_HEADER_HEIGHT: f32 = 32.0; +const WINDOW_DECORATION_PADDING: f32 = 2.0; + impl WguiWindow { pub fn close(&self) { self.0.borrow_mut().opened_window = None; @@ -61,15 +84,59 @@ impl WguiWindow { const XML_PATH: AssetPath = AssetPath::WguiInternal("wgui/window_frame.xml"); + let (padding, justify_content, align_items) = match params.extra.placement { + WguiWindowPlacement::TopLeft => ( + taffy::Rect { + left: length(params.position.x - WINDOW_DECORATION_PADDING), + top: length(params.position.y - WINDOW_DECORATION_HEADER_HEIGHT - WINDOW_DECORATION_PADDING), + bottom: length(0.0), + right: length(0.0), + }, + taffy::JustifyContent::Start, // x start + taffy::AlignItems::Start, // y start + ), + WguiWindowPlacement::BottomLeft => ( + taffy::Rect { + left: length(params.position.x - WINDOW_DECORATION_PADDING), + top: length(0.0), + bottom: length(params.position.y - WINDOW_DECORATION_PADDING), + right: length(0.0), + }, + taffy::JustifyContent::Start, // x start + taffy::AlignItems::End, // y end + ), + WguiWindowPlacement::TopRight => ( + taffy::Rect { + left: length(0.0), + top: length(params.position.y - WINDOW_DECORATION_HEADER_HEIGHT - WINDOW_DECORATION_PADDING), + bottom: length(0.0), + right: length(params.position.x - WINDOW_DECORATION_PADDING), + }, + taffy::JustifyContent::End, // x end + taffy::AlignItems::Start, // y start + ), + WguiWindowPlacement::BottomRight => ( + taffy::Rect { + left: length(0.0), + top: length(0.0), + bottom: length(params.position.y - WINDOW_DECORATION_PADDING), + right: length(params.position.x - WINDOW_DECORATION_PADDING), + }, + taffy::JustifyContent::End, // x end + taffy::AlignItems::End, // y end + ), + }; + let (widget, _) = params.layout.add_topmost_child( WidgetDiv::create(), taffy::Style { position: taffy::Position::Absolute, - margin: taffy::Rect { - left: length(params.position.x), - right: length(0.0), - top: length(params.position.y), - bottom: length(0.0), + align_items: Some(align_items), + justify_content: Some(justify_content), + padding, + size: taffy::Size { + width: percent(1.0), + height: percent(1.0), }, ..Default::default() }, @@ -94,15 +161,35 @@ impl WguiWindow { }) }); + { + let mut text_title = state.fetch_widget_as::(¶ms.layout.state, "text_window_title")?; + text_title.set_text_simple(&mut params.globals.get(), params.title.clone()); + } + let content = state.fetch_widget(¶ms.layout.state, "content")?; self.0.borrow_mut().opened_window = Some(OpenedWindow { widget, state, layout_tasks: params.layout.tasks.clone(), - content, + content: content.clone(), }); + let mut c = params.layout.start_common(); + if let Some(width) = params.extra.fixed_width { + c.common() + .alterables + .set_style(content.id, StyleSetRequest::Width(length(width))); + } + + if let Some(height) = params.extra.fixed_height { + c.common() + .alterables + .set_style(content.id, StyleSetRequest::Height(length(height))); + } + + c.finish()?; + Ok(()) }