bar app icons & tooltips
This commit is contained in:
@@ -88,14 +88,14 @@
|
||||
|
||||
<!-- An app with a single icon. -->
|
||||
<template name="App">
|
||||
<Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" tooltip="${name}" tooltip_side="bottom">
|
||||
<sprite width="56" height="56" color="~text_color" src_ext="${src}" />
|
||||
<Button macro="button_style" id="overlay_${idx}" _short_release="::OverlayToggle ${name}" tooltip="OVERLAY_TOOLTIP.APP;${name}" tooltip_side="bottom">
|
||||
<sprite width="56" height="56" color="~text_color" src_ext="${icon}" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<!-- A screen with a shortened connector name, e.g. "H1" for HDMI-A-1 or "D2" for DP-2 -->
|
||||
<template name="Screen">
|
||||
<Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" tooltip="${name}" tooltip_side="bottom">
|
||||
<Button macro="button_style" id="overlay_${idx}" _short_release="::OverlayToggle ${name}" tooltip="OVERLAY_TOOLTIP.SCREEN;${name}" tooltip_side="bottom">
|
||||
<sprite width="56" height="56" color="~text_color" src_builtin="edit/screen.svg" />
|
||||
<div position="absolute" margin_top="-10">
|
||||
<label text="${display}" size="26" color="~color_faded_20" weight="bold" />
|
||||
@@ -104,13 +104,13 @@
|
||||
</template>
|
||||
|
||||
<template name="Panel">
|
||||
<Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" tooltip="${name}" tooltip_side="bottom">
|
||||
<Button macro="button_style" id="overlay_${idx}" _short_release="::OverlayToggle ${name}" tooltip="OVERLAY_TOOLTIP.PANEL;${name}" tooltip_side="bottom">
|
||||
<sprite width="56" height="56" color="~text_color" src_builtin="edit/panel.svg" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<template name="Mirror">
|
||||
<Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" tooltip="${name}" tooltip_side="bottom">
|
||||
<Button macro="button_style" id="overlay_${idx}" _short_release="::OverlayToggle ${name}" tooltip="OVERLAY_TOOLTIP.MIRROR;${name}" tooltip_side="bottom">
|
||||
<sprite width="56" height="56" color="~text_color" src_builtin="edit/mirror.svg" />
|
||||
<div position="absolute" margin_top="7" margin_left="20">
|
||||
<label text="${display}" size="26" color="~color_faded_20" weight="bold" />
|
||||
@@ -119,7 +119,7 @@
|
||||
</template>
|
||||
|
||||
<template name="Set">
|
||||
<Button macro="button_style" id="set_${idx}" _press="::SetSwitch ${idx}" tooltip="WATCH.SWITCH_TO_SET" tooltip_side="bottom">
|
||||
<Button macro="button_style" id="set_${idx}" _release="::SetSwitch ${idx}" tooltip="WATCH.SWITCH_TO_SET" tooltip_side="bottom">
|
||||
<sprite width="56" height="56" color="~text_color" src_builtin="watch/set2.svg" />
|
||||
<div position="absolute" margin_top="16" margin_left="-8">
|
||||
<label text="${display}" size="26" color="~color_faded_20" weight="bold" />
|
||||
@@ -139,9 +139,9 @@
|
||||
</div>
|
||||
<VerticalSeparator />
|
||||
<div id="apps_root" gap="8">
|
||||
<App id="test1" name="Blender" src="/usr/share/icons/hicolor/scalable/apps/blender-5.0.svg" />
|
||||
<App id="test2" name="Inkscape" src="/usr/share/icons/hicolor/scalable/apps/org.inkscape.Inkscape.svg" />
|
||||
<App id="test3" name="GIMP" src="/usr/share/icons/hicolor/scalable/apps/gimp.svg" />
|
||||
<App id="test1" name="Blender" icon="/usr/share/icons/hicolor/scalable/apps/blender-5.0.svg" />
|
||||
<App id="test2" name="Inkscape" icon="/usr/share/icons/hicolor/scalable/apps/org.inkscape.Inkscape.svg" />
|
||||
<App id="test3" name="GIMP" icon="/usr/share/icons/hicolor/scalable/apps/gimp.svg" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="tray_root" flex_direction="row" gap="16">
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
},
|
||||
"DEFAULT": "Default",
|
||||
"DISABLED": "Disabled",
|
||||
"OVERLAY_TOOLTIP": {
|
||||
"SCREEN": "Screen {}",
|
||||
"MIRROR": "Mirror {}",
|
||||
"PANEL": "Panel {}",
|
||||
"APP": "{}"
|
||||
},
|
||||
"EDIT_MODE": {
|
||||
"ADJUST_CURVATURE": "Adjust curvature",
|
||||
"ALPHA_BLEND_MODE": "Alpha blend mode",
|
||||
|
||||
@@ -19,6 +19,7 @@ use smithay::{
|
||||
shm::ShmState,
|
||||
}
|
||||
};
|
||||
use wlx_common::desktop_finder::DesktopFinder;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, HashSet},
|
||||
@@ -301,23 +302,57 @@ impl WvrServerState {
|
||||
continue;
|
||||
};
|
||||
|
||||
let [size_x, size_y] = match wvr_server.processes.get(&process_handle) {
|
||||
Some(Process::Managed(p)) => p.resolution,
|
||||
_ => [1920, 1080],
|
||||
// Size, icon & fallback title comes from process
|
||||
let ([size_x, size_y], fallback_title, icon, is_cage) = match wvr_server.processes.get(&process_handle) {
|
||||
Some(Process::Managed(p)) => (p.resolution, Some(p.app_name.clone()), p.icon.as_ref().cloned(), p.exec_path.ends_with("cage")),
|
||||
_ => ([1920, 1080], None, None, false)
|
||||
};
|
||||
|
||||
let window_handle = wvr_server
|
||||
.wm
|
||||
.create_window(toplevel.clone(), process_handle, size_x, size_y);
|
||||
|
||||
let title: Arc<str> = with_states(toplevel.wl_surface(), |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.and_then(|t| t.lock().unwrap().title.clone())
|
||||
.map_or_else(|| format!("P{}", client.pid).into(), String::into)
|
||||
});
|
||||
let mut title: Arc<str> = fallback_title.unwrap_or_else(|| format!("P{}", client.pid)).into();
|
||||
let mut icon = icon;
|
||||
|
||||
// Try to get title from xdg_toplevel, unless it's running in cage
|
||||
if !is_cage {
|
||||
let mut needs_title = true;
|
||||
let (xdg_title, app_id): (Option<String>, Option<String>) = with_states(toplevel.wl_surface(), |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.map(|t| {
|
||||
let t = t.lock().unwrap();
|
||||
(t.title.clone(), t.app_id.clone())
|
||||
})
|
||||
.unwrap_or((None, None))
|
||||
});
|
||||
if let Some(xdg_title) = xdg_title {
|
||||
needs_title = false;
|
||||
title = xdg_title.into();
|
||||
}
|
||||
|
||||
// Try to get title & icon from desktop entry
|
||||
if let Some(app_id) = app_id
|
||||
&& let Some(desktop_entry) = app.desktop_finder.get_cached_entry(&app_id) {
|
||||
if needs_title {
|
||||
title = desktop_entry.app_name.as_ref().into();
|
||||
}
|
||||
if icon.is_none()
|
||||
&& let Some(icon_path) = desktop_entry.icon_path.as_ref() {
|
||||
icon = Some(icon_path.as_ref().into());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to identicon
|
||||
let icon = match icon {
|
||||
Some(icon) => icon,
|
||||
None => DesktopFinder::create_icon(&*title)?.into(),
|
||||
};
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Create(
|
||||
OverlaySelector::Nothing,
|
||||
Box::new(move |app: &mut AppState| {
|
||||
@@ -325,7 +360,8 @@ impl WvrServerState {
|
||||
title,
|
||||
app,
|
||||
window_handle,
|
||||
size_x.max(size_y),
|
||||
icon,
|
||||
[size_x, size_y],
|
||||
).context("Could not create WvrWindow overlay").inspect_err(|e| log::warn!("{e:?}")).ok()
|
||||
}),
|
||||
)));
|
||||
@@ -500,11 +536,13 @@ impl WvrServerState {
|
||||
|
||||
pub fn spawn_process(
|
||||
&mut self,
|
||||
app_name: &str,
|
||||
exec_path: &str,
|
||||
args: &[&str],
|
||||
env: &[(&str, &str)],
|
||||
resolution: [u32; 2],
|
||||
working_dir: Option<&str>,
|
||||
icon: Option<&str>,
|
||||
userdata: HashMap<String, String>,
|
||||
) -> anyhow::Result<process::ProcessHandle> {
|
||||
let auth_key = generate_auth_key();
|
||||
@@ -528,6 +566,7 @@ impl WvrServerState {
|
||||
auth_key,
|
||||
child,
|
||||
exec_path: String::from(exec_path),
|
||||
app_name: String::from(app_name),
|
||||
userdata,
|
||||
args: args.iter().map(|x| String::from(*x)).collect(),
|
||||
working_dir: working_dir.map(String::from),
|
||||
@@ -535,6 +574,7 @@ impl WvrServerState {
|
||||
.iter()
|
||||
.map(|(a, b)| (String::from(*a), String::from(*b)))
|
||||
.collect(),
|
||||
icon: icon.map(Arc::from),
|
||||
resolution,
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{collections::HashMap, io::Read};
|
||||
use std::{collections::HashMap, io::Read, sync::Arc};
|
||||
|
||||
use wayvr_ipc::packet_server;
|
||||
|
||||
@@ -9,12 +9,13 @@ use crate::gen_id;
|
||||
pub struct WayVRProcess {
|
||||
pub auth_key: String,
|
||||
pub child: std::process::Child,
|
||||
|
||||
pub app_name: String,
|
||||
pub exec_path: String,
|
||||
pub args: Vec<String>,
|
||||
pub env: Vec<(String, String)>,
|
||||
pub working_dir: Option<String>,
|
||||
pub resolution: [u32; 2],
|
||||
pub icon: Option<Arc<str>>,
|
||||
|
||||
pub userdata: HashMap<String, String>,
|
||||
}
|
||||
@@ -47,8 +48,11 @@ impl Process {
|
||||
|
||||
pub fn get_name(&self) -> String {
|
||||
match self {
|
||||
Self::Managed(p) => p.get_name().unwrap_or_else(|| String::from("unknown")),
|
||||
Self::External(p) => p.get_name().unwrap_or_else(|| String::from("unknown")),
|
||||
Self::Managed(p) => p.get_name()
|
||||
.or_else(|| p.exec_path.split('/').last().map(String::from))
|
||||
.unwrap_or_else(|| String::from("unknown")),
|
||||
Self::External(p) => p.get_name()
|
||||
.unwrap_or_else(|| String::from("unknown")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +75,8 @@ impl Process {
|
||||
impl Drop for WayVRProcess {
|
||||
fn drop(&mut self) {
|
||||
log::info!(
|
||||
"Sending SIGTERM (graceful exit) to process {}",
|
||||
"Sending SIGTERM (graceful exit) to process {} ({})",
|
||||
self.child.id(),
|
||||
self.exec_path.as_str()
|
||||
);
|
||||
self.terminate();
|
||||
|
||||
@@ -239,11 +239,13 @@ impl Connection {
|
||||
let env_vec = gen_env_vec(&packet_params.env);
|
||||
|
||||
let res = params.wvr_server.spawn_process(
|
||||
&packet_params.name,
|
||||
&packet_params.exec,
|
||||
&args_vec,
|
||||
&env_vec,
|
||||
packet_params.resolution,
|
||||
None,
|
||||
packet_params.icon.as_deref(),
|
||||
packet_params.userdata,
|
||||
);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ use dash_frontend::{
|
||||
settings::{self, SettingsIO},
|
||||
};
|
||||
use glam::{Affine2, Affine3A, Vec2, vec2, vec3};
|
||||
use tracing::instrument::WithSubscriber;
|
||||
use wayvr_ipc::{
|
||||
packet_client::WvrProcessLaunchParams,
|
||||
packet_server::{WvrProcess, WvrProcessHandle, WvrWindow, WvrWindowHandle},
|
||||
@@ -376,11 +375,13 @@ impl DashInterface<AppState> for DashInterfaceLive {
|
||||
|
||||
wvr_server
|
||||
.spawn_process(
|
||||
¶ms.name,
|
||||
¶ms.exec,
|
||||
&args_vec,
|
||||
&env_vec,
|
||||
params.resolution,
|
||||
None,
|
||||
params.icon.as_deref(),
|
||||
params.userdata,
|
||||
)
|
||||
.map(|x| x.as_packet())
|
||||
@@ -434,4 +435,11 @@ impl DashInterface<AppState> for DashInterfaceLive {
|
||||
.enqueue(TaskType::Playspace(PlayspaceTask::Recenter));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn desktop_finder<'a>(
|
||||
&'a mut self,
|
||||
data: &'a mut AppState,
|
||||
) -> &'a mut wlx_common::desktop_finder::DesktopFinder {
|
||||
&mut data.desktop_finder
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
use anyhow::Context;
|
||||
use glam::{FloatExt, Mat4, Vec2, vec2, vec3};
|
||||
use wgui::{
|
||||
animation::{Animation, AnimationEasing}, assets::AssetPath, components::button::ComponentButton, drawing::{self, Color}, event::{self, CallbackDataCommon, CallbackMetadata, EventAlterables, EventListenerKind}, layout::LayoutUpdateParams, parser::{Fetchable, ParseDocumentExtra, ParseDocumentParams}, renderer_vk::util, taffy::{self, prelude::length}, widget::{div::WidgetDiv, rectangle::WidgetRectangle, EventResult}
|
||||
animation::{Animation, AnimationEasing}, assets::AssetPath, components::button::ComponentButton, drawing::{self, Color}, event::{self, CallbackDataCommon, CallbackMetadata, EventAlterables, EventListenerKind}, layout::LayoutUpdateParams, parser::{Fetchable, ParseDocumentParams}, renderer_vk::util, taffy::{self, prelude::length}, widget::{div::WidgetDiv, rectangle::WidgetRectangle, EventResult}
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -319,6 +319,7 @@ pub(super) fn create_keyboard_panel(
|
||||
("Panel", panels_root)
|
||||
},
|
||||
OverlayCategory::WayVR => {
|
||||
params.insert("icon".into(), meta.icon.as_ref().expect("WayVR overlay without Icon attribute!").as_ref().into());
|
||||
("App", apps_root)
|
||||
},
|
||||
_ => continue
|
||||
|
||||
@@ -51,9 +51,11 @@ pub fn create_wl_window_overlay(
|
||||
name: Arc<str>,
|
||||
app: &mut AppState,
|
||||
window: wayvr::window::WindowHandle,
|
||||
size_major: u32,
|
||||
icon: Arc<str>,
|
||||
size: [u32; 2],
|
||||
) -> anyhow::Result<OverlayWindowConfig> {
|
||||
let scale = size_major as f32 / 1920.0;
|
||||
let scale = size[0].max(size[1]) as f32 / 1920.0;
|
||||
let curve_scale = size[0] as f32 / 1920.0;
|
||||
|
||||
Ok(OverlayWindowConfig {
|
||||
name: name.clone(),
|
||||
@@ -61,7 +63,7 @@ pub fn create_wl_window_overlay(
|
||||
grabbable: true,
|
||||
interactable: true,
|
||||
positioning: Positioning::Floating,
|
||||
curvature: Some(0.15),
|
||||
curvature: Some(0.15 * curve_scale),
|
||||
transform: Affine3A::from_scale_rotation_translation(
|
||||
Vec3::ONE * scale,
|
||||
Quat::IDENTITY,
|
||||
@@ -72,12 +74,15 @@ pub fn create_wl_window_overlay(
|
||||
keyboard_focus: Some(KeyboardFocus::WayVR),
|
||||
category: OverlayCategory::WayVR,
|
||||
show_on_spawn: true,
|
||||
..OverlayWindowConfig::from_backend(Box::new(WvrWindowBackend::new(name, app, window)?))
|
||||
..OverlayWindowConfig::from_backend(Box::new(WvrWindowBackend::new(
|
||||
name, app, window, icon,
|
||||
)?))
|
||||
})
|
||||
}
|
||||
|
||||
pub struct WvrWindowBackend {
|
||||
name: Arc<str>,
|
||||
icon: Arc<str>,
|
||||
pipeline: Option<ScreenPipeline>,
|
||||
popups_pipeline: Arc<WGfxPipeline<Vert2Uv>>,
|
||||
interaction_transform: Option<Affine2>,
|
||||
@@ -100,6 +105,7 @@ impl WvrWindowBackend {
|
||||
name: Arc<str>,
|
||||
app: &mut AppState,
|
||||
window: wayvr::window::WindowHandle,
|
||||
icon: Arc<str>,
|
||||
) -> anyhow::Result<Self> {
|
||||
let popups_pipeline = app.gfx.create_pipeline(
|
||||
app.gfx_extras.shaders.get("vert_quad").unwrap(), // want panic
|
||||
@@ -170,6 +176,7 @@ impl WvrWindowBackend {
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
icon,
|
||||
pipeline: None,
|
||||
window,
|
||||
popups: vec![],
|
||||
@@ -508,6 +515,7 @@ impl OverlayBackend for WvrWindowBackend {
|
||||
fn get_attrib(&self, attrib: BackendAttrib) -> Option<BackendAttribValue> {
|
||||
match attrib {
|
||||
BackendAttrib::Stereo => self.stereo.map(BackendAttribValue::Stereo),
|
||||
BackendAttrib::Icon => Some(BackendAttribValue::Icon(self.icon.clone())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use wgui::{
|
||||
use wlx_common::{
|
||||
audio,
|
||||
config::GeneralConfig,
|
||||
desktop_finder::DesktopFinder,
|
||||
overlays::{ToastDisplayMethod, ToastTopic},
|
||||
};
|
||||
|
||||
@@ -57,6 +58,8 @@ pub struct AppState {
|
||||
pub ipc_server: ipc_server::WayVRServer,
|
||||
pub wayvr_signals: SyncEventQueue<WayVRSignal>,
|
||||
|
||||
pub desktop_finder: DesktopFinder,
|
||||
|
||||
#[cfg(feature = "osc")]
|
||||
pub osc_sender: Option<OscSender>,
|
||||
|
||||
@@ -136,6 +139,9 @@ impl AppState {
|
||||
|
||||
let ipc_server = ipc_server::WayVRServer::new()?;
|
||||
|
||||
let mut desktop_finder = DesktopFinder::new();
|
||||
desktop_finder.refresh();
|
||||
|
||||
Ok(Self {
|
||||
session,
|
||||
tasks,
|
||||
@@ -159,6 +165,7 @@ impl AppState {
|
||||
xr_backend,
|
||||
ipc_server,
|
||||
wayvr_signals,
|
||||
desktop_finder,
|
||||
|
||||
#[cfg(feature = "osc")]
|
||||
osc_sender,
|
||||
|
||||
@@ -112,6 +112,7 @@ pub struct OverlayMeta {
|
||||
pub name: Arc<str>,
|
||||
pub category: OverlayCategory,
|
||||
pub visible: bool,
|
||||
pub icon: Option<Arc<str>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
|
||||
@@ -5,8 +5,9 @@ use std::{
|
||||
use anyhow::Context;
|
||||
use glam::{Affine3A, Vec3, Vec3A};
|
||||
use slotmap::{HopSlotMap, Key, SecondaryMap};
|
||||
use wgui::log::LogErr;
|
||||
use wlx_common::{
|
||||
astr_containers::{AStrMap, AStrMapExt}, common::LogErr, config::SerializedWindowSet, overlays::{BackendAttrib, ToastTopic}
|
||||
astr_containers::{AStrMap, AStrMapExt}, config::SerializedWindowSet, overlays::{BackendAttrib, BackendAttribValue, ToastTopic}
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -773,11 +774,18 @@ impl<T> OverlayWindowManager<T> {
|
||||
if matches!(data.config.category, OverlayCategory::Internal) {
|
||||
continue;
|
||||
}
|
||||
let icon = if let Some(BackendAttribValue::Icon(icon)) = data.config.backend.get_attrib(BackendAttrib::Icon) {
|
||||
Some(icon)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
meta.push(OverlayMeta {
|
||||
id,
|
||||
name: data.config.name.clone(),
|
||||
category: data.config.category,
|
||||
visible: data.config.is_active(),
|
||||
icon,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user