App launcher

[skip ci]
This commit is contained in:
Aleksander
2025-12-23 19:17:51 +01:00
parent 1b4c2a9006
commit 2d40b8ac00
18 changed files with 400 additions and 84 deletions

View File

@@ -2,7 +2,7 @@
<template name="Subtext"> <template name="Subtext">
<div flex_direction="row" gap="8"> <div flex_direction="row" gap="8">
<label weight="bold" text="${title}" /> <label weight="bold" text="${title}" />
<label text="foo" /> <label id="${label_id}" />
</div> </div>
</template> </template>
@@ -14,26 +14,25 @@
<include src="../t_group_box.xml" /> <include src="../t_group_box.xml" />
<elements> <elements>
<div flex_direction="row" gap="16" width="100%"> <div flex_direction="row" gap="16">
<rectangle macro="group_box" id="icon_parent" height="100%" padding="8" color="#0033aa66" color2="#00000022" gradient="vertical" justify_content="center"> <rectangle macro="group_box" id="icon_parent" padding="8" color="#0033aa66" color2="#00000022" gradient="vertical" justify_content="center">
</rectangle> </rectangle>
<div flex_direction="column" gap="8" width="100%" align_items="baseline"> <div flex_direction="column" gap="8">
<label id="label_title" weight="bold" size="32" /> <label id="label_title" weight="bold" size="32" />
<Subtext title="Exec:" /> <Subtext title="Exec:" label_id="label_exec" />
<Subtext title="Args:" /> <Subtext title="Args:" label_id="label_args" />
<Separator /> <Separator />
<CheckBox text="Run in X11 mode (cage)" /> <CheckBox id="cb_cage_mode" text="Run in X11 mode (cage)" />
<CheckBox text="Run in Wayland mode" checked="1" /> <CheckBox id="cb_wayland_mode" text="Run in Wayland mode" checked="1" />
<Separator /> <Separator />
<Button color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_right="12"> <Button align_self="baseline" color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_right="12" min_height="40">
<sprite src_builtin="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 (todo)" weight="bold" size="17" shadow="#00000099" />
</Button> </Button>
<Separator /> <Separator />
<rectangle macro="group_box">
<label size="16" weight="bold" text="Or launch it detached" /> <label size="16" weight="bold" text="Or launch it detached" />
</rectangle> <div id="display_list_parent" />
</div> </div>
</div> </div>
</elements> </elements>

View File

@@ -34,7 +34,7 @@
</rectangle> </rectangle>
<!-- Content --> <!-- Content -->
<rectangle width="100%" height="100%" <rectangle height="100%"
color="#010310ee" color="#010310ee"
color2="#062a5eee" color2="#062a5eee"
gradient="vertical" gradient="vertical"

View File

@@ -3,7 +3,7 @@
<elements> <elements>
<rectangle macro="group_box" flex_direction="row" align_items="center"> <rectangle macro="group_box" flex_direction="row" align_items="center">
<div id="list_parent" gap="8" flex_direction="row" flex_wrap="wrap" flex_grow="1" /> <div id="list_parent" gap="8" flex_direction="column" flex_wrap="wrap" flex_grow="1" />
</rectangle> </rectangle>
</elements> </elements>
</layout> </layout>

View File

@@ -62,6 +62,9 @@
"DISPLAY_OPTIONS": "Anzeigeeinstellungen", "DISPLAY_OPTIONS": "Anzeigeeinstellungen",
"PROCESS_LIST": { "PROCESS_LIST": {
"NO_PROCESSES_FOUND": "Keine Prozesse gefunden", "NO_PROCESSES_FOUND": "Keine Prozesse gefunden",
"LOCATED_ON": "auf" "LOCATED_ON": "auf",
} "TERMINATE_PROCESS_NAMED_X": "Prozess \"{PROCESS_NAME}\" beenden"
},
"FAILED_TO_LAUNCH_APPLICATION": "Fehler beim Starten der Anwendung:",
"APPLICATION_LAUNCHED_ON": "Anwendung wurde auf {DISPLAY_NAME} gestartet."
} }

View File

@@ -22,6 +22,7 @@
}, },
"WLX_OVERLAY_S_SETTINGS": "WlxOverlay-S settings" "WLX_OVERLAY_S_SETTINGS": "WlxOverlay-S settings"
}, },
"APPLICATION_LAUNCHED_ON": "Application launched on {DISPLAY_NAME}.",
"APPLICATION_LAUNCHER": "Application launcher", "APPLICATION_LAUNCHER": "Application launcher",
"APPLICATIONS": "Applications", "APPLICATIONS": "Applications",
"AUDIO": { "AUDIO": {
@@ -41,6 +42,7 @@
}, },
"DISPLAY_OPTIONS": "Display options", "DISPLAY_OPTIONS": "Display options",
"DISPLAY_PORTRAIT_MODE": "Portrait mode", "DISPLAY_PORTRAIT_MODE": "Portrait mode",
"FAILED_TO_LAUNCH_APPLICATION": "Failed to launcha application:",
"GAMES": "Games", "GAMES": "Games",
"GENERAL_SETTINGS": "General settings", "GENERAL_SETTINGS": "General settings",
"HEIGHT": "Height", "HEIGHT": "Height",

View File

@@ -62,6 +62,9 @@
"DISPLAY_OPTIONS": "Opciones de pantalla", "DISPLAY_OPTIONS": "Opciones de pantalla",
"PROCESS_LIST": { "PROCESS_LIST": {
"NO_PROCESSES_FOUND": "No se encontraron procesos", "NO_PROCESSES_FOUND": "No se encontraron procesos",
"LOCATED_ON": "en" "LOCATED_ON": "en",
} "TERMINATE_PROCESS_NAMED_X": "Terminar proceso \"{PROCESS_NAME}\""
},
"FAILED_TO_LAUNCH_APPLICATION": "No se pudo iniciar la aplicación:",
"APPLICATION_LAUNCHED_ON": "Aplicación iniciada en {DISPLAY_NAME}."
} }

View File

@@ -62,6 +62,9 @@
"DISPLAY_OPTIONS": "表示オプション", "DISPLAY_OPTIONS": "表示オプション",
"PROCESS_LIST": { "PROCESS_LIST": {
"NO_PROCESSES_FOUND": "プロセスが見つかりませんでした", "NO_PROCESSES_FOUND": "プロセスが見つかりませんでした",
"LOCATED_ON": "に" "LOCATED_ON": "に",
} "TERMINATE_PROCESS_NAMED_X": "プロセス \"{PROCESS_NAME}\" を終了します"
},
"FAILED_TO_LAUNCH_APPLICATION": "アプリケーションの起動に失敗しました:",
"APPLICATION_LAUNCHED_ON": "{DISPLAY_NAME}でアプリケーションが起動しました。"
} }

View File

@@ -62,6 +62,9 @@
"PROCESSES": "Procesy", "PROCESSES": "Procesy",
"PROCESS_LIST": { "PROCESS_LIST": {
"NO_PROCESSES_FOUND": "Nie znaleziono procesów", "NO_PROCESSES_FOUND": "Nie znaleziono procesów",
"LOCATED_ON": "na" "LOCATED_ON": "na",
} "TERMINATE_PROCESS_NAMED_X": "Zakończ proces \"{PROCESS_NAME}\""
},
"FAILED_TO_LAUNCH_APPLICATION": "Nie udało się uruchomić aplikacji:",
"APPLICATION_LAUNCHED_ON": "Aplikacja uruchomiona na {DISPLAY_NAME}."
} }

View File

@@ -232,9 +232,13 @@ impl Frontend {
fn mount_popup(&mut self, params: MountPopupParams) -> anyhow::Result<()> { fn mount_popup(&mut self, params: MountPopupParams) -> anyhow::Result<()> {
let mut layout = self.layout.borrow_mut(); let mut layout = self.layout.borrow_mut();
self self.popup_manager.mount_popup(
.popup_manager self.globals.clone(),
.mount_popup(self.globals.clone(), &mut layout, self.tasks.clone(), params)?; self.settings.as_ref(),
&mut layout,
self.tasks.clone(),
params,
)?;
Ok(()) Ok(())
} }

View File

@@ -10,8 +10,9 @@ use wgui::{
}; };
use crate::{ use crate::{
frontend::{FrontendTask, RcFrontend}, frontend::{FrontendTask, FrontendTasks, RcFrontend},
tab::{Tab, TabParams, TabType}, tab::{Tab, TabParams, TabType},
task::Tasks,
util::{ util::{
self, self,
desktop_finder::DesktopEntry, desktop_finder::DesktopEntry,
@@ -20,6 +21,10 @@ use crate::{
views::{self, app_launcher}, views::{self, app_launcher},
}; };
enum Task {
CloseLauncher,
}
struct State { struct State {
launcher: Option<(PopupHandle, views::app_launcher::View)>, launcher: Option<(PopupHandle, views::app_launcher::View)>,
} }
@@ -35,12 +40,29 @@ pub struct TabApps {
entries: Vec<DesktopEntry>, entries: Vec<DesktopEntry>,
#[allow(dead_code)] #[allow(dead_code)]
app_list: AppList, app_list: AppList,
tasks: Tasks<Task>,
} }
impl Tab for TabApps { impl Tab for TabApps {
fn get_type(&self) -> TabType { fn get_type(&self) -> TabType {
TabType::Apps TabType::Apps
} }
fn update(&mut self, params: super::TabUpdateParams) -> anyhow::Result<()> {
let mut state = self.state.borrow_mut();
for task in self.tasks.drain() {
match task {
Task::CloseLauncher => state.launcher = None,
}
}
if let Some((_, launcher)) = &mut state.launcher {
launcher.update(params.layout, params.interface)?;
}
Ok(())
}
} }
#[derive(Default)] #[derive(Default)]
@@ -51,9 +73,11 @@ struct AppList {
// called after the user clicks any desktop entry // called after the user clicks any desktop entry
fn on_app_click( fn on_app_click(
frontend: RcFrontend, frontend: RcFrontend,
frontend_tasks: FrontendTasks,
globals: WguiGlobals, globals: WguiGlobals,
entry: DesktopEntry, entry: DesktopEntry,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
tasks: Tasks<Task>,
) -> ButtonClickCallback { ) -> ButtonClickCallback {
Box::new(move |_common, _evt| { Box::new(move |_common, _evt| {
frontend frontend
@@ -62,15 +86,27 @@ fn on_app_click(
.push(FrontendTask::MountPopup(MountPopupParams { .push(FrontendTask::MountPopup(MountPopupParams {
title: Translation::from_raw_text(&entry.app_name), title: Translation::from_raw_text(&entry.app_name),
on_content: { on_content: {
// this is awful
let state = state.clone(); let state = state.clone();
let entry = entry.clone(); let entry = entry.clone();
let globals = globals.clone(); let globals = globals.clone();
let frontend_tasks = frontend_tasks.clone();
let tasks = tasks.clone();
Rc::new(move |data| { Rc::new(move |data| {
let on_launched = {
let tasks = tasks.clone();
Box::new(move || tasks.push(Task::CloseLauncher))
};
let view = app_launcher::View::new(app_launcher::Params { let view = app_launcher::View::new(app_launcher::Params {
entry: entry.clone(), entry: entry.clone(),
globals: globals.clone(), globals: &globals,
layout: data.layout, layout: data.layout,
parent_id: data.id_content, parent_id: data.id_content,
frontend_tasks: &frontend_tasks,
settings: data.settings,
on_launched,
})?; })?;
state.borrow_mut().launcher = Some((data.handle, view)); state.borrow_mut().launcher = Some((data.handle, view));
@@ -93,9 +129,11 @@ impl TabApps {
gtk::init()?; gtk::init()?;
let entries = util::desktop_finder::find_entries()?; let entries = util::desktop_finder::find_entries()?;
let frontend_tasks = tab_params.frontend_tasks.clone();
let frontend = tab_params.frontend.clone(); let frontend = tab_params.frontend.clone();
let globals = tab_params.globals.clone(); let globals = tab_params.globals.clone();
let tasks = Tasks::new();
let state = Rc::new(RefCell::new(State { launcher: None })); let state = Rc::new(RefCell::new(State { launcher: None }));
let mut parser_state = wgui::parser::parse_from_assets(doc_params, tab_params.layout, tab_params.parent_id)?; let mut parser_state = wgui::parser::parse_from_assets(doc_params, tab_params.layout, tab_params.parent_id)?;
@@ -111,9 +149,11 @@ impl TabApps {
// Set up the click handler for the app button // Set up the click handler for the app button
button.on_click(on_app_click( button.on_click(on_app_click(
frontend.clone(), frontend.clone(),
frontend_tasks.clone(),
globals.clone(), globals.clone(),
entry.clone(), entry.clone(),
state.clone(), state.clone(),
tasks.clone(),
)); ));
}, },
)?; )?;
@@ -123,6 +163,7 @@ impl TabApps {
parser_state, parser_state,
entries, entries,
state, state,
tasks,
}) })
} }
} }

View File

@@ -44,8 +44,9 @@ impl TabProcesses {
view_display_list: display_list::View::new(display_list::Params { view_display_list: display_list::View::new(display_list::Params {
layout: params.layout, layout: params.layout,
parent_id: state.get_widget_id("display_list_parent")?, parent_id: state.get_widget_id("display_list_parent")?,
globals: params.globals.clone(), globals: params.globals,
frontend_tasks: params.frontend_tasks.clone(), frontend_tasks: params.frontend_tasks.clone(),
on_click: None,
})?, })?,
view_process_list: process_list::View::new(process_list::Params { view_process_list: process_list::View::new(process_list::Params {
layout: params.layout, layout: params.layout,

View File

@@ -3,6 +3,7 @@ use gtk::traits::IconThemeExt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
// compatibility with wayvr-ipc // compatibility with wayvr-ipc
// TODO: remove this after we're done with the old wayvr-dashboard and use DesktopEntry instead
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct DesktopFile { pub struct DesktopFile {
pub name: String, pub name: String,
@@ -13,7 +14,6 @@ pub struct DesktopFile {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(dead_code)] // TODO: remove this
pub struct DesktopEntry { pub struct DesktopEntry {
pub exec_path: String, pub exec_path: String,
pub exec_args: Vec<String>, pub exec_args: Vec<String>,
@@ -140,3 +140,15 @@ pub fn find_entries() -> anyhow::Result<Vec<DesktopEntry>> {
Ok(res) Ok(res)
} }
impl DesktopEntry {
pub fn to_desktop_file(&self) -> DesktopFile {
DesktopFile {
categories: self.categories.clone(),
exec_args: self.exec_args.clone(),
exec_path: self.exec_path.clone(),
icon: self.icon_path.clone(),
name: self.app_name.clone(),
}
}
}

View File

@@ -15,7 +15,10 @@ use wgui::{
widget::label::WidgetLabel, widget::label::WidgetLabel,
}; };
use crate::frontend::{FrontendTask, FrontendTasks}; use crate::{
frontend::{FrontendTask, FrontendTasks},
settings::SettingsIO,
};
pub struct PopupManagerParams { pub struct PopupManagerParams {
pub parent_id: WidgetID, pub parent_id: WidgetID,
@@ -55,6 +58,7 @@ pub struct PopupManager {
pub struct PopupContentFuncData<'a> { pub struct PopupContentFuncData<'a> {
pub layout: &'a mut Layout, pub layout: &'a mut Layout,
pub settings: &'a dyn SettingsIO,
pub handle: PopupHandle, pub handle: PopupHandle,
pub id_content: WidgetID, pub id_content: WidgetID,
} }
@@ -119,6 +123,7 @@ impl PopupManager {
pub fn mount_popup( pub fn mount_popup(
&mut self, &mut self,
globals: WguiGlobals, globals: WguiGlobals,
settings: &dyn SettingsIO,
layout: &mut Layout, layout: &mut Layout,
frontend_tasks: FrontendTasks, frontend_tasks: FrontendTasks,
params: MountPopupParams, params: MountPopupParams,
@@ -175,6 +180,7 @@ impl PopupManager {
layout, layout,
handle: popup_handle.clone(), handle: popup_handle.clone(),
id_content, id_content,
settings,
})?; })?;
Ok(()) Ok(())

View File

@@ -130,7 +130,7 @@ impl ToastManager {
// show-up animation // show-up animation
layout.animations.add(Animation::new( layout.animations.add(Animation::new(
rect.id, rect.id,
160, // does not use anim_mult (120.0 * globals.defaults.animation_mult) as u32,
AnimationEasing::Linear, AnimationEasing::Linear,
Box::new(move |common, data| { Box::new(move |common, data| {
let pos_showup = AnimationEasing::OutQuint.interpolate((data.pos * 4.0).min(1.0)); let pos_showup = AnimationEasing::OutQuint.interpolate((data.pos * 4.0).min(1.0));
@@ -138,7 +138,7 @@ impl ToastManager {
let scale = AnimationEasing::OutBack.interpolate((data.pos * 4.0).min(1.0)); let scale = AnimationEasing::OutBack.interpolate((data.pos * 4.0).min(1.0));
{ {
let mtx = Mat4::from_translation(Vec3::new(0.0, (1.0 - pos_showup) * 100.0, 0.0)) let mtx = Mat4::from_translation(Vec3::new(0.0, (1.0 - pos_showup) * 20.0, 0.0))
* Mat4::from_scale(Vec3::new(scale, scale, 1.0)); * Mat4::from_scale(Vec3::new(scale, scale, 1.0));
data.data.transform = centered_matrix(data.widget_boundary.size, &mtx); data.data.transform = centered_matrix(data.widget_boundary.size, &mtx);
} }

View File

@@ -1,27 +1,71 @@
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc};
use anyhow::Context;
use wayvr_ipc::{packet_client::WvrProcessLaunchParams, packet_server::WvrDisplayHandle};
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
components::checkbox::ComponentCheckbox,
globals::WguiGlobals, globals::WguiGlobals,
i18n::Translation, i18n::Translation,
layout::{Layout, WidgetID}, layout::{Layout, WidgetID},
parser::{Fetchable, ParseDocumentParams, ParserState}, parser::{Fetchable, ParseDocumentParams, ParserState},
widget::label::WidgetLabel, widget::label::WidgetLabel,
}; };
use wlx_common::dash_interface::BoxDashInterface;
use crate::util::desktop_finder::DesktopEntry; use crate::{
frontend::{FrontendTask, FrontendTasks},
settings::SettingsIO,
task::Tasks,
util::desktop_finder::DesktopEntry,
views::display_list,
};
#[derive(Clone, Eq, PartialEq)]
enum RunMode {
Cage,
Wayland,
}
enum Task {
SetRunMode(RunMode),
DisplayClick(WvrDisplayHandle),
}
struct LaunchParams<'a> {
display_handle: WvrDisplayHandle,
application: &'a DesktopEntry,
run_mode: RunMode,
globals: &'a WguiGlobals,
frontend_tasks: &'a FrontendTasks,
interface: &'a mut BoxDashInterface,
on_launched: &'a dyn Fn(),
}
pub struct View { pub struct View {
#[allow(dead_code)] #[allow(dead_code)]
pub state: ParserState, state: ParserState,
//entry: DesktopEntry, entry: DesktopEntry,
view_display_list: display_list::View,
tasks: Tasks<Task>,
frontend_tasks: FrontendTasks,
globals: WguiGlobals,
cb_cage_mode: Rc<ComponentCheckbox>,
cb_wayland_mode: Rc<ComponentCheckbox>,
run_mode: RunMode,
on_launched: Box<dyn Fn()>,
} }
pub struct Params<'a> { pub struct Params<'a> {
pub globals: WguiGlobals, pub globals: &'a WguiGlobals,
pub entry: DesktopEntry, pub entry: DesktopEntry,
pub layout: &'a mut Layout, pub layout: &'a mut Layout,
pub parent_id: WidgetID, pub parent_id: WidgetID,
pub settings: &'a dyn SettingsIO,
pub frontend_tasks: &'a FrontendTasks,
pub on_launched: Box<dyn Fn()>,
} }
impl View { impl View {
@@ -33,6 +77,44 @@ impl View {
}; };
let mut state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?; let mut state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?;
let cb_cage_mode = state.fetch_component_as::<ComponentCheckbox>("cb_cage_mode")?;
let cb_wayland_mode = state.fetch_component_as::<ComponentCheckbox>("cb_wayland_mode")?;
{
let mut label_exec = state.fetch_widget_as::<WidgetLabel>(&params.layout.state, "label_exec")?;
let mut label_args = state.fetch_widget_as::<WidgetLabel>(&params.layout.state, "label_args")?;
label_exec.set_text_simple(
&mut params.globals.get(),
Translation::from_raw_text_string(params.entry.app_name.clone()),
);
label_args.set_text_simple(
&mut params.globals.get(),
Translation::from_raw_text_string(params.entry.exec_args.join(" ")),
);
}
let display_list_parent = state.fetch_widget(&params.layout.state, "display_list_parent")?.id;
let tasks = Tasks::new();
let on_display_click = {
let tasks = tasks.clone();
Box::new(move |disp_handle: WvrDisplayHandle| {
tasks.push(Task::DisplayClick(disp_handle));
})
};
let view_display_list = display_list::View::new(display_list::Params {
frontend_tasks: params.frontend_tasks.clone(),
globals: params.globals,
layout: params.layout,
parent_id: display_list_parent,
on_click: Some(on_display_click),
})?;
let id_icon_parent = state.get_widget_id("icon_parent")?; let id_icon_parent = state.get_widget_id("icon_parent")?;
// app icon // app icon
@@ -48,6 +130,30 @@ impl View {
)?; )?;
} }
let run_mode = if params.settings.get().tweaks.xwayland_by_default {
RunMode::Cage
} else {
RunMode::Wayland
};
tasks.push(Task::SetRunMode(run_mode.clone()));
cb_cage_mode.on_toggle({
let tasks = tasks.clone();
Box::new(move |_, _| {
tasks.push(Task::SetRunMode(RunMode::Cage));
Ok(())
})
});
cb_wayland_mode.on_toggle({
let tasks = tasks.clone();
Box::new(move |_, _| {
tasks.push(Task::SetRunMode(RunMode::Wayland));
Ok(())
})
});
let mut label_title = state.fetch_widget_as::<WidgetLabel>(&params.layout.state, "label_title")?; let mut label_title = state.fetch_widget_as::<WidgetLabel>(&params.layout.state, "label_title")?;
label_title.set_text_simple( label_title.set_text_simple(
@@ -56,8 +162,139 @@ impl View {
); );
Ok(Self { Ok(Self {
//entry: params.entry,
state, state,
view_display_list,
tasks,
cb_cage_mode,
cb_wayland_mode,
run_mode,
entry: params.entry,
frontend_tasks: params.frontend_tasks.clone(),
globals: params.globals.clone(),
on_launched: params.on_launched,
}) })
} }
pub fn update(&mut self, layout: &mut Layout, interface: &mut BoxDashInterface) -> anyhow::Result<()> {
loop {
let tasks = self.tasks.drain();
if tasks.is_empty() {
break;
}
for task in tasks {
match task {
Task::SetRunMode(run_mode) => self.action_set_run_mode(layout, run_mode)?,
Task::DisplayClick(disp_handle) => self.action_display_click(disp_handle, interface),
}
}
}
self.view_display_list.update(layout, interface)?;
Ok(())
}
fn action_set_run_mode(&mut self, layout: &mut Layout, run_mode: RunMode) -> anyhow::Result<()> {
let (n1, n2) = match run_mode {
RunMode::Cage => (true, false),
RunMode::Wayland => (false, true),
};
let mut c = layout.start_common();
self.cb_cage_mode.set_checked(&mut c.common(), n1);
self.cb_wayland_mode.set_checked(&mut c.common(), n2);
c.finish()?;
Ok(())
}
fn action_display_click(&mut self, handle: WvrDisplayHandle, interface: &mut BoxDashInterface) {
View::try_launch(LaunchParams {
application: &self.entry,
display_handle: handle,
frontend_tasks: &self.frontend_tasks,
globals: &self.globals,
run_mode: self.run_mode.clone(),
interface,
on_launched: &self.on_launched,
});
}
fn try_launch(params: LaunchParams) {
let globals = params.globals.clone();
let frontend_tasks = params.frontend_tasks.clone();
// launch app itself
let Err(e) = View::launch(params) else { return };
let str_failed = globals.i18n().translate("FAILED_TO_LAUNCH_APPLICATION");
frontend_tasks.push(FrontendTask::PushToast(Translation::from_raw_text_string(format!(
"{} {:?}",
str_failed, e
))));
}
fn launch(params: LaunchParams) -> anyhow::Result<()> {
let mut env = Vec::<String>::new();
if params.run_mode == RunMode::Wayland {
// This list could be larger, feel free to expand it
env.push("QT_QPA_PLATFORM=wayland".into());
env.push("GDK_BACKEND=wayland".into());
env.push("SDL_VIDEODRIVER=wayland".into());
env.push("XDG_SESSION_TYPE=wayland".into());
env.push("ELECTRON_OZONE_PLATFORM_HINT=wayland".into());
}
// TODO: refactor this after we ditch old wayvr-dashboard completely
let desktop_file = params.application.to_desktop_file();
let mut userdata = HashMap::<String, String>::new();
userdata.insert("desktop_file".into(), serde_json::to_string(&desktop_file)?);
let exec_args_str = desktop_file.exec_args.join(" ");
params
.interface
.display_set_visible(params.display_handle.clone(), true)?;
let args = match params.run_mode {
RunMode::Cage => format!("-- {} {}", desktop_file.exec_path, exec_args_str),
RunMode::Wayland => exec_args_str,
};
let exec = match params.run_mode {
RunMode::Cage => "cage",
RunMode::Wayland => &desktop_file.name,
};
let display = params
.interface
.display_get(params.display_handle.clone())
.context("Display not found")?;
params.interface.process_launch(WvrProcessLaunchParams {
env,
exec: String::from(exec),
name: desktop_file.name,
target_display: params.display_handle,
args,
userdata,
})?;
let str_launched_on = params
.globals
.i18n()
.translate_and_replace("APPLICATION_LAUNCHED_ON", ("{DISPLAY_NAME}", &display.name));
params
.frontend_tasks
.push(FrontendTask::PushToast(Translation::from_raw_text_string(
str_launched_on,
)));
(*params.on_launched)();
// we're done!
Ok(())
}
} }

View File

@@ -2,7 +2,7 @@ use std::{cell::RefCell, rc::Rc};
use wayvr_ipc::{ use wayvr_ipc::{
packet_client::{self}, packet_client::{self},
packet_server::{self}, packet_server::{self, WvrDisplayHandle},
}; };
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
@@ -31,16 +31,17 @@ use crate::{
enum Task { enum Task {
AddDisplay, AddDisplay,
AddDisplayFinish(add_display::Result), AddDisplayFinish(add_display::Result),
DisplayOptions(packet_server::WvrDisplay), DisplayClicked(packet_server::WvrDisplay),
DisplayOptionsFinish, DisplayOptionsFinish,
Refresh, Refresh,
} }
pub struct Params<'a> { pub struct Params<'a> {
pub globals: WguiGlobals, pub globals: &'a WguiGlobals,
pub frontend_tasks: FrontendTasks, pub frontend_tasks: FrontendTasks,
pub layout: &'a mut Layout, pub layout: &'a mut Layout,
pub parent_id: WidgetID, pub parent_id: WidgetID,
pub on_click: Option<Box<dyn Fn(WvrDisplayHandle)>>,
} }
struct State { struct State {
@@ -56,6 +57,7 @@ pub struct View {
globals: WguiGlobals, globals: WguiGlobals,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
id_list_parent: WidgetID, id_list_parent: WidgetID,
on_click: Option<Box<dyn Fn(WvrDisplayHandle)>>,
} }
impl View { impl View {
@@ -84,9 +86,10 @@ impl View {
parser_state, parser_state,
tasks, tasks,
frontend_tasks: params.frontend_tasks, frontend_tasks: params.frontend_tasks,
globals: params.globals, globals: params.globals.clone(),
state, state,
id_list_parent: list_parent.id, id_list_parent: list_parent.id,
on_click: params.on_click,
}) })
} }
@@ -98,11 +101,11 @@ impl View {
} }
for task in tasks { for task in tasks {
match task { match task {
Task::AddDisplay => self.add_display(), Task::AddDisplay => self.action_add_display(),
Task::AddDisplayFinish(result) => self.add_display_finish(interface, result)?, Task::AddDisplayFinish(result) => self.action_add_display_finish(interface, result)?,
Task::DisplayOptionsFinish => self.display_options_finish(), Task::DisplayOptionsFinish => self.action_display_options_finish(),
Task::Refresh => self.refresh(layout, interface)?, Task::Refresh => self.refresh(layout, interface)?,
Task::DisplayOptions(display) => self.display_options(display)?, Task::DisplayClicked(display) => self.action_display_clicked(display)?,
} }
} }
} }
@@ -189,7 +192,7 @@ fn fill_display_list(
button.on_click({ button.on_click({
let tasks = tasks.clone(); let tasks = tasks.clone();
Box::new(move |_, _| { Box::new(move |_, _| {
tasks.push(Task::DisplayOptions(entry.clone())); tasks.push(Task::DisplayClicked(entry.clone()));
Ok(()) Ok(())
}) })
}); });
@@ -199,7 +202,7 @@ fn fill_display_list(
} }
impl View { impl View {
fn add_display(&mut self) { fn action_add_display(&mut self) {
self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams { self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
title: Translation::from_translation_key("ADD_DISPLAY"), title: Translation::from_translation_key("ADD_DISPLAY"),
on_content: { on_content: {
@@ -230,7 +233,7 @@ impl View {
})); }));
} }
fn add_display_finish( fn action_add_display_finish(
&mut self, &mut self,
interface: &mut BoxDashInterface, interface: &mut BoxDashInterface,
result: add_display::Result, result: add_display::Result,
@@ -246,7 +249,7 @@ impl View {
Ok(()) Ok(())
} }
fn display_options_finish(&mut self) { fn action_display_options_finish(&mut self) {
self.state.borrow_mut().view_display_options = None; self.state.borrow_mut().view_display_options = None;
self.tasks.push(Task::Refresh); self.tasks.push(Task::Refresh);
} }
@@ -291,7 +294,10 @@ impl View {
Ok(()) Ok(())
} }
fn display_options(&mut self, display: packet_server::WvrDisplay) -> anyhow::Result<()> { fn action_display_clicked(&mut self, display: packet_server::WvrDisplay) -> anyhow::Result<()> {
if let Some(on_click) = &mut self.on_click {
(*on_click)(display.handle);
} else {
self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams { self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
title: Translation::from_translation_key("DISPLAY_OPTIONS"), title: Translation::from_translation_key("DISPLAY_OPTIONS"),
on_content: { on_content: {
@@ -316,6 +322,7 @@ impl View {
}) })
}, },
})); }));
}
Ok(()) Ok(())
} }

View File

@@ -82,7 +82,7 @@ impl View {
for task in tasks { for task in tasks {
match task { match task {
Task::Refresh => self.refresh(layout, interface)?, Task::Refresh => self.refresh(layout, interface)?,
Task::TerminateProcess(process) => self.terminate_process(interface, process)?, Task::TerminateProcess(process) => self.action_terminate_process(interface, process)?,
} }
} }
} }
@@ -100,7 +100,7 @@ fn get_desktop_file_from_process(
continue; continue;
} }
// TODO: refactor this after we ditch wayvr-ipc completely // TODO: refactor this after we ditch old wayvr-dashboard completely
let Some(dfile_str) = process.userdata.get("desktop_file") else { let Some(dfile_str) = process.userdata.get("desktop_file") else {
continue; continue;
}; };
@@ -294,7 +294,7 @@ impl View {
Ok(()) Ok(())
} }
fn terminate_process( fn action_terminate_process(
&mut self, &mut self,
interface: &mut BoxDashInterface, interface: &mut BoxDashInterface,
process: packet_server::WvrProcess, process: packet_server::WvrProcess,

View File

@@ -205,11 +205,7 @@ impl EventResult {
#[must_use] #[must_use]
pub fn merge(self, other: Self) -> Self { pub fn merge(self, other: Self) -> Self {
if self > other { if self > other { self } else { other }
self
} else {
other
}
} }
} }
@@ -349,7 +345,6 @@ impl WidgetState {
if (scrolling_cur.x - scrolling_target.x).abs() < epsilon if (scrolling_cur.x - scrolling_target.x).abs() < epsilon
&& (scrolling_cur.y - scrolling_target.y).abs() < epsilon && (scrolling_cur.y - scrolling_target.y).abs() < epsilon
{ {
log::info!("stopped animating");
*scrolling_cur = *scrolling_target; *scrolling_cur = *scrolling_target;
} }
} }