App launcher
[skip ci]
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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."
|
||||||
}
|
}
|
||||||
@@ -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",
|
||||||
|
|||||||
@@ -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}."
|
||||||
}
|
}
|
||||||
@@ -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}でアプリケーションが起動しました。"
|
||||||
}
|
}
|
||||||
@@ -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}."
|
||||||
}
|
}
|
||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>(¶ms.layout.state, "label_exec")?;
|
||||||
|
let mut label_args = state.fetch_widget_as::<WidgetLabel>(¶ms.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(¶ms.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>(¶ms.layout.state, "label_title")?;
|
let mut label_title = state.fetch_widget_as::<WidgetLabel>(¶ms.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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user