Monado app switcher, lang update

This commit is contained in:
Aleksander
2026-01-08 19:46:34 +01:00
parent 650bc99a95
commit e421c39539
22 changed files with 566 additions and 153 deletions

View File

@@ -1,7 +1,35 @@
<layout>
<include src="t_tab_title.xml" />
<include src="../t_group_box.xml" />
<!-- key: str, value: str -->
<template name="BoolFlag">
<div flex_direction="row" gap="4">
<label text="${key}" />
<label weight="bold" text="${value}" />
</div>
</template>
<!-- name, checked, flag_* -->
<template name="Cell">
<rectangle macro="group_box">
<CheckBox id="checkbox" text="${name}" checked="${checked}" />
<div flex_direction="row" gap="8">
<BoolFlag key="Active:" value="${flag_active}" />
<BoolFlag key="Focused:" value="${flag_focused}" />
<BoolFlag key="IO active:" value="${flag_io_active}" />
<BoolFlag key="Overlay:" value="${flag_overlay}" />
<BoolFlag key="Primary:" value="${flag_primary}" />
<BoolFlag key="Visible:" value="${flag_visible}" />
</div>
</rectangle>
</template>
<elements>
<TabTitle translation="MONADO_RUNTIME" icon="dashboard/monado.svg" />
<div id="list_parent" flex_direction="column" gap="8">
<!-- filled at runtime -->
</div>
</elements>
</layout>

View File

@@ -14,11 +14,11 @@
<include src="../t_group_box.xml" />
<elements>
<div flex_direction="row" gap="16">
<div flex_direction="row" gap="16" flex_grow="1">
<rectangle macro="group_box" id="icon_parent" padding="16" color="#0033aa66" color2="#00000022" gradient="vertical" justify_content="center">
</rectangle>
<div flex_direction="column" gap="8" min_width="720" max_width="720">
<div flex_direction="column" gap="8" flex_grow="1">
<label id="label_title" weight="bold" size="32" overflow="hidden" />
<Subtext label_id="label_exec" overflow="hidden" />
<Separator />
@@ -61,4 +61,4 @@
</div>
</div>
</elements>
</layout>
</layout>

View File

@@ -1,6 +1,6 @@
{
"HOME_SCREEN": "Startbildschirm",
"MONADO_RUNTIME": "Monado-Laufzeitumgebung",
"MONADO_RUNTIME": "Monado-Laufzeitumgebung",
"APPLICATIONS": "Anwendungen",
"GAMES": "Spiele",
"SETTINGS": "Einstellungen",

View File

@@ -127,7 +127,7 @@
"HOME_SCREEN": "Home",
"LIST_OF_PROCESSES": "Process list",
"LIST_OF_WINDOWS": "Window list",
"MONADO_RUNTIME": "Monado runtime",
"MONADO_RUNTIME": "Monado runtime",
"NO_WINDOWS_FOUND": "No windows found",
"POPUP_ADD_DISPLAY": {
"RESOLUTION": "Resolution"

View File

@@ -1,6 +1,6 @@
{
"HOME_SCREEN": "Inicio",
"MONADO_RUNTIME": "Monado tiempo de ejecución",
"MONADO_RUNTIME": "Monado tiempo de ejecución",
"APPLICATIONS": "Aplicaciones",
"GAMES": "Juegos",
"SETTINGS": "Ajustes",

View File

@@ -50,7 +50,7 @@
"USE_SKYBOX_HELP": "Wyświetlaj niebo, jeśli nie ma aplikacji sceny lub passthrough",
"USE_PASSTHROUGH_HELP": "Pozwól na passthrough, jeśli runtime XR to obsługuje",
"SCREEN_RENDER_DOWN_HELP": "Pomaga redukować aliasing na ekranach o wysokiej rozdzielczości",
"SETS_ON_WATCH": "Lista zestawówna zegarku",
"SETS_ON_WATCH": "Lista zestawów na zegarku",
"TROUBLESHOOTING": "Rozwiązywanie problemów",
"CLEAR_SAVED_STATE": "Wyczyść zapisany stan",
"CLEAR_PIPEWIRE_TOKENS": "Wyczyść tokeny PipeWire",

View File

@@ -1,43 +1,163 @@
use std::marker::PhantomData;
use std::{collections::HashMap, marker::PhantomData, rc::Rc};
use wgui::{
assets::AssetPath,
components::checkbox::ComponentCheckbox,
globals::WguiGlobals,
layout::WidgetID,
parser::{ParseDocumentParams, ParserState},
parser::{self, Fetchable, ParseDocumentParams, ParserState},
task::Tasks,
};
use wlx_common::dash_interface;
use crate::{
frontend::Frontend,
tab::{Tab, TabType},
};
#[derive(Debug)]
enum Task {
Refresh,
FocusClient(String),
}
pub struct TabMonado<T> {
#[allow(dead_code)]
pub state: ParserState,
state: ParserState,
tasks: Tasks<Task>,
marker: PhantomData<T>,
globals: WguiGlobals,
id_list_parent: WidgetID,
cells: Vec<parser::ParserData>,
ticks: u32,
}
impl<T> Tab<T> for TabMonado<T> {
fn get_type(&self) -> TabType {
TabType::Games
}
fn update(&mut self, frontend: &mut Frontend<T>, data: &mut T) -> anyhow::Result<()> {
for task in self.tasks.drain() {
match task {
Task::Refresh => self.refresh(frontend, data)?,
Task::FocusClient(name) => self.focus_client(frontend, data, name)?,
}
}
// every few seconds
if self.ticks.is_multiple_of(500) {
self.tasks.push(Task::Refresh);
}
self.ticks += 1;
Ok(())
}
}
fn doc_params(globals: &'_ WguiGlobals) -> ParseDocumentParams<'_> {
ParseDocumentParams {
globals: globals.clone(),
path: AssetPath::BuiltIn("gui/tab/monado.xml"),
extra: Default::default(),
}
}
fn yesno(n: bool) -> &'static str {
match n {
true => "yes",
false => "no",
}
}
impl<T> TabMonado<T> {
pub fn new(frontend: &mut Frontend<T>, parent_id: WidgetID) -> anyhow::Result<Self> {
let state = wgui::parser::parse_from_assets(
&ParseDocumentParams {
globals: frontend.layout.state.globals.clone(),
path: AssetPath::BuiltIn("gui/tab/monado.xml"),
extra: Default::default(),
},
&mut frontend.layout,
parent_id,
)?;
let globals = frontend.layout.state.globals.clone();
let state = wgui::parser::parse_from_assets(&doc_params(&globals), &mut frontend.layout, parent_id)?;
let id_list_parent = state.get_widget_id("list_parent")?;
let tasks = Tasks::<Task>::new();
tasks.push(Task::Refresh);
Ok(Self {
state,
marker: PhantomData,
tasks,
globals,
id_list_parent,
ticks: 0,
cells: Vec::new(),
})
}
fn mount_client(&mut self, frontend: &mut Frontend<T>, client: &dash_interface::MonadoClient) -> anyhow::Result<()> {
let mut par = HashMap::<Rc<str>, Rc<str>>::new();
par.insert(
"checked".into(),
if client.is_primary {
Rc::from("1")
} else {
Rc::from("0")
},
);
par.insert("name".into(), client.name.clone().into());
par.insert("flag_active".into(), yesno(client.is_active).into());
par.insert("flag_focused".into(), yesno(client.is_focused).into());
par.insert("flag_io_active".into(), yesno(client.is_io_active).into());
par.insert("flag_overlay".into(), yesno(client.is_overlay).into());
par.insert("flag_primary".into(), yesno(client.is_primary).into());
par.insert("flag_visible".into(), yesno(client.is_visible).into());
let state_cell = self.state.parse_template(
&doc_params(&self.globals),
"Cell",
&mut frontend.layout,
self.id_list_parent,
par,
)?;
let checkbox = state_cell.fetch_component_as::<ComponentCheckbox>("checkbox")?;
checkbox.on_toggle({
let tasks = self.tasks.clone();
let client_name = client.name.clone();
Box::new(move |_common, e| {
if e.checked {
tasks.push(Task::FocusClient(client_name.clone()));
}
Ok(())
})
});
self.cells.push(state_cell);
Ok(())
}
fn refresh(&mut self, frontend: &mut Frontend<T>, data: &mut T) -> anyhow::Result<()> {
log::debug!("refreshing monado client list");
let clients = frontend.interface.monado_client_list(data)?;
frontend.layout.remove_children(self.id_list_parent);
self.cells.clear();
for client in clients {
self.mount_client(frontend, &client)?;
}
Ok(())
}
fn focus_client(&mut self, frontend: &mut Frontend<T>, data: &mut T, name: String) -> anyhow::Result<()> {
frontend.interface.monado_client_focus(data, &name)?;
self.tasks.push(Task::Refresh);
Ok(())
}
}

View File

@@ -129,7 +129,7 @@ pub fn stop(app_id: AppID, force_kill: bool) -> anyhow::Result<()> {
log::info!("Killing process with PID {} and its children", game.pid);
let _ = std::process::Command::new("pkill")
.arg(if force_kill { "-9" } else { "-11" })
.arg(if force_kill { "-9" } else { "-15" })
.arg("-P")
.arg(format!("{}", game.pid))
.spawn()?;