diff --git a/Cargo.lock b/Cargo.lock
index aa7f727e..908d0ab2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1479,6 +1479,7 @@ dependencies = [
"rust-embed",
"serde",
"serde_json",
+ "wayvr_ipc",
"wgui",
"wlx-common",
]
diff --git a/dash-frontend/Cargo.toml b/dash-frontend/Cargo.toml
index 5bd23d7b..6211da2e 100644
--- a/dash-frontend/Cargo.toml
+++ b/dash-frontend/Cargo.toml
@@ -15,3 +15,4 @@ gtk = "0.18.2"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
wlx-common = { path = "../wlx-common" }
+wayvr_ipc = { workspace = true }
diff --git a/dash-frontend/assets/gui/tab/processes.xml b/dash-frontend/assets/gui/tab/processes.xml
index 0330d48c..d8234299 100644
--- a/dash-frontend/assets/gui/tab/processes.xml
+++ b/dash-frontend/assets/gui/tab/processes.xml
@@ -2,6 +2,8 @@
-
+
+
+
\ No newline at end of file
diff --git a/dash-frontend/assets/gui/view/add_display.xml b/dash-frontend/assets/gui/view/add_display.xml
new file mode 100644
index 00000000..34f73be2
--- /dev/null
+++ b/dash-frontend/assets/gui/view/add_display.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dash-frontend/assets/gui/view/app_launcher.xml b/dash-frontend/assets/gui/view/app_launcher.xml
index 8b0b4bdc..951d5bd8 100644
--- a/dash-frontend/assets/gui/view/app_launcher.xml
+++ b/dash-frontend/assets/gui/view/app_launcher.xml
@@ -27,7 +27,7 @@
diff --git a/dash-frontend/assets/gui/view/display_list.xml b/dash-frontend/assets/gui/view/display_list.xml
new file mode 100644
index 00000000..bd72cf4e
--- /dev/null
+++ b/dash-frontend/assets/gui/view/display_list.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dash-frontend/assets/gui/view/popup_window.xml b/dash-frontend/assets/gui/view/popup_window.xml
index 011895df..180c88dd 100644
--- a/dash-frontend/assets/gui/view/popup_window.xml
+++ b/dash-frontend/assets/gui/view/popup_window.xml
@@ -25,7 +25,7 @@
diff --git a/dash-frontend/assets/lang/de.json b/dash-frontend/assets/lang/de.json
index f353d6a0..30e504ed 100644
--- a/dash-frontend/assets/lang/de.json
+++ b/dash-frontend/assets/lang/de.json
@@ -45,5 +45,12 @@
},
"ACTIONS": {
"RECENTER_PLAYSPACE": "Playspace neu zentrieren"
+ },
+ "LIST_OF_DISPLAYS": "Anzeigeliste",
+ "LIST_OF_PROCESSES": "Prozessliste",
+ "NO_DISPLAYS_FOUND": "Keine Displays gefunden",
+ "ADD_DISPLAY": "Bildschirm hinzufügen",
+ "POPUP_ADD_DISPLAY": {
+ "RESOLUTION": "Auflösung"
}
}
diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json
index b92964e6..3fe371a3 100644
--- a/dash-frontend/assets/lang/en.json
+++ b/dash-frontend/assets/lang/en.json
@@ -1,49 +1,56 @@
{
- "HOME_SCREEN": "Home",
- "MONADO_RUNTIME": "„Monado” runtime",
- "APPLICATIONS": "Applications",
- "GAMES": "Games",
- "SETTINGS": "Settings",
- "PROCESSES": "Processes",
- "HELLO_USER": "Hello, {USER}!",
- "HELLO": "Hello!",
- "GENERAL_SETTINGS": "General settings",
- "APPLICATION_LAUNCHER": "Application launcher",
- "APP_SETTINGS": {
- "RESTART_SOFTWARE": "Restart software",
- "HIDE_USERNAME": "Hide username",
- "OPAQUE_BACKGROUND": "Opaque background",
- "RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Run in XWayland mode by default",
- "WLX_OVERLAY_S_SETTINGS": "WlxOverlay-S settings",
- "HEADSET_SETTINGS": "Headset settings",
- "BRIGHTNESS": "Brightness",
- "WLX": {
- "NOTIFICATIONS_ENABLED": "Notifications enabled",
- "NOTIFICATIONS_SOUND_ENABLED": "Notifications sound enabled",
- "KEYBOARD_SOUND_ENABLED": "Keyboard sound enabled",
- "BLOCK_GAME_INPUT": "Block game input",
- "SPACE_DRAG_MULTIPLIER": "Space-drag multiplier",
- "SPACE_DRAG_ROTATION_ENABLED": "Enable rotation in space-drag",
- "SHOW_SKYBOX": "Show skybox",
- "ENABLE_PASSTHROUGH": "Enable passthrough"
- }
- },
- "AUDIO": {
- "SELECT_AUDIO_CARD_PROFILE": "Select audio card profile",
- "SETTINGS": "Audio settings",
- "VOLUME": "Volume",
- "AUTO_SWITCH_TO_VR_AUDIO": "Auto-switch to VR audio",
- "SPEAKERS": "Speakers",
- "MICROPHONES": "Microphones",
- "CARDS": "Cards",
- "NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "No VR speakers found. Switch them manually.",
- "NO_VR_MICROPHONE_SWITCH_MANUALLY": "No VR microphone found. Switch it manually.",
- "FAILED_TO_SWITCH_MICROPHONE": "Failed to switch microphone",
- "MICROPHONE_SET_SUCCESSFULLY": "Microphone set successfully",
- "SPEAKERS_SET_SUCCESSFULLY": "Speakers set successfully",
- "DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Device found and initialized, but not switched"
- },
"ACTIONS": {
"RECENTER_PLAYSPACE": "Re-center playspace"
- }
+ },
+ "POPUP_ADD_DISPLAY": {
+ "RESOLUTION": "Resolution"
+ },
+ "ADD_DISPLAY": "Add display",
+ "APP_SETTINGS": {
+ "BRIGHTNESS": "Brightness",
+ "HEADSET_SETTINGS": "Headset settings",
+ "HIDE_USERNAME": "Hide username",
+ "OPAQUE_BACKGROUND": "Opaque background",
+ "RESTART_SOFTWARE": "Restart software",
+ "RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Run in XWayland mode by default",
+ "WLX": {
+ "BLOCK_GAME_INPUT": "Block game input",
+ "ENABLE_PASSTHROUGH": "Enable passthrough",
+ "KEYBOARD_SOUND_ENABLED": "Keyboard sound enabled",
+ "NOTIFICATIONS_ENABLED": "Notifications enabled",
+ "NOTIFICATIONS_SOUND_ENABLED": "Notifications sound enabled",
+ "SHOW_SKYBOX": "Show skybox",
+ "SPACE_DRAG_MULTIPLIER": "Space-drag multiplier",
+ "SPACE_DRAG_ROTATION_ENABLED": "Enable rotation in space-drag"
+ },
+ "WLX_OVERLAY_S_SETTINGS": "WlxOverlay-S settings"
+ },
+ "APPLICATION_LAUNCHER": "Application launcher",
+ "APPLICATIONS": "Applications",
+ "AUDIO": {
+ "AUTO_SWITCH_TO_VR_AUDIO": "Auto-switch to VR audio",
+ "CARDS": "Cards",
+ "DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Device found and initialized, but not switched",
+ "FAILED_TO_SWITCH_MICROPHONE": "Failed to switch microphone",
+ "MICROPHONE_SET_SUCCESSFULLY": "Microphone set successfully",
+ "MICROPHONES": "Microphones",
+ "NO_VR_MICROPHONE_SWITCH_MANUALLY": "No VR microphone found. Switch it manually.",
+ "NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "No VR speakers found. Switch them manually.",
+ "SELECT_AUDIO_CARD_PROFILE": "Select audio card profile",
+ "SETTINGS": "Audio settings",
+ "SPEAKERS": "Speakers",
+ "SPEAKERS_SET_SUCCESSFULLY": "Speakers set successfully",
+ "VOLUME": "Volume"
+ },
+ "GAMES": "Games",
+ "GENERAL_SETTINGS": "General settings",
+ "HELLO": "Hello!",
+ "HELLO_USER": "Hello, {USER}!",
+ "HOME_SCREEN": "Home",
+ "LIST_OF_DISPLAYS": "Display list",
+ "LIST_OF_PROCESSES": "Process list",
+ "MONADO_RUNTIME": "„Monado” runtime",
+ "NO_DISPLAYS_FOUND": "No displays found",
+ "PROCESSES": "Processes",
+ "SETTINGS": "Settings"
}
diff --git a/dash-frontend/assets/lang/es.json b/dash-frontend/assets/lang/es.json
index cb7869dc..3c6b476f 100644
--- a/dash-frontend/assets/lang/es.json
+++ b/dash-frontend/assets/lang/es.json
@@ -45,5 +45,12 @@
},
"ACTIONS": {
"RECENTER_PLAYSPACE": "Re-centrar espacio de juego"
+ },
+ "LIST_OF_DISPLAYS": "Lista de pantallas",
+ "LIST_OF_PROCESSES": "Lista de procesos",
+ "NO_DISPLAYS_FOUND": "No se encontraron pantallas",
+ "ADD_DISPLAY": "Agregar pantalla",
+ "POPUP_ADD_DISPLAY": {
+ "RESOLUTION": "Resolución"
}
}
\ No newline at end of file
diff --git a/dash-frontend/assets/lang/ja.json b/dash-frontend/assets/lang/ja.json
index e8c59fb8..ce60767d 100644
--- a/dash-frontend/assets/lang/ja.json
+++ b/dash-frontend/assets/lang/ja.json
@@ -45,5 +45,12 @@
},
"ACTIONS": {
"RECENTER_PLAYSPACE": "プレイスペースを再中央"
+ },
+ "LIST_OF_DISPLAYS": "ディスプレイリスト",
+ "LIST_OF_PROCESSES": "プロセスのリスト",
+ "NO_DISPLAYS_FOUND": "ディスプレイが見つかりません",
+ "ADD_DISPLAY": "ディスプレイを追加",
+ "POPUP_ADD_DISPLAY": {
+ "RESOLUTION": "解像度"
}
}
\ No newline at end of file
diff --git a/dash-frontend/assets/lang/pl.json b/dash-frontend/assets/lang/pl.json
index e24c7364..2af95591 100644
--- a/dash-frontend/assets/lang/pl.json
+++ b/dash-frontend/assets/lang/pl.json
@@ -1,49 +1,56 @@
{
- "HOME_SCREEN": "Ekran główny",
- "MONADO_RUNTIME": "Środowisko Monado",
- "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",
- "SELECT_AUDIO_CARD_PROFILE": "Wybierz profil karty dźwiękowej",
- "NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "Brak głośników VR. Włącz je ręcznie.",
- "NO_VR_MICROPHONE_SWITCH_MANUALLY": "Brak mikrofonu VR. Włącz go ręcznie.",
- "FAILED_TO_SWITCH_MICROPHONE": "Nie udało się przełączyć mikrofon",
- "MICROPHONE_SET_SUCCESSFULLY": "Mikrofon ustawiono pomyślnie",
- "SPEAKERS_SET_SUCCESSFULLY": "Głośniki ustawiono pomyślnie",
- "DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Urządzenie znalezione i zainicjalizowane, ale nie przełączone"
- },
- "ACTIONS": {
- "RECENTER_PLAYSPACE": "Wycentruj przestrzeń"
- }
-}
+ "HOME_SCREEN": "Ekran główny",
+ "MONADO_RUNTIME": "Środowisko Monado",
+ "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",
+ "SELECT_AUDIO_CARD_PROFILE": "Wybierz profil karty dźwiękowej",
+ "NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "Brak głośników VR. Włącz je ręcznie.",
+ "NO_VR_MICROPHONE_SWITCH_MANUALLY": "Brak mikrofonu VR. Włącz go ręcznie.",
+ "FAILED_TO_SWITCH_MICROPHONE": "Nie udało się przełączyć mikrofon",
+ "MICROPHONE_SET_SUCCESSFULLY": "Mikrofon ustawiono pomyślnie",
+ "SPEAKERS_SET_SUCCESSFULLY": "Głośniki ustawiono pomyślnie",
+ "DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Urządzenie znalezione i zainicjalizowane, ale nie przełączone"
+ },
+ "ACTIONS": {
+ "RECENTER_PLAYSPACE": "Wycentruj przestrzeń"
+ },
+ "LIST_OF_DISPLAYS": "Lista wyświetlaczy",
+ "LIST_OF_PROCESSES": "Lista procesów",
+ "NO_DISPLAYS_FOUND": "Brak monitorów",
+ "ADD_DISPLAY": "Dodaj monitor",
+ "POPUP_ADD_DISPLAY": {
+ "RESOLUTION": "Rozdzielczość"
+ }
+}
\ No newline at end of file
diff --git a/dash-frontend/src/frontend.rs b/dash-frontend/src/frontend.rs
index f2a1355b..6f2d19cb 100644
--- a/dash-frontend/src/frontend.rs
+++ b/dash-frontend/src/frontend.rs
@@ -18,8 +18,8 @@ use wlx_common::{dash_interface, timestep::Timestep};
use crate::{
assets, settings,
tab::{
- Tab, TabParams, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses,
- settings::TabSettings,
+ Tab, TabParams, TabType, TabUpdateParams, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado,
+ processes::TabProcesses, settings::TabSettings,
},
task::Tasks,
util::{
@@ -166,6 +166,17 @@ impl Frontend {
self.process_task(rc_this, task)?;
}
+ if let Some(tab) = &mut self.current_tab {
+ let mut layout = self.layout.borrow_mut();
+
+ tab.update(TabUpdateParams {
+ globals: &self.globals,
+ frontend_tasks: &self.tasks,
+ layout: &mut layout,
+ interface: &mut self.interface,
+ })?;
+ }
+
self.tick(width, height, timestep_alpha)?;
self.ticks += 1;
@@ -292,6 +303,7 @@ impl Frontend {
frontend: rc_this,
//frontend_widgets: &self.widgets,
settings: self.settings.get_mut(),
+ frontend_tasks: &self.tasks,
};
let tab: Box = match tab_type {
@@ -308,15 +320,6 @@ 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();
@@ -325,44 +328,38 @@ impl Frontend {
// ################################
// "Home" side button
- Frontend::register_button_task(
- rc_this.clone(),
- &this.state.fetch_component_as::("btn_side_home")?,
+ this.tasks.handle_button(
+ 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")?,
+ this.tasks.handle_button(
+ 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")?,
+ this.tasks.handle_button(
+ 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")?,
+ this.tasks.handle_button(
+ 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")?,
+ this.tasks.handle_button(
+ 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")?,
+ this.tasks.handle_button(
+ this.state.fetch_component_as::("btn_side_settings")?,
FrontendTask::SetTab(TabType::Settings),
);
@@ -371,16 +368,14 @@ impl Frontend {
// ################################
// "Audio" bottom bar button
- Frontend::register_button_task(
- rc_this.clone(),
- &this.state.fetch_component_as::("btn_audio")?,
+ this.tasks.handle_button(
+ 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")?,
+ this.tasks.handle_button(
+ this.state.fetch_component_as::("btn_recenter")?,
FrontendTask::RecenterPlayspace,
);
diff --git a/dash-frontend/src/tab/home.rs b/dash-frontend/src/tab/home.rs
index ffb40845..ab6ed12c 100644
--- a/dash-frontend/src/tab/home.rs
+++ b/dash-frontend/src/tab/home.rs
@@ -66,16 +66,12 @@ impl TabHome {
let btn_processes = state.fetch_component_as::("btn_processes")?;
let btn_settings = state.fetch_component_as::("btn_settings")?;
- let frontend = params.frontend;
- 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));
+ let tasks = params.frontend_tasks;
+ tasks.handle_button(btn_apps, FrontendTask::SetTab(TabType::Apps));
+ tasks.handle_button(btn_games, FrontendTask::SetTab(TabType::Games));
+ tasks.handle_button(btn_monado, FrontendTask::SetTab(TabType::Monado));
+ tasks.handle_button(btn_processes, FrontendTask::SetTab(TabType::Processes));
+ tasks.handle_button(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 15a2f759..f0f3a60c 100644
--- a/dash-frontend/src/tab/mod.rs
+++ b/dash-frontend/src/tab/mod.rs
@@ -2,8 +2,9 @@ use wgui::{
globals::WguiGlobals,
layout::{Layout, WidgetID},
};
+use wlx_common::dash_interface;
-use crate::frontend::RcFrontend;
+use crate::frontend::{FrontendTasks, RcFrontend};
pub mod apps;
pub mod games;
@@ -28,9 +29,21 @@ pub struct TabParams<'a> {
pub parent_id: WidgetID,
pub frontend: &'a RcFrontend,
pub settings: &'a mut crate::settings::Settings,
+ pub frontend_tasks: &'a FrontendTasks,
+}
+
+pub struct TabUpdateParams<'a> {
+ pub globals: &'a WguiGlobals,
+ pub frontend_tasks: &'a FrontendTasks,
+ pub layout: &'a mut Layout,
+ pub interface: &'a mut Box,
}
pub trait Tab {
#[allow(dead_code)]
fn get_type(&self) -> TabType;
+
+ fn update(&mut self, _params: TabUpdateParams) -> anyhow::Result<()> {
+ Ok(())
+ }
}
diff --git a/dash-frontend/src/tab/processes.rs b/dash-frontend/src/tab/processes.rs
index a19bf280..fdda10d2 100644
--- a/dash-frontend/src/tab/processes.rs
+++ b/dash-frontend/src/tab/processes.rs
@@ -1,19 +1,29 @@
use wgui::{
assets::AssetPath,
- parser::{ParseDocumentParams, ParserState},
+ parser::{Fetchable, ParseDocumentParams, ParserState},
};
-use crate::tab::{Tab, TabParams, TabType};
+use crate::{
+ tab::{Tab, TabParams, TabType, TabUpdateParams},
+ views::display_list,
+};
pub struct TabProcesses {
#[allow(dead_code)]
pub state: ParserState,
+
+ view_display_list: display_list::View,
}
impl Tab for TabProcesses {
fn get_type(&self) -> TabType {
TabType::Games
}
+
+ fn update(&mut self, params: TabUpdateParams) -> anyhow::Result<()> {
+ self.view_display_list.update(params.layout, params.interface)?;
+ Ok(())
+ }
}
impl TabProcesses {
@@ -28,6 +38,14 @@ impl TabProcesses {
params.parent_id,
)?;
- Ok(Self { state })
+ Ok(Self {
+ view_display_list: display_list::View::new(display_list::Params {
+ layout: params.layout,
+ parent_id: state.get_widget_id("display_list_parent")?,
+ globals: params.globals.clone(),
+ frontend_tasks: params.frontend_tasks.clone(),
+ })?,
+ state,
+ })
}
}
diff --git a/dash-frontend/src/task.rs b/dash-frontend/src/task.rs
index f6af4569..26bc5cdd 100644
--- a/dash-frontend/src/task.rs
+++ b/dash-frontend/src/task.rs
@@ -1,11 +1,16 @@
use std::{cell::RefCell, collections::VecDeque, rc::Rc};
-#[derive(Clone)]
-pub struct Tasks(Rc>>)
-where
- TaskType: Clone;
+use wgui::components::button::ComponentButton;
-impl Tasks {
+pub struct Tasks(Rc>>);
+
+impl Clone for Tasks {
+ fn clone(&self) -> Self {
+ Self(self.0.clone())
+ }
+}
+
+impl Tasks {
pub fn new() -> Self {
Self(Rc::new(RefCell::new(VecDeque::new())))
}
@@ -20,8 +25,27 @@ impl Tasks {
}
}
-impl Default for Tasks {
+impl Default for Tasks {
fn default() -> Self {
Self::new()
}
}
+
+impl Tasks {
+ pub fn handle_button(&self, button: Rc, task: TaskType) {
+ button.on_click({
+ let this = self.clone();
+ Box::new(move |_, _| {
+ this.push(task.clone());
+ Ok(())
+ })
+ });
+ }
+
+ pub fn make_callback(&self, task: TaskType) -> Rc {
+ let this = self.clone();
+ Rc::new(move || {
+ this.push(task.clone());
+ })
+ }
+}
diff --git a/dash-frontend/src/views/add_display.rs b/dash-frontend/src/views/add_display.rs
new file mode 100644
index 00000000..26722466
--- /dev/null
+++ b/dash-frontend/src/views/add_display.rs
@@ -0,0 +1,79 @@
+use std::{collections::HashMap, rc::Rc};
+
+use wgui::{
+ assets::AssetPath,
+ components::{button::ComponentButton, slider::ComponentSlider},
+ globals::WguiGlobals,
+ layout::{Layout, WidgetID},
+ parser::{Fetchable, ParseDocumentParams, ParserState},
+ widget::{label::WidgetLabel, rectangle::WidgetRectangle},
+};
+
+use crate::{frontend::FrontendTasks, tab::TabUpdateParams, task::Tasks};
+
+#[derive(Clone)]
+enum Task {
+ Confirm,
+}
+
+pub struct View {
+ #[allow(dead_code)]
+ pub state: ParserState,
+ tasks: Tasks,
+ frontend_tasks: FrontendTasks,
+ on_submit: Rc,
+}
+
+pub struct Params<'a> {
+ pub globals: WguiGlobals,
+ pub frontend_tasks: FrontendTasks,
+ pub layout: &'a mut Layout,
+ pub parent_id: WidgetID,
+ pub on_submit: Rc,
+}
+
+impl View {
+ pub fn new(params: Params) -> anyhow::Result {
+ let doc_params = &ParseDocumentParams {
+ globals: params.globals.clone(),
+ path: AssetPath::BuiltIn("gui/view/add_display.xml"),
+ extra: Default::default(),
+ };
+
+ let state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?;
+
+ let tasks = Tasks::new();
+
+ let slider_width = state.fetch_component_as::("slider_width")?;
+ let slider_height = state.fetch_component_as::("slider_height")?;
+ let label_display_name = state.fetch_widget_as::(¶ms.layout.state, "label_display_name")?;
+ let rect_display = state.fetch_widget_as::(¶ms.layout.state, "rect_display");
+ let label_display = state.fetch_widget_as::(¶ms.layout.state, "label_display");
+ let btn_confirm = state.fetch_component_as::("btn_confirm")?;
+
+ tasks.handle_button(btn_confirm, Task::Confirm);
+
+ Ok(Self {
+ state,
+ tasks,
+ frontend_tasks: params.frontend_tasks,
+ on_submit: params.on_submit,
+ })
+ }
+
+ pub fn update(&mut self) -> anyhow::Result<()> {
+ for task in self.tasks.drain() {
+ match task {
+ Task::Confirm => self.confirm(),
+ }
+ }
+ Ok(())
+ }
+}
+
+impl View {
+ fn confirm(&mut self) {
+ log::info!("confirm");
+ (*self.on_submit)();
+ }
+}
diff --git a/dash-frontend/src/views/display_list.rs b/dash-frontend/src/views/display_list.rs
new file mode 100644
index 00000000..06748c9d
--- /dev/null
+++ b/dash-frontend/src/views/display_list.rs
@@ -0,0 +1,224 @@
+use std::{cell::RefCell, collections::HashMap, rc::Rc};
+
+use wayvr_ipc::packet_server::WvrDisplay;
+use wgui::{
+ assets::AssetPath,
+ components::button::ComponentButton,
+ globals::WguiGlobals,
+ i18n::Translation,
+ layout::{Layout, WidgetID},
+ parser::{Fetchable, ParseDocumentParams, ParserState},
+ renderer_vk::text::{FontWeight, TextStyle},
+ taffy,
+ widget::{
+ label::{WidgetLabel, WidgetLabelParams},
+ rectangle::{WidgetRectangle, WidgetRectangleParams},
+ },
+};
+use wlx_common::dash_interface;
+
+use crate::{
+ frontend::{FrontendTask, FrontendTasks},
+ tab::TabUpdateParams,
+ task::Tasks,
+ util::popup_manager::{MountPopupParams, PopupHandle},
+ views::add_display,
+};
+
+#[derive(Clone)]
+enum Task {
+ AddDisplay,
+ AddDisplayFinish,
+ Refresh,
+}
+
+pub struct Params<'a> {
+ pub globals: WguiGlobals,
+ pub frontend_tasks: FrontendTasks,
+ pub layout: &'a mut Layout,
+ pub parent_id: WidgetID,
+}
+
+struct State {
+ view_add_display: Option<(PopupHandle, add_display::View)>,
+}
+
+pub struct View {
+ #[allow(dead_code)]
+ pub parser_state: ParserState,
+ tasks: Tasks,
+ frontend_tasks: FrontendTasks,
+ globals: WguiGlobals,
+ state: Rc>,
+ id_list_parent: WidgetID,
+}
+
+impl View {
+ pub fn new(params: Params) -> anyhow::Result {
+ let doc_params = &ParseDocumentParams {
+ globals: params.globals.clone(),
+ path: AssetPath::BuiltIn("gui/view/display_list.xml"),
+ extra: Default::default(),
+ };
+
+ let parser_state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?;
+ let list_parent = parser_state.fetch_widget(¶ms.layout.state, "list_parent")?;
+
+ let tasks = Tasks::new();
+
+ let btn_add = parser_state.fetch_component_as::("btn_add")?;
+ tasks.handle_button(btn_add, Task::AddDisplay);
+
+ tasks.push(Task::Refresh);
+
+ let state = Rc::new(RefCell::new(State { view_add_display: None }));
+
+ Ok(Self {
+ parser_state,
+ tasks,
+ frontend_tasks: params.frontend_tasks,
+ globals: params.globals,
+ state,
+ id_list_parent: list_parent.id,
+ })
+ }
+
+ pub fn update(
+ &mut self,
+ layout: &mut Layout,
+ interface: &mut Box,
+ ) -> anyhow::Result<()> {
+ loop {
+ let tasks = self.tasks.drain();
+ if tasks.is_empty() {
+ break;
+ }
+ for task in tasks {
+ match task {
+ Task::AddDisplay => self.add_display(),
+ Task::AddDisplayFinish => self.add_display_finish()?,
+ Task::Refresh => self.refresh(layout, interface)?,
+ }
+ }
+ }
+
+ let mut state = self.state.borrow_mut();
+ if let Some((_, view)) = &mut state.view_add_display {
+ view.update()?;
+ }
+
+ Ok(())
+ }
+}
+
+fn fill_display_list(
+ globals: &WguiGlobals,
+ parent: WidgetID,
+ layout: &mut Layout,
+ list: Vec,
+) -> anyhow::Result<()> {
+ for entry in list {
+ let (rect, _) = layout.add_child(
+ parent,
+ WidgetRectangle::create(WidgetRectangleParams { ..Default::default() }),
+ taffy::Style {
+ align_items: Some(taffy::AlignItems::Center),
+ justify_content: Some(taffy::JustifyContent::Center),
+ ..Default::default()
+ },
+ )?;
+
+ let label_name = WidgetLabel::create(
+ &mut globals.get(),
+ WidgetLabelParams {
+ content: Translation::from_raw_text(&entry.name),
+ style: TextStyle {
+ weight: Some(FontWeight::Bold),
+ ..Default::default()
+ },
+ },
+ );
+
+ let label_resolution = WidgetLabel::create(
+ &mut globals.get(),
+ WidgetLabelParams {
+ content: Translation::from_raw_text(""),
+ ..Default::default()
+ },
+ );
+
+ layout.add_child(rect.id, label_name, Default::default())?;
+ layout.add_child(rect.id, label_resolution, Default::default())?;
+ }
+
+ Ok(())
+}
+
+impl View {
+ fn add_display(&mut self) {
+ self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
+ title: Translation::from_translation_key("ADD_DISPLAY"),
+ on_content: {
+ let frontend_tasks = self.frontend_tasks.clone();
+ let globals = self.globals.clone();
+ let state = self.state.clone();
+ let tasks = self.tasks.clone();
+
+ Rc::new(move |data| {
+ state.borrow_mut().view_add_display = Some((
+ data.handle,
+ add_display::View::new(add_display::Params {
+ frontend_tasks: frontend_tasks.clone(),
+ globals: globals.clone(),
+ layout: data.layout,
+ parent_id: data.id_content,
+ on_submit: tasks.make_callback(Task::AddDisplayFinish),
+ })?,
+ ));
+ Ok(())
+ })
+ },
+ }));
+ }
+
+ fn add_display_finish(&mut self) -> anyhow::Result<()> {
+ self.state.borrow_mut().view_add_display = None;
+ Ok(())
+ }
+
+ fn refresh(
+ &mut self,
+ layout: &mut Layout,
+ interface: &mut Box,
+ ) -> anyhow::Result<()> {
+ layout.remove_children(self.id_list_parent);
+
+ let mut text: Option = None;
+ match interface.display_list() {
+ Ok(list) => {
+ if list.is_empty() {
+ text = Some(Translation::from_translation_key("NO_DISPLAYS_FOUND"))
+ } else {
+ fill_display_list(&self.globals, self.id_list_parent, layout, list)?
+ }
+ }
+ Err(e) => text = Some(Translation::from_raw_text(&format!("Error: {:?}", e))),
+ }
+
+ if let Some(text) = text.take() {
+ layout.add_child(
+ self.id_list_parent,
+ WidgetLabel::create(
+ &mut self.globals.get(),
+ WidgetLabelParams {
+ content: text,
+ ..Default::default()
+ },
+ ),
+ Default::default(),
+ )?;
+ }
+
+ Ok(())
+ }
+}
diff --git a/dash-frontend/src/views/mod.rs b/dash-frontend/src/views/mod.rs
index 8fe26e0b..ec19001e 100644
--- a/dash-frontend/src/views/mod.rs
+++ b/dash-frontend/src/views/mod.rs
@@ -1,2 +1,4 @@
+pub mod add_display;
pub mod app_launcher;
pub mod audio_settings;
+pub mod display_list;
diff --git a/wgui/src/components/button.rs b/wgui/src/components/button.rs
index 2f66cdc6..f84b79d0 100644
--- a/wgui/src/components/button.rs
+++ b/wgui/src/components/button.rs
@@ -63,7 +63,7 @@ impl Default for Params<'_> {
}
pub struct ButtonClickEvent {}
-pub type ButtonClickCallback = Box anyhow::Result<()>>;
+pub type ButtonClickCallback = Box anyhow::Result<()>>;
pub struct Colors {
pub color: drawing::Color,
@@ -351,7 +351,7 @@ fn register_event_mouse_release(
state.down = false;
if state.hovered
- && let Some(on_click) = &state.on_click
+ && let Some(on_click) = &mut state.on_click
{
anim_hover(
rect,