customization via wayvr_ipc
This commit is contained in:
@@ -48,6 +48,22 @@ pub struct WlxHapticsParams {
|
|||||||
pub frequency: f32,
|
pub frequency: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum WlxCustomCommand {
|
||||||
|
SetText(String),
|
||||||
|
SetColor(String),
|
||||||
|
SetSprite(String),
|
||||||
|
SetVisible(bool),
|
||||||
|
SetStickyState(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WlxCustomParams {
|
||||||
|
pub overlay: String,
|
||||||
|
pub element: String,
|
||||||
|
pub command: WlxCustomCommand,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum PacketClient {
|
pub enum PacketClient {
|
||||||
Handshake(Handshake),
|
Handshake(Handshake),
|
||||||
@@ -68,4 +84,5 @@ pub enum PacketClient {
|
|||||||
WvrProcessTerminate(packet_server::WvrProcessHandle),
|
WvrProcessTerminate(packet_server::WvrProcessHandle),
|
||||||
WlxHaptics(WlxHapticsParams),
|
WlxHaptics(WlxHapticsParams),
|
||||||
WlxInputState(Serial),
|
WlxInputState(Serial),
|
||||||
|
WlxCustom(WlxCustomParams),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::{
|
use std::{
|
||||||
f32,
|
f32,
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
|
||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ use slotmap::Key;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
drawing::{self, GradientMode, PrimitiveExtent},
|
drawing::{self, GradientMode, PrimitiveExtent},
|
||||||
|
event::CallbackDataCommon,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
widget::{WidgetStateFlags, util::WLength},
|
widget::{util::WLength, WidgetStateFlags},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{WidgetObj, WidgetState};
|
use super::{WidgetObj, WidgetState};
|
||||||
@@ -35,6 +36,10 @@ impl WidgetRectangle {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
pub fn set_color(&mut self, common: &mut CallbackDataCommon, color: drawing::Color) {
|
||||||
|
self.params.color = color;
|
||||||
|
common.mark_widget_dirty(self.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WidgetObj for WidgetRectangle {
|
impl WidgetObj for WidgetRectangle {
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ use slotmap::Key;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
drawing::{self, PrimitiveExtent},
|
drawing::{self, PrimitiveExtent},
|
||||||
|
event::CallbackDataCommon,
|
||||||
globals::Globals,
|
globals::Globals,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
renderer_vk::text::{
|
renderer_vk::text::{
|
||||||
DEFAULT_METRICS,
|
|
||||||
custom_glyph::{CustomGlyph, CustomGlyphData},
|
custom_glyph::{CustomGlyph, CustomGlyphData},
|
||||||
|
DEFAULT_METRICS,
|
||||||
},
|
},
|
||||||
widget::WidgetStateFlags,
|
widget::WidgetStateFlags,
|
||||||
};
|
};
|
||||||
@@ -39,16 +40,18 @@ impl WidgetSprite {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_color(&mut self, color: drawing::Color) {
|
pub fn set_color(&mut self, common: &mut CallbackDataCommon, color: drawing::Color) {
|
||||||
self.params.color = Some(color);
|
self.params.color = Some(color);
|
||||||
|
common.mark_widget_dirty(self.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_color(&self) -> Option<drawing::Color> {
|
pub fn get_color(&self) -> Option<drawing::Color> {
|
||||||
self.params.color
|
self.params.color
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_content(&mut self, content: Option<CustomGlyphData>) {
|
pub fn set_content(&mut self, common: &mut CallbackDataCommon, content: Option<CustomGlyphData>) {
|
||||||
self.params.glyph_data = content;
|
self.params.glyph_data = content;
|
||||||
|
common.mark_widget_dirty(self.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_content(&self) -> Option<CustomGlyphData> {
|
pub fn get_content(&self) -> Option<CustomGlyphData> {
|
||||||
|
|||||||
@@ -54,6 +54,22 @@ pub enum PlayspaceTask {
|
|||||||
FixFloor,
|
FixFloor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum OverlayCustomCommand {
|
||||||
|
SetText(String),
|
||||||
|
SetColor(String),
|
||||||
|
SetSprite(String),
|
||||||
|
SetVisible(bool),
|
||||||
|
SetStickyState(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct OverlayCustomTask {
|
||||||
|
pub overlay: String,
|
||||||
|
pub element: String,
|
||||||
|
pub command: OverlayCustomCommand,
|
||||||
|
}
|
||||||
|
|
||||||
pub type ModifyOverlayTask = dyn FnOnce(&mut AppState, &mut OverlayWindowConfig) + Send;
|
pub type ModifyOverlayTask = dyn FnOnce(&mut AppState, &mut OverlayWindowConfig) + Send;
|
||||||
pub type CreateOverlayTask = dyn FnOnce(&mut AppState) -> Option<OverlayWindowConfig> + Send;
|
pub type CreateOverlayTask = dyn FnOnce(&mut AppState) -> Option<OverlayWindowConfig> + Send;
|
||||||
pub enum OverlayTask {
|
pub enum OverlayTask {
|
||||||
@@ -66,6 +82,7 @@ pub enum OverlayTask {
|
|||||||
CleanupMirrors,
|
CleanupMirrors,
|
||||||
Modify(OverlaySelector, Box<ModifyOverlayTask>),
|
Modify(OverlaySelector, Box<ModifyOverlayTask>),
|
||||||
Create(OverlaySelector, Box<CreateOverlayTask>),
|
Create(OverlaySelector, Box<CreateOverlayTask>),
|
||||||
|
Custom(OverlayCustomTask),
|
||||||
Drop(OverlaySelector),
|
Drop(OverlaySelector),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,10 @@ use std::{
|
|||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use time::get_millis;
|
use time::get_millis;
|
||||||
use wayvr_ipc::{packet_client, packet_server};
|
use wayvr_ipc::{
|
||||||
|
packet_client::{self},
|
||||||
|
packet_server,
|
||||||
|
};
|
||||||
use xkbcommon::xkb;
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -94,6 +97,7 @@ pub enum WayVRSignal {
|
|||||||
BroadcastStateChanged(packet_server::WvrStateChanged),
|
BroadcastStateChanged(packet_server::WvrStateChanged),
|
||||||
DropOverlay(crate::windowing::OverlayID),
|
DropOverlay(crate::windowing::OverlayID),
|
||||||
Haptics(super::input::Haptics),
|
Haptics(super::input::Haptics),
|
||||||
|
CustomTask(crate::backend::task::OverlayCustomTask),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum BlitMethod {
|
pub enum BlitMethod {
|
||||||
|
|||||||
@@ -479,6 +479,35 @@ impl Connection {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_wlx_custom(params: &mut TickParams, custom_params: packet_client::WlxCustomParams) {
|
||||||
|
use crate::backend::task::{OverlayCustomCommand, OverlayCustomTask};
|
||||||
|
|
||||||
|
params
|
||||||
|
.state
|
||||||
|
.signals
|
||||||
|
.send(super::WayVRSignal::CustomTask(OverlayCustomTask {
|
||||||
|
overlay: custom_params.overlay,
|
||||||
|
element: custom_params.element,
|
||||||
|
command: match custom_params.command {
|
||||||
|
packet_client::WlxCustomCommand::SetText(text) => {
|
||||||
|
OverlayCustomCommand::SetText(text)
|
||||||
|
}
|
||||||
|
packet_client::WlxCustomCommand::SetSprite(sprite) => {
|
||||||
|
OverlayCustomCommand::SetSprite(sprite)
|
||||||
|
}
|
||||||
|
packet_client::WlxCustomCommand::SetStickyState(sticky) => {
|
||||||
|
OverlayCustomCommand::SetStickyState(sticky)
|
||||||
|
}
|
||||||
|
packet_client::WlxCustomCommand::SetVisible(visible) => {
|
||||||
|
OverlayCustomCommand::SetVisible(visible)
|
||||||
|
}
|
||||||
|
packet_client::WlxCustomCommand::SetColor(color) => {
|
||||||
|
OverlayCustomCommand::SetColor(color)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
fn process_payload(&mut self, params: &mut TickParams, payload: Payload) -> anyhow::Result<()> {
|
fn process_payload(&mut self, params: &mut TickParams, payload: Payload) -> anyhow::Result<()> {
|
||||||
let packet: PacketClient = ipc::data_decode(&payload)?;
|
let packet: PacketClient = ipc::data_decode(&payload)?;
|
||||||
|
|
||||||
@@ -531,6 +560,9 @@ impl Connection {
|
|||||||
PacketClient::WlxHaptics(haptics_params) => {
|
PacketClient::WlxHaptics(haptics_params) => {
|
||||||
Self::handle_wlx_haptics(params, haptics_params);
|
Self::handle_wlx_haptics(params, haptics_params);
|
||||||
}
|
}
|
||||||
|
PacketClient::WlxCustom(custom_params) => {
|
||||||
|
Self::handle_wlx_custom(params, custom_params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,15 +1,29 @@
|
|||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
use glam::{Affine3A, Quat, Vec3, vec3};
|
use glam::{Affine3A, Quat, Vec3, vec3};
|
||||||
|
use wgui::{
|
||||||
|
components::button::ComponentButton,
|
||||||
|
event::{CallbackDataCommon, EventAlterables},
|
||||||
|
i18n::Translation,
|
||||||
|
parser::{Fetchable, parse_color_hex},
|
||||||
|
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
||||||
|
taffy,
|
||||||
|
widget::{label::WidgetLabel, rectangle::WidgetRectangle, sprite::WidgetSprite},
|
||||||
|
};
|
||||||
use wlx_common::windowing::OverlayWindowState;
|
use wlx_common::windowing::OverlayWindowState;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
backend::task::OverlayCustomCommand,
|
||||||
gui::{
|
gui::{
|
||||||
panel::{GuiPanel, NewGuiPanelParams},
|
panel::{GuiPanel, NewGuiPanelParams},
|
||||||
timer::GuiTimer,
|
timer::GuiTimer,
|
||||||
},
|
},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
windowing::window::{OverlayCategory, OverlayWindowConfig},
|
windowing::{
|
||||||
|
backend::OverlayEventData,
|
||||||
|
window::{OverlayCategory, OverlayWindowConfig},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CustomPanelState {}
|
struct CustomPanelState {}
|
||||||
@@ -36,6 +50,21 @@ pub fn create_custom(app: &mut AppState, name: Arc<str>) -> Option<OverlayWindow
|
|||||||
|
|
||||||
let scale = panel.layout.content_size.x / 40.0 * 0.05;
|
let scale = panel.layout.content_size.x / 40.0 * 0.05;
|
||||||
|
|
||||||
|
panel.on_notify = Some(Box::new({
|
||||||
|
let name = name.clone();
|
||||||
|
move |panel, app, event_data| {
|
||||||
|
let OverlayEventData::CustomCommand { element, command } = event_data else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = apply_custom_command(panel, app, &element, &command) {
|
||||||
|
log::warn!("Could not apply {command:?} on {name}/{element}: {e:?}");
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
Some(OverlayWindowConfig {
|
Some(OverlayWindowConfig {
|
||||||
name,
|
name,
|
||||||
category: OverlayCategory::Panel,
|
category: OverlayCategory::Panel,
|
||||||
@@ -52,3 +81,100 @@ pub fn create_custom(app: &mut AppState, name: Arc<str>) -> Option<OverlayWindow
|
|||||||
..OverlayWindowConfig::from_backend(Box::new(panel))
|
..OverlayWindowConfig::from_backend(Box::new(panel))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_custom_command(
|
||||||
|
panel: &mut GuiPanel<CustomPanelState>,
|
||||||
|
app: &mut AppState,
|
||||||
|
element: &str,
|
||||||
|
command: &OverlayCustomCommand,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let mut alterables = EventAlterables::default();
|
||||||
|
let mut com = CallbackDataCommon {
|
||||||
|
alterables: &mut alterables,
|
||||||
|
state: &panel.layout.state,
|
||||||
|
};
|
||||||
|
|
||||||
|
match command {
|
||||||
|
OverlayCustomCommand::SetText(text) => {
|
||||||
|
if let Ok(mut label) = panel
|
||||||
|
.parser_state
|
||||||
|
.fetch_widget_as::<WidgetLabel>(&panel.layout.state, element)
|
||||||
|
{
|
||||||
|
label.set_text(&mut com, Translation::from_raw_text(text));
|
||||||
|
} else if let Ok(button) = panel
|
||||||
|
.parser_state
|
||||||
|
.fetch_component_as::<ComponentButton>(&element)
|
||||||
|
{
|
||||||
|
button.set_text(&mut com, Translation::from_raw_text(text));
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("No <label> or <Button> with such id.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OverlayCustomCommand::SetSprite(path) => {
|
||||||
|
let mut widget = panel
|
||||||
|
.parser_state
|
||||||
|
.fetch_widget_as::<WidgetSprite>(&panel.layout.state, element)
|
||||||
|
.context("No <sprite> with such id.")?;
|
||||||
|
|
||||||
|
if path == "none" {
|
||||||
|
widget.set_content(&mut com, None);
|
||||||
|
} else {
|
||||||
|
let content = CustomGlyphContent::from_assets(
|
||||||
|
&mut app.wgui_globals,
|
||||||
|
wgui::assets::AssetPath::File(&path),
|
||||||
|
)
|
||||||
|
.context("Could not load content from supplied path.")?;
|
||||||
|
|
||||||
|
let data = CustomGlyphData::new(content);
|
||||||
|
|
||||||
|
widget.set_content(&mut com, Some(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OverlayCustomCommand::SetColor(color) => {
|
||||||
|
let color = parse_color_hex(&color)
|
||||||
|
.context("Invalid color format, must be a html hex color!")?;
|
||||||
|
|
||||||
|
if let Ok(pair) = panel
|
||||||
|
.parser_state
|
||||||
|
.fetch_widget(&panel.layout.state, element)
|
||||||
|
{
|
||||||
|
if let Some(mut rect) = pair.widget.get_as_mut::<WidgetRectangle>() {
|
||||||
|
rect.set_color(&mut com, color);
|
||||||
|
} else if let Some(mut label) = pair.widget.get_as_mut::<WidgetLabel>() {
|
||||||
|
label.set_color(&mut com, color, true);
|
||||||
|
} else if let Some(mut sprite) = pair.widget.get_as_mut::<WidgetSprite>() {
|
||||||
|
sprite.set_color(&mut com, color);
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("No <rectangle> or <label> or <sprite> with such id.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("No <rectangle> or <label> or <sprite> with such id.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OverlayCustomCommand::SetVisible(visible) => {
|
||||||
|
let wid = panel
|
||||||
|
.parser_state
|
||||||
|
.get_widget_id(&element)
|
||||||
|
.context("No widget with such id.")?;
|
||||||
|
|
||||||
|
let display = if *visible {
|
||||||
|
taffy::Display::Flex
|
||||||
|
} else {
|
||||||
|
taffy::Display::None
|
||||||
|
};
|
||||||
|
|
||||||
|
com.alterables
|
||||||
|
.set_style(wid, wgui::event::StyleSetRequest::Display(display));
|
||||||
|
}
|
||||||
|
OverlayCustomCommand::SetStickyState(sticky_down) => {
|
||||||
|
let button = panel
|
||||||
|
.parser_state
|
||||||
|
.fetch_component_as::<ComponentButton>(element)
|
||||||
|
.context("No <Button> with such id.")?;
|
||||||
|
button.set_sticky_state(&mut com, *sticky_down);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.layout.process_alterables(alterables)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ where
|
|||||||
.widgets
|
.widgets
|
||||||
.get_as::<WidgetSprite>(self.top_sprite_id)
|
.get_as::<WidgetSprite>(self.top_sprite_id)
|
||||||
{
|
{
|
||||||
sprite.set_content(Some(new.sprite.clone()));
|
sprite.set_content(common, Some(new.sprite.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -473,7 +473,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
.get_as::<WidgetSprite>(btn.sprite)
|
.get_as::<WidgetSprite>(btn.sprite)
|
||||||
&& let Some(glyph) = panel.state.overlay_cat_icons.get(meta.category)
|
&& let Some(glyph) = panel.state.overlay_cat_icons.get(meta.category)
|
||||||
{
|
{
|
||||||
sprite.set_content(Some(glyph.clone()));
|
sprite.set_content(&mut com, Some(glyph.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
btn.button.set_sticky_state(&mut com, meta.visible);
|
btn.button.set_sticky_state(&mut com, meta.visible);
|
||||||
@@ -525,7 +525,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
|||||||
&& let Some(glyph) = panel.state.device_role_icons.get(dev.role)
|
&& let Some(glyph) = panel.state.device_role_icons.get(dev.role)
|
||||||
&& let Some(mut s) = panel.layout.state.widgets.get_as::<WidgetSprite>(*s)
|
&& let Some(mut s) = panel.layout.state.widgets.get_as::<WidgetSprite>(*s)
|
||||||
{
|
{
|
||||||
s.set_content(Some(glyph.clone()));
|
s.set_content(&mut com, Some(glyph.clone()));
|
||||||
com.alterables
|
com.alterables
|
||||||
.set_style(*div, StyleSetRequest::Display(taffy::Display::Flex));
|
.set_style(*div, StyleSetRequest::Display(taffy::Display::Flex));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -461,6 +461,10 @@ where
|
|||||||
wayvr::WayVRSignal::Haptics(haptics) => {
|
wayvr::WayVRSignal::Haptics(haptics) => {
|
||||||
wayvr.pending_haptics = Some(haptics);
|
wayvr.pending_haptics = Some(haptics);
|
||||||
}
|
}
|
||||||
|
wayvr::WayVRSignal::CustomTask(custom_task) => {
|
||||||
|
app.tasks
|
||||||
|
.enqueue(TaskType::Overlay(OverlayTask::Custom(custom_task)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ use wlx_common::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::input::{HoverResult, PointerHit},
|
backend::{
|
||||||
|
input::{HoverResult, PointerHit},
|
||||||
|
task::OverlayCustomCommand,
|
||||||
|
},
|
||||||
graphics::{ExtentExt, RenderResult},
|
graphics::{ExtentExt, RenderResult},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::hid::WheelDelta,
|
subsystem::hid::WheelDelta,
|
||||||
@@ -123,6 +126,10 @@ pub enum OverlayEventData {
|
|||||||
pos: Positioning,
|
pos: Positioning,
|
||||||
editing: bool,
|
editing: bool,
|
||||||
},
|
},
|
||||||
|
CustomCommand {
|
||||||
|
element: String,
|
||||||
|
command: OverlayCustomCommand,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait OverlayBackend: Any {
|
pub trait OverlayBackend: Any {
|
||||||
|
|||||||
@@ -290,6 +290,27 @@ where
|
|||||||
self.dropped_overlays.push_back(o);
|
self.dropped_overlays.push_back(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
OverlayTask::Custom(task) => {
|
||||||
|
if let Some(oid) = self.lookup(&task.overlay)
|
||||||
|
&& let Some(o) = self.mut_by_id(oid)
|
||||||
|
{
|
||||||
|
if !matches!(o.config.category, OverlayCategory::Panel) {
|
||||||
|
log::warn!(
|
||||||
|
"Received command for '{}', but this overlay does not support commands",
|
||||||
|
&task.overlay
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
o.config.backend.notify(
|
||||||
|
app,
|
||||||
|
OverlayEventData::CustomCommand {
|
||||||
|
element: task.element,
|
||||||
|
command: task.command,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user