display_list, add_display views (wip)

[skip ci]
This commit is contained in:
Aleksander
2025-12-15 21:08:59 +01:00
parent 7118cea810
commit 9aea733452
21 changed files with 574 additions and 153 deletions

1
Cargo.lock generated
View File

@@ -1479,6 +1479,7 @@ dependencies = [
"rust-embed", "rust-embed",
"serde", "serde",
"serde_json", "serde_json",
"wayvr_ipc",
"wgui", "wgui",
"wlx-common", "wlx-common",
] ]

View File

@@ -15,3 +15,4 @@ gtk = "0.18.2"
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145" serde_json = "1.0.145"
wlx-common = { path = "../wlx-common" } wlx-common = { path = "../wlx-common" }
wayvr_ipc = { workspace = true }

View File

@@ -2,6 +2,8 @@
<include src="t_tab_title.xml" /> <include src="t_tab_title.xml" />
<elements> <elements>
<TabTitle translation="PROCESSES" icon="dashboard/window.svg" /> <TabTitle translation="LIST_OF_DISPLAYS" icon="dashboard/window.svg" />
<div id="display_list_parent" />
<TabTitle translation="LIST_OF_PROCESSES" icon="dashboard/cpu.svg" />
</elements> </elements>
</layout> </layout>

View File

@@ -0,0 +1,21 @@
<layout>
<include src="../t_group_box.xml" />
<elements>
<div gap="8" flex_direction="column" width="100%" justify_self="center" align_items="center" justify_content="center">
<rectangle macro="group_box">
<label translation="POPUP_ADD_DISPLAY.RESOLUTION" weight="bold" size="20" />
<Slider id="slider_width" min_value="0" max_value="10" width="250" height="24" />
<Slider id="slider_height" min_value="0" max_value="10" width="250" height="24" />
<rectangle macro="group_box" id="rect_display">
<label id="label_display" />
</rectangle>
</rectangle>
<Button id="btn_confirm" color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_left="12" padding_right="12">
<sprite src_builtin="dashboard/display.svg" width="32" height="32" />
<label translation="ADD_DISPLAY" weight="bold" size="17" shadow="#00000099" />
</Button>
<label id="label_display_name" weight="bold" size="20" />
</div>
</elements>
</layout>

View File

@@ -27,7 +27,7 @@
<CheckBox text="Run in Wayland mode" checked="1" /> <CheckBox text="Run in Wayland mode" checked="1" />
<Separator /> <Separator />
<Button color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_right="12"> <Button color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_right="12">
<sprite src="dashboard/play.svg" width="32" height="32" /> <sprite src_builtin="dashboard/play.svg" width="32" height="32" />
<label text="Launch embedded" weight="bold" size="17" shadow="#00000099" /> <label text="Launch embedded" weight="bold" size="17" shadow="#00000099" />
</Button> </Button>
<Separator /> <Separator />

View File

@@ -0,0 +1,10 @@
<layout>
<include src="../t_group_box.xml" />
<elements>
<rectangle macro="group_box" flex_direction="row">
<div id="list_parent" />
<Button id="btn_add" sprite_src_builtin="dashboard/add.svg" tooltip="ADD_DISPLAY" height="100%" width="32" />
</rectangle>
</elements>
</layout>

View File

@@ -25,7 +25,7 @@
<div gap="16" align_items="center"> <div gap="16" align_items="center">
<!-- Back button --> <!-- Back button -->
<Button id="but_back" width="48" height="48" color="#ffffff00" border_color="#ffffff00"> <Button id="but_back" width="48" height="48" color="#ffffff00" border_color="#ffffff00">
<sprite src="dashboard/back.svg" width="24" height="24" /> <sprite src_builtin="dashboard/back.svg" width="24" height="24" />
</Button> </Button>
<!-- Title --> <!-- Title -->

View File

@@ -45,5 +45,12 @@
}, },
"ACTIONS": { "ACTIONS": {
"RECENTER_PLAYSPACE": "Playspace neu zentrieren" "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"
} }
} }

View File

@@ -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": { "ACTIONS": {
"RECENTER_PLAYSPACE": "Re-center playspace" "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"
} }

View File

@@ -45,5 +45,12 @@
}, },
"ACTIONS": { "ACTIONS": {
"RECENTER_PLAYSPACE": "Re-centrar espacio de juego" "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"
} }
} }

View File

@@ -45,5 +45,12 @@
}, },
"ACTIONS": { "ACTIONS": {
"RECENTER_PLAYSPACE": "プレイスペースを再中央" "RECENTER_PLAYSPACE": "プレイスペースを再中央"
},
"LIST_OF_DISPLAYS": "ディスプレイリスト",
"LIST_OF_PROCESSES": "プロセスのリスト",
"NO_DISPLAYS_FOUND": "ディスプレイが見つかりません",
"ADD_DISPLAY": "ディスプレイを追加",
"POPUP_ADD_DISPLAY": {
"RESOLUTION": "解像度"
} }
} }

View File

@@ -1,49 +1,56 @@
{ {
"HOME_SCREEN": "Ekran główny", "HOME_SCREEN": "Ekran główny",
"MONADO_RUNTIME": "Środowisko Monado", "MONADO_RUNTIME": "Środowisko Monado",
"APPLICATIONS": "Aplikacje", "APPLICATIONS": "Aplikacje",
"GAMES": "Gry", "GAMES": "Gry",
"SETTINGS": "Ustawienia", "SETTINGS": "Ustawienia",
"PROCESSES": "Procesy", "PROCESSES": "Procesy",
"HELLO_USER": "Witaj, {USER}!", "HELLO_USER": "Witaj, {USER}!",
"GENERAL_SETTINGS": "Ustawienia ogólne", "GENERAL_SETTINGS": "Ustawienia ogólne",
"APPLICATION_LAUNCHER": "Uruchamiacz aplikacji", "APPLICATION_LAUNCHER": "Uruchamiacz aplikacji",
"APP_SETTINGS": { "APP_SETTINGS": {
"HIDE_USERNAME": "Ukryj nazwę użytkownika", "HIDE_USERNAME": "Ukryj nazwę użytkownika",
"OPAQUE_BACKGROUND": "Nieprzezroczyste tło", "OPAQUE_BACKGROUND": "Nieprzezroczyste tło",
"RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Uruchom domyślnie w trybie XWayland", "RUN_IN_XWAYLAND_MODE_BY_DEFAULT": "Uruchom domyślnie w trybie XWayland",
"WLX_OVERLAY_S_SETTINGS": "Ustawienia wlx-overlay-s", "WLX_OVERLAY_S_SETTINGS": "Ustawienia wlx-overlay-s",
"HEADSET_SETTINGS": "Ustawienia HMD", "HEADSET_SETTINGS": "Ustawienia HMD",
"BRIGHTNESS": "Jasność", "BRIGHTNESS": "Jasność",
"WLX": { "WLX": {
"NOTIFICATIONS_ENABLED": "Powiadomienia", "NOTIFICATIONS_ENABLED": "Powiadomienia",
"NOTIFICATIONS_SOUND_ENABLED": "Dźwięk powiadomień", "NOTIFICATIONS_SOUND_ENABLED": "Dźwięk powiadomień",
"KEYBOARD_SOUND_ENABLED": "Dźwięki klawiatury", "KEYBOARD_SOUND_ENABLED": "Dźwięki klawiatury",
"BLOCK_GAME_INPUT": "Zablokuj sterowanie grą podczas używania Wlx", "BLOCK_GAME_INPUT": "Zablokuj sterowanie grą podczas używania Wlx",
"SPACE_DRAG_MULTIPLIER": "Mnożnik space-drag", "SPACE_DRAG_MULTIPLIER": "Mnożnik space-drag",
"SPACE_DRAG_ROTATION_ENABLED": "Włącz rotację w space-drag", "SPACE_DRAG_ROTATION_ENABLED": "Włącz rotację w space-drag",
"SHOW_SKYBOX": "Pokaż skybox", "SHOW_SKYBOX": "Pokaż skybox",
"ENABLE_PASSTHROUGH": "Włącz passthrough" "ENABLE_PASSTHROUGH": "Włącz passthrough"
}, },
"RESTART_SOFTWARE": "Uruchom ponownie oprogramowanie" "RESTART_SOFTWARE": "Uruchom ponownie oprogramowanie"
}, },
"HELLO": "Witaj!", "HELLO": "Witaj!",
"AUDIO": { "AUDIO": {
"VOLUME": "Głośność", "VOLUME": "Głośność",
"SETTINGS": "Ustawienia dźwięku", "SETTINGS": "Ustawienia dźwięku",
"AUTO_SWITCH_TO_VR_AUDIO": "Automatyczne przełączanie na dźwięk VR", "AUTO_SWITCH_TO_VR_AUDIO": "Automatyczne przełączanie na dźwięk VR",
"SPEAKERS": "Głośniki", "SPEAKERS": "Głośniki",
"MICROPHONES": "Mikrofony", "MICROPHONES": "Mikrofony",
"CARDS": "Karty", "CARDS": "Karty",
"SELECT_AUDIO_CARD_PROFILE": "Wybierz profil karty dźwiękowej", "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_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.", "NO_VR_MICROPHONE_SWITCH_MANUALLY": "Brak mikrofonu VR. Włącz go ręcznie.",
"FAILED_TO_SWITCH_MICROPHONE": "Nie udało się przełączyć mikrofon", "FAILED_TO_SWITCH_MICROPHONE": "Nie udało się przełączyć mikrofon",
"MICROPHONE_SET_SUCCESSFULLY": "Mikrofon ustawiono pomyślnie", "MICROPHONE_SET_SUCCESSFULLY": "Mikrofon ustawiono pomyślnie",
"SPEAKERS_SET_SUCCESSFULLY": "Głośniki 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" "DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Urządzenie znalezione i zainicjalizowane, ale nie przełączone"
}, },
"ACTIONS": { "ACTIONS": {
"RECENTER_PLAYSPACE": "Wycentruj przestrzeń" "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ść"
}
}

View File

@@ -18,8 +18,8 @@ use wlx_common::{dash_interface, timestep::Timestep};
use crate::{ use crate::{
assets, settings, assets, settings,
tab::{ tab::{
Tab, TabParams, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses, Tab, TabParams, TabType, TabUpdateParams, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado,
settings::TabSettings, processes::TabProcesses, settings::TabSettings,
}, },
task::Tasks, task::Tasks,
util::{ util::{
@@ -166,6 +166,17 @@ impl Frontend {
self.process_task(rc_this, task)?; 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.tick(width, height, timestep_alpha)?;
self.ticks += 1; self.ticks += 1;
@@ -292,6 +303,7 @@ impl Frontend {
frontend: rc_this, frontend: rc_this,
//frontend_widgets: &self.widgets, //frontend_widgets: &self.widgets,
settings: self.settings.get_mut(), settings: self.settings.get_mut(),
frontend_tasks: &self.tasks,
}; };
let tab: Box<dyn Tab> = match tab_type { let tab: Box<dyn Tab> = match tab_type {
@@ -308,15 +320,6 @@ impl Frontend {
Ok(()) 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<()> { fn register_widgets(rc_this: &RcFrontend) -> anyhow::Result<()> {
let this = rc_this.borrow_mut(); let this = rc_this.borrow_mut();
@@ -325,44 +328,38 @@ impl Frontend {
// ################################ // ################################
// "Home" side button // "Home" side button
Frontend::register_button_task( this.tasks.handle_button(
rc_this.clone(), this.state.fetch_component_as::<ComponentButton>("btn_side_home")?,
&this.state.fetch_component_as::<ComponentButton>("btn_side_home")?,
FrontendTask::SetTab(TabType::Home), FrontendTask::SetTab(TabType::Home),
); );
// "Apps" side button // "Apps" side button
Frontend::register_button_task( this.tasks.handle_button(
rc_this.clone(), this.state.fetch_component_as::<ComponentButton>("btn_side_apps")?,
&this.state.fetch_component_as::<ComponentButton>("btn_side_apps")?,
FrontendTask::SetTab(TabType::Apps), FrontendTask::SetTab(TabType::Apps),
); );
// "Games" side button // "Games" side button
Frontend::register_button_task( this.tasks.handle_button(
rc_this.clone(), this.state.fetch_component_as::<ComponentButton>("btn_side_games")?,
&this.state.fetch_component_as::<ComponentButton>("btn_side_games")?,
FrontendTask::SetTab(TabType::Games), FrontendTask::SetTab(TabType::Games),
); );
// "Monado side button" // "Monado side button"
Frontend::register_button_task( this.tasks.handle_button(
rc_this.clone(), this.state.fetch_component_as::<ComponentButton>("btn_side_monado")?,
&this.state.fetch_component_as::<ComponentButton>("btn_side_monado")?,
FrontendTask::SetTab(TabType::Monado), FrontendTask::SetTab(TabType::Monado),
); );
// "Processes" side button // "Processes" side button
Frontend::register_button_task( this.tasks.handle_button(
rc_this.clone(), this.state.fetch_component_as::<ComponentButton>("btn_side_processes")?,
&this.state.fetch_component_as::<ComponentButton>("btn_side_processes")?,
FrontendTask::SetTab(TabType::Processes), FrontendTask::SetTab(TabType::Processes),
); );
// "Settings" side button // "Settings" side button
Frontend::register_button_task( this.tasks.handle_button(
rc_this.clone(), this.state.fetch_component_as::<ComponentButton>("btn_side_settings")?,
&this.state.fetch_component_as::<ComponentButton>("btn_side_settings")?,
FrontendTask::SetTab(TabType::Settings), FrontendTask::SetTab(TabType::Settings),
); );
@@ -371,16 +368,14 @@ impl Frontend {
// ################################ // ################################
// "Audio" bottom bar button // "Audio" bottom bar button
Frontend::register_button_task( this.tasks.handle_button(
rc_this.clone(), this.state.fetch_component_as::<ComponentButton>("btn_audio")?,
&this.state.fetch_component_as::<ComponentButton>("btn_audio")?,
FrontendTask::ShowAudioSettings, FrontendTask::ShowAudioSettings,
); );
// "Recenter playspace" bottom bar button // "Recenter playspace" bottom bar button
Frontend::register_button_task( this.tasks.handle_button(
rc_this.clone(), this.state.fetch_component_as::<ComponentButton>("btn_recenter")?,
&this.state.fetch_component_as::<ComponentButton>("btn_recenter")?,
FrontendTask::RecenterPlayspace, FrontendTask::RecenterPlayspace,
); );

View File

@@ -66,16 +66,12 @@ impl TabHome {
let btn_processes = state.fetch_component_as::<ComponentButton>("btn_processes")?; let btn_processes = state.fetch_component_as::<ComponentButton>("btn_processes")?;
let btn_settings = state.fetch_component_as::<ComponentButton>("btn_settings")?; let btn_settings = state.fetch_component_as::<ComponentButton>("btn_settings")?;
let frontend = params.frontend; let tasks = params.frontend_tasks;
Frontend::register_button_task(frontend.clone(), &btn_apps, FrontendTask::SetTab(TabType::Apps)); tasks.handle_button(btn_apps, FrontendTask::SetTab(TabType::Apps));
Frontend::register_button_task(frontend.clone(), &btn_games, FrontendTask::SetTab(TabType::Games)); tasks.handle_button(btn_games, FrontendTask::SetTab(TabType::Games));
Frontend::register_button_task(frontend.clone(), &btn_monado, FrontendTask::SetTab(TabType::Monado)); tasks.handle_button(btn_monado, FrontendTask::SetTab(TabType::Monado));
Frontend::register_button_task( tasks.handle_button(btn_processes, FrontendTask::SetTab(TabType::Processes));
frontend.clone(), tasks.handle_button(btn_settings, FrontendTask::SetTab(TabType::Settings));
&btn_processes,
FrontendTask::SetTab(TabType::Processes),
);
Frontend::register_button_task(frontend.clone(), &btn_settings, FrontendTask::SetTab(TabType::Settings));
Ok(Self { state }) Ok(Self { state })
} }

View File

@@ -2,8 +2,9 @@ use wgui::{
globals::WguiGlobals, globals::WguiGlobals,
layout::{Layout, WidgetID}, layout::{Layout, WidgetID},
}; };
use wlx_common::dash_interface;
use crate::frontend::RcFrontend; use crate::frontend::{FrontendTasks, RcFrontend};
pub mod apps; pub mod apps;
pub mod games; pub mod games;
@@ -28,9 +29,21 @@ pub struct TabParams<'a> {
pub parent_id: WidgetID, pub parent_id: WidgetID,
pub frontend: &'a RcFrontend, pub frontend: &'a RcFrontend,
pub settings: &'a mut crate::settings::Settings, 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<dyn dash_interface::DashInterface>,
} }
pub trait Tab { pub trait Tab {
#[allow(dead_code)] #[allow(dead_code)]
fn get_type(&self) -> TabType; fn get_type(&self) -> TabType;
fn update(&mut self, _params: TabUpdateParams) -> anyhow::Result<()> {
Ok(())
}
} }

View File

@@ -1,19 +1,29 @@
use wgui::{ use wgui::{
assets::AssetPath, 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 { pub struct TabProcesses {
#[allow(dead_code)] #[allow(dead_code)]
pub state: ParserState, pub state: ParserState,
view_display_list: display_list::View,
} }
impl Tab for TabProcesses { impl Tab for TabProcesses {
fn get_type(&self) -> TabType { fn get_type(&self) -> TabType {
TabType::Games TabType::Games
} }
fn update(&mut self, params: TabUpdateParams) -> anyhow::Result<()> {
self.view_display_list.update(params.layout, params.interface)?;
Ok(())
}
} }
impl TabProcesses { impl TabProcesses {
@@ -28,6 +38,14 @@ impl TabProcesses {
params.parent_id, 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,
})
} }
} }

View File

@@ -1,11 +1,16 @@
use std::{cell::RefCell, collections::VecDeque, rc::Rc}; use std::{cell::RefCell, collections::VecDeque, rc::Rc};
#[derive(Clone)] use wgui::components::button::ComponentButton;
pub struct Tasks<TaskType>(Rc<RefCell<VecDeque<TaskType>>>)
where
TaskType: Clone;
impl<TaskType: Clone + 'static> Tasks<TaskType> { pub struct Tasks<TaskType>(Rc<RefCell<VecDeque<TaskType>>>);
impl<T> Clone for Tasks<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<TaskType: 'static> Tasks<TaskType> {
pub fn new() -> Self { pub fn new() -> Self {
Self(Rc::new(RefCell::new(VecDeque::new()))) Self(Rc::new(RefCell::new(VecDeque::new())))
} }
@@ -20,8 +25,27 @@ impl<TaskType: Clone + 'static> Tasks<TaskType> {
} }
} }
impl<TaskType: Clone + 'static> Default for Tasks<TaskType> { impl<TaskType: 'static> Default for Tasks<TaskType> {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl<TaskType: Clone + 'static> Tasks<TaskType> {
pub fn handle_button(&self, button: Rc<ComponentButton>, 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<dyn Fn()> {
let this = self.clone();
Rc::new(move || {
this.push(task.clone());
})
}
}

View File

@@ -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<Task>,
frontend_tasks: FrontendTasks,
on_submit: Rc<dyn Fn()>,
}
pub struct Params<'a> {
pub globals: WguiGlobals,
pub frontend_tasks: FrontendTasks,
pub layout: &'a mut Layout,
pub parent_id: WidgetID,
pub on_submit: Rc<dyn Fn()>,
}
impl View {
pub fn new(params: Params) -> anyhow::Result<Self> {
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::<ComponentSlider>("slider_width")?;
let slider_height = state.fetch_component_as::<ComponentSlider>("slider_height")?;
let label_display_name = state.fetch_widget_as::<WidgetLabel>(&params.layout.state, "label_display_name")?;
let rect_display = state.fetch_widget_as::<WidgetRectangle>(&params.layout.state, "rect_display");
let label_display = state.fetch_widget_as::<WidgetLabel>(&params.layout.state, "label_display");
let btn_confirm = state.fetch_component_as::<ComponentButton>("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)();
}
}

View File

@@ -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<Task>,
frontend_tasks: FrontendTasks,
globals: WguiGlobals,
state: Rc<RefCell<State>>,
id_list_parent: WidgetID,
}
impl View {
pub fn new(params: Params) -> anyhow::Result<Self> {
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(&params.layout.state, "list_parent")?;
let tasks = Tasks::new();
let btn_add = parser_state.fetch_component_as::<ComponentButton>("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<dyn dash_interface::DashInterface>,
) -> 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<WvrDisplay>,
) -> 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<dyn dash_interface::DashInterface>,
) -> anyhow::Result<()> {
layout.remove_children(self.id_list_parent);
let mut text: Option<Translation> = 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(())
}
}

View File

@@ -1,2 +1,4 @@
pub mod add_display;
pub mod app_launcher; pub mod app_launcher;
pub mod audio_settings; pub mod audio_settings;
pub mod display_list;

View File

@@ -63,7 +63,7 @@ impl Default for Params<'_> {
} }
pub struct ButtonClickEvent {} pub struct ButtonClickEvent {}
pub type ButtonClickCallback = Box<dyn Fn(&mut CallbackDataCommon, ButtonClickEvent) -> anyhow::Result<()>>; pub type ButtonClickCallback = Box<dyn FnMut(&mut CallbackDataCommon, ButtonClickEvent) -> anyhow::Result<()>>;
pub struct Colors { pub struct Colors {
pub color: drawing::Color, pub color: drawing::Color,
@@ -351,7 +351,7 @@ fn register_event_mouse_release(
state.down = false; state.down = false;
if state.hovered if state.hovered
&& let Some(on_click) = &state.on_click && let Some(on_click) = &mut state.on_click
{ {
anim_hover( anim_hover(
rect, rect,