initial commit
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
use std::{collections::VecDeque, time::Instant};
|
||||
|
||||
use glam::{Affine3A, Vec2, Vec3};
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
pub const HAND_LEFT: usize = 0;
|
||||
pub const HAND_RIGHT: usize = 1;
|
||||
|
||||
pub const POINTER_NORM: u16 = 0;
|
||||
pub const POINTER_SHIFT: u16 = 1;
|
||||
pub const POINTER_ALT: u16 = 2;
|
||||
|
||||
pub trait InteractionHandler {
|
||||
fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit);
|
||||
fn on_left(&mut self, app: &mut AppState, hand: usize);
|
||||
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool);
|
||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32);
|
||||
}
|
||||
|
||||
// --- Dummies & plumbing below ---
|
||||
|
||||
impl Default for PointerState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
click: false,
|
||||
grab: false,
|
||||
show_hide: false,
|
||||
scroll: 0.,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DummyInteractionHandler;
|
||||
|
||||
impl InteractionHandler for DummyInteractionHandler {
|
||||
fn on_left(&mut self, _app: &mut AppState, _hand: usize) {}
|
||||
fn on_hover(&mut self, _app: &mut AppState, _hit: &PointerHit) {}
|
||||
fn on_pointer(&mut self, _app: &mut AppState, _hit: &PointerHit, _pressed: bool) {}
|
||||
fn on_scroll(&mut self, _app: &mut AppState, _hit: &PointerHit, _delta: f32) {}
|
||||
}
|
||||
@@ -10,23 +10,25 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::overlay::{OverlayData, OverlayState},
|
||||
gui::{color_parse, CanvasBuilder, Control},
|
||||
input::{KeyModifier, VirtualKey, KEYS_TO_MODS},
|
||||
state::AppState,
|
||||
};
|
||||
use glam::{vec2, vec3};
|
||||
use glam::{vec2, vec3a};
|
||||
use log::error;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rodio::{Decoder, OutputStream, Source};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::OverlayData;
|
||||
|
||||
const PIXELS_PER_UNIT: f32 = 80.;
|
||||
const BUTTON_PADDING: f32 = 4.;
|
||||
|
||||
pub fn create_keyboard(app: &AppState) -> OverlayData {
|
||||
pub fn create_keyboard<O>(app: &AppState) -> OverlayData<O>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let size = vec2(
|
||||
LAYOUT.row_size * PIXELS_PER_UNIT,
|
||||
(LAYOUT.main_layout.len() as f32) * PIXELS_PER_UNIT,
|
||||
@@ -106,12 +108,15 @@ pub fn create_keyboard(app: &AppState) -> OverlayData {
|
||||
let canvas = canvas.build();
|
||||
|
||||
OverlayData {
|
||||
name: Arc::from("Kbd"),
|
||||
show_hide: true,
|
||||
width: LAYOUT.row_size * 0.05,
|
||||
size: (size.x as _, size.y as _),
|
||||
grabbable: true,
|
||||
spawn_point: vec3(0., -0.5, -1.),
|
||||
state: OverlayState {
|
||||
name: Arc::from("Kbd"),
|
||||
show_hide: true,
|
||||
width: LAYOUT.row_size * 0.05,
|
||||
size: (size.x as _, size.y as _),
|
||||
grabbable: true,
|
||||
spawn_point: vec3a(0., -0.5, -1.),
|
||||
..Default::default()
|
||||
},
|
||||
backend: Box::new(canvas),
|
||||
..Default::default()
|
||||
}
|
||||
|
||||
@@ -1,145 +1,3 @@
|
||||
use std::sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
use glam::{Affine3A, Quat, Vec3};
|
||||
use vulkano::image::ImageViewAbstract;
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
use self::interactions::{DummyInteractionHandler, InteractionHandler, PointerHit};
|
||||
|
||||
pub mod interactions;
|
||||
pub mod keyboard;
|
||||
pub mod screen;
|
||||
pub mod watch;
|
||||
|
||||
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
pub enum RelativeTo {
|
||||
None,
|
||||
Head,
|
||||
Hand(usize),
|
||||
}
|
||||
|
||||
pub trait OverlayBackend: OverlayRenderer + InteractionHandler {}
|
||||
|
||||
pub struct OverlayData {
|
||||
pub id: usize,
|
||||
pub name: Arc<str>,
|
||||
pub width: f32,
|
||||
pub size: (i32, i32),
|
||||
pub want_visible: bool,
|
||||
pub show_hide: bool,
|
||||
pub grabbable: bool,
|
||||
pub transform: Affine3A,
|
||||
pub spawn_point: Vec3,
|
||||
pub spawn_rotation: Quat,
|
||||
pub relative_to: RelativeTo,
|
||||
pub interaction_transform: Affine3A,
|
||||
pub backend: Box<dyn OverlayBackend>,
|
||||
pub primary_pointer: Option<usize>,
|
||||
}
|
||||
impl Default for OverlayData {
|
||||
fn default() -> OverlayData {
|
||||
OverlayData {
|
||||
id: AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed),
|
||||
name: Arc::from(""),
|
||||
width: 1.,
|
||||
size: (0, 0),
|
||||
want_visible: false,
|
||||
show_hide: false,
|
||||
grabbable: false,
|
||||
relative_to: RelativeTo::None,
|
||||
spawn_point: Vec3::NEG_Z,
|
||||
spawn_rotation: Quat::IDENTITY,
|
||||
transform: Affine3A::IDENTITY,
|
||||
interaction_transform: Affine3A::IDENTITY,
|
||||
backend: Box::new(SplitOverlayBackend::default()),
|
||||
primary_pointer: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OverlayData {
|
||||
pub fn reset(&mut self, app: &mut AppState) {
|
||||
todo!()
|
||||
}
|
||||
pub fn init(&mut self, app: &mut AppState) {
|
||||
self.backend.init(app);
|
||||
}
|
||||
pub fn render(&mut self, app: &mut AppState) {
|
||||
self.backend.render(app);
|
||||
}
|
||||
pub fn view(&mut self) -> Arc<dyn ImageViewAbstract> {
|
||||
self.backend.view()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait OverlayRenderer {
|
||||
fn init(&mut self, app: &mut AppState);
|
||||
fn pause(&mut self, app: &mut AppState);
|
||||
fn resume(&mut self, app: &mut AppState);
|
||||
fn render(&mut self, app: &mut AppState);
|
||||
fn view(&mut self) -> Arc<dyn ImageViewAbstract>;
|
||||
}
|
||||
|
||||
pub struct FallbackRenderer;
|
||||
|
||||
impl OverlayRenderer for FallbackRenderer {
|
||||
fn init(&mut self, _app: &mut AppState) {}
|
||||
fn pause(&mut self, _app: &mut AppState) {}
|
||||
fn resume(&mut self, _app: &mut AppState) {}
|
||||
fn render(&mut self, _app: &mut AppState) {}
|
||||
fn view(&mut self) -> Arc<dyn ImageViewAbstract> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
// Boilerplate and dummies
|
||||
|
||||
pub struct SplitOverlayBackend {
|
||||
pub renderer: Box<dyn OverlayRenderer>,
|
||||
pub interaction: Box<dyn InteractionHandler>,
|
||||
}
|
||||
|
||||
impl Default for SplitOverlayBackend {
|
||||
fn default() -> SplitOverlayBackend {
|
||||
SplitOverlayBackend {
|
||||
renderer: Box::new(FallbackRenderer),
|
||||
interaction: Box::new(DummyInteractionHandler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OverlayBackend for SplitOverlayBackend {}
|
||||
impl OverlayRenderer for SplitOverlayBackend {
|
||||
fn init(&mut self, app: &mut AppState) {
|
||||
self.renderer.init(app);
|
||||
}
|
||||
fn pause(&mut self, app: &mut AppState) {
|
||||
self.renderer.pause(app);
|
||||
}
|
||||
fn resume(&mut self, app: &mut AppState) {
|
||||
self.renderer.resume(app);
|
||||
}
|
||||
fn render(&mut self, app: &mut AppState) {
|
||||
self.renderer.render(app);
|
||||
}
|
||||
fn view(&mut self) -> Arc<dyn ImageViewAbstract> {
|
||||
self.renderer.view()
|
||||
}
|
||||
}
|
||||
impl InteractionHandler for SplitOverlayBackend {
|
||||
fn on_left(&mut self, app: &mut AppState, hand: usize) {
|
||||
self.interaction.on_left(app, hand);
|
||||
}
|
||||
fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) {
|
||||
self.interaction.on_hover(app, hit);
|
||||
}
|
||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32) {
|
||||
self.interaction.on_scroll(app, hit, delta);
|
||||
}
|
||||
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
|
||||
self.interaction.on_pointer(app, hit, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,59 @@
|
||||
use log::{info, warn};
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
path::Path,
|
||||
sync::{mpsc::Receiver, Arc},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use vulkano::{
|
||||
command_buffer::CommandBufferUsage,
|
||||
format::Format,
|
||||
image::{view::ImageView, ImageAccess, ImageViewAbstract, ImmutableImage},
|
||||
Handle, VulkanObject,
|
||||
};
|
||||
use wlx_capture::{
|
||||
frame::WlxFrame,
|
||||
pipewire::{pipewire_select_screen, PipewireCapture},
|
||||
wayland::{Transform, WlxClient, WlxOutput},
|
||||
wlr::WlrDmabufCapture,
|
||||
WlxCapture,
|
||||
};
|
||||
|
||||
pub struct ScreenInteractionData {
|
||||
use glam::{vec2, Affine2, Quat, Vec2, Vec3};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
input::{InteractionHandler, PointerHit, PointerMode},
|
||||
overlay::{OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend},
|
||||
},
|
||||
input::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT},
|
||||
state::{AppSession, AppState},
|
||||
};
|
||||
|
||||
pub struct ScreenInteractionHandler {
|
||||
next_scroll: Instant,
|
||||
next_move: Instant,
|
||||
mouse_transform: Affine2,
|
||||
}
|
||||
impl ScreenInteractionData {
|
||||
impl ScreenInteractionHandler {
|
||||
fn new(pos: Vec2, size: Vec2, transform: Transform) -> ScreenInteractionHandler {
|
||||
let transform = match transform {
|
||||
Transform::_90 | Transform::Flipped90 =>
|
||||
Affine2::from_cols(vec2(0., size.y), vec2(-size.x, 0.), vec2(pos.x + size.x, pos.y)),
|
||||
Transform::_180 | Transform::Flipped180 =>
|
||||
Affine2::from_cols(vec2(-size.x, 0.), vec2(0., -size.y), vec2(pos.x + size.x, pos.y + size.y)),
|
||||
Transform::_270 | Transform::Flipped270 =>
|
||||
Affine2::from_cols(vec2(0., -size.y), vec2(size.x, 0.), vec2(pos.x, pos.y + size.y)),
|
||||
_ =>
|
||||
Affine2::from_cols(vec2(size.x, 0.), vec2(0., size.y), pos),
|
||||
Transform::_90 | Transform::Flipped90 => Affine2::from_cols(
|
||||
vec2(0., size.y),
|
||||
vec2(-size.x, 0.),
|
||||
vec2(pos.x + size.x, pos.y),
|
||||
),
|
||||
Transform::_180 | Transform::Flipped180 => Affine2::from_cols(
|
||||
vec2(-size.x, 0.),
|
||||
vec2(0., -size.y),
|
||||
vec2(pos.x + size.x, pos.y + size.y),
|
||||
),
|
||||
Transform::_270 | Transform::Flipped270 => Affine2::from_cols(
|
||||
vec2(0., -size.y),
|
||||
vec2(size.x, 0.),
|
||||
vec2(pos.x, pos.y + size.y),
|
||||
),
|
||||
_ => Affine2::from_cols(vec2(size.x, 0.), vec2(0., size.y), pos),
|
||||
};
|
||||
|
||||
ScreenInteractionHandler {
|
||||
@@ -25,7 +64,222 @@ impl ScreenInteractionData {
|
||||
}
|
||||
}
|
||||
|
||||
struct ScreenInteractionHandler {
|
||||
impl InteractionHandler for ScreenInteractionHandler {
|
||||
fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) {
|
||||
if self.next_move < Instant::now() {
|
||||
let pos = self.mouse_transform.transform_point2(hit.uv);
|
||||
app.input.mouse_move(pos);
|
||||
}
|
||||
}
|
||||
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
|
||||
let pos = self.mouse_transform.transform_point2(hit.uv);
|
||||
app.input.mouse_move(pos);
|
||||
|
||||
let btn = match hit.mode {
|
||||
PointerMode::Right => MOUSE_RIGHT,
|
||||
PointerMode::Middle => MOUSE_MIDDLE,
|
||||
_ => MOUSE_LEFT,
|
||||
};
|
||||
|
||||
if pressed {
|
||||
self.next_move = Instant::now() + Duration::from_millis(300);
|
||||
}
|
||||
|
||||
app.input.send_button(btn, pressed);
|
||||
}
|
||||
fn on_scroll(&mut self, app: &mut AppState, _hit: &PointerHit, delta: f32) {
|
||||
let millis = (1. - delta.abs()) * delta;
|
||||
if let Some(next_scroll) = Instant::now().checked_add(Duration::from_millis(millis as _)) {
|
||||
self.next_scroll = next_scroll;
|
||||
}
|
||||
app.input.wheel(if delta < 0. { -1 } else { 1 })
|
||||
}
|
||||
fn on_left(&mut self, _app: &mut AppState, _hand: usize) {}
|
||||
}
|
||||
|
||||
pub struct ScreenRenderer {
|
||||
capture: Box<dyn WlxCapture>,
|
||||
resolution: (i32, i32),
|
||||
receiver: Option<Receiver<WlxFrame>>,
|
||||
view: Option<Arc<dyn ImageViewAbstract>>,
|
||||
}
|
||||
|
||||
impl ScreenRenderer {
|
||||
pub fn new_wlr(output: &WlxOutput) -> Option<ScreenRenderer> {
|
||||
let Some(client) = WlxClient::new() else {
|
||||
return None;
|
||||
};
|
||||
let Some(capture) = WlrDmabufCapture::new(client, output.id) else {
|
||||
return None;
|
||||
};
|
||||
Some(ScreenRenderer {
|
||||
capture: Box::new(capture),
|
||||
resolution: output.size,
|
||||
receiver: None,
|
||||
view: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_pw(
|
||||
output: &WlxOutput,
|
||||
token: Option<&str>,
|
||||
_fallback: bool,
|
||||
) -> Option<ScreenRenderer> {
|
||||
let name = output.name.clone();
|
||||
let node_id = futures::executor::block_on(pipewire_select_screen(token)).ok()?;
|
||||
|
||||
let capture = PipewireCapture::new(name, node_id, 60);
|
||||
|
||||
Some(ScreenRenderer {
|
||||
capture: Box::new(capture),
|
||||
resolution: output.size,
|
||||
receiver: None,
|
||||
view: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_xshm() -> ScreenRenderer {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl OverlayRenderer for ScreenRenderer {
|
||||
fn init(&mut self, app: &mut AppState) {
|
||||
self.receiver = Some(self.capture.init());
|
||||
let mut cmd = app
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit);
|
||||
let default_image = cmd.texture2d(1, 1, Format::R8G8B8A8_UNORM, vec![255, 0, 255, 255]);
|
||||
let _ = cmd.end_and_execute();
|
||||
}
|
||||
fn render(&mut self, app: &mut AppState) {
|
||||
let Some(receiver) = self.receiver.as_mut() else {
|
||||
log::error!("No receiver");
|
||||
return;
|
||||
};
|
||||
|
||||
for frame in receiver.try_iter() {
|
||||
match frame {
|
||||
WlxFrame::Dmabuf(frame) => {
|
||||
if let Ok(new) = app.graphics.dmabuf_texture(frame) {
|
||||
if let Some(current) = self.view.as_ref() {
|
||||
if current.image().inner().image.handle().as_raw()
|
||||
== new.inner().image.handle().as_raw()
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.view = Some(ImageView::new_default(new).unwrap());
|
||||
}
|
||||
}
|
||||
WlxFrame::MemFd(frame) => {
|
||||
todo!()
|
||||
}
|
||||
WlxFrame::MemPtr(frame) => {
|
||||
todo!()
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
self.capture.request_new_frame();
|
||||
}
|
||||
fn pause(&mut self, _app: &mut AppState) {
|
||||
self.capture.pause();
|
||||
}
|
||||
fn resume(&mut self, _app: &mut AppState) {
|
||||
self.capture.resume();
|
||||
}
|
||||
fn view(&mut self) -> Option<Arc<dyn ImageViewAbstract>> {
|
||||
self.view.as_ref().and_then(|v| Some(v.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
fn try_create_screen<O>(wl: &WlxClient, idx: usize, session: &AppSession) -> Option<OverlayData<O>>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let output = &wl.outputs[idx];
|
||||
info!(
|
||||
"{}: Res {}x{} Size {:?} Pos {:?}",
|
||||
output.name, output.size.0, output.size.1, output.logical_size, output.logical_pos,
|
||||
);
|
||||
|
||||
let size = (output.size.0, output.size.1);
|
||||
let mut capture: Option<ScreenRenderer> = None;
|
||||
|
||||
if session.capture_method == "auto" && wl.maybe_wlr_dmabuf_mgr.is_some() {
|
||||
info!("{}: Using Wlr DMA-Buf", &output.name);
|
||||
capture = ScreenRenderer::new_wlr(output);
|
||||
}
|
||||
|
||||
if capture.is_none() {
|
||||
info!("{}: Using Pipewire capture", &output.name);
|
||||
let file_name = format!("{}.token", &output.name);
|
||||
let full_path = Path::new(&session.config_path).join(file_name);
|
||||
let token = std::fs::read_to_string(full_path).ok();
|
||||
|
||||
capture = ScreenRenderer::new_pw(
|
||||
output,
|
||||
token.as_deref(),
|
||||
session.capture_method == "pw_fallback",
|
||||
);
|
||||
}
|
||||
if let Some(capture) = capture {
|
||||
let backend = Box::new(SplitOverlayBackend {
|
||||
renderer: Box::new(capture),
|
||||
interaction: Box::new(ScreenInteractionHandler::new(
|
||||
vec2(output.logical_pos.0 as f32, output.logical_pos.1 as f32),
|
||||
vec2(output.logical_size.0 as f32, output.logical_size.1 as f32),
|
||||
output.transform,
|
||||
)),
|
||||
});
|
||||
|
||||
let axis = Vec3::new(0., 0., 1.);
|
||||
|
||||
let angle = match output.transform {
|
||||
Transform::_90 | Transform::Flipped90 => PI / 2.,
|
||||
Transform::_180 | Transform::Flipped180 => PI,
|
||||
Transform::_270 | Transform::Flipped270 => -PI / 2.,
|
||||
_ => 0.,
|
||||
};
|
||||
|
||||
Some(OverlayData {
|
||||
state: OverlayState {
|
||||
name: output.name.clone(),
|
||||
size,
|
||||
want_visible: idx == 0,
|
||||
show_hide: true,
|
||||
grabbable: true,
|
||||
spawn_rotation: Quat::from_axis_angle(axis, angle),
|
||||
..Default::default()
|
||||
},
|
||||
backend,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
warn!("{}: Will not be used", &output.name);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_screens_wayland<O>(session: &AppSession) -> Vec<OverlayData<O>>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let mut overlays = vec![];
|
||||
let wl = WlxClient::new().unwrap();
|
||||
|
||||
for idx in 0..wl.outputs.len() {
|
||||
if let Some(overlay) = try_create_screen(&wl, idx, &session) {
|
||||
overlays.push(overlay);
|
||||
}
|
||||
}
|
||||
overlays
|
||||
}
|
||||
|
||||
pub fn get_screens_x11<O>() -> Vec<OverlayData<O>>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
use std::{sync::Arc, time::Instant};
|
||||
|
||||
use chrono::Local;
|
||||
use glam::{Quat, Vec3};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
common::{OverlaySelector, TaskType},
|
||||
overlay::{OverlayData, OverlayState, RelativeTo},
|
||||
},
|
||||
gui::{color_parse, CanvasBuilder},
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
use super::{OverlayData, RelativeTo};
|
||||
|
||||
pub const WATCH_DEFAULT_POS: Vec3 = Vec3::new(0., 0., 0.15);
|
||||
pub const WATCH_DEFAULT_ROT: Quat = Quat::from_xyzw(0.7071066, 0., 0.7071066, 0.0007963);
|
||||
|
||||
pub fn create_watch(state: &AppState, screens: Vec<(usize, Arc<str>)>) -> OverlayData {
|
||||
pub fn create_watch<O>(state: &AppState, screens: &[OverlayData<O>]) -> OverlayData<O>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let mut canvas = CanvasBuilder::new(400, 200, state.graphics.clone(), state.format, ());
|
||||
let empty_str: Arc<str> = Arc::from("");
|
||||
|
||||
@@ -93,22 +94,19 @@ pub fn create_watch(state: &AppState, screens: Vec<(usize, Arc<str>)>) -> Overla
|
||||
.as_millis()
|
||||
< 2000
|
||||
{
|
||||
app.tasks.push_back(Box::new(|_app, o| {
|
||||
for overlay in o {
|
||||
if &*overlay.name == "Kbd" {
|
||||
overlay.want_visible = !overlay.want_visible;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}));
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Name("Kbd".into()),
|
||||
Box::new(|_app, o| {
|
||||
o.want_visible = !o.want_visible;
|
||||
}),
|
||||
));
|
||||
} else {
|
||||
app.tasks.push_back(Box::new(|app, o| {
|
||||
for overlay in o {
|
||||
if &*overlay.name == "Kbd" {
|
||||
overlay.reset(app);
|
||||
}
|
||||
}
|
||||
}));
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Name("Kbd".into()),
|
||||
Box::new(|app, o| {
|
||||
o.reset(app);
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -116,11 +114,17 @@ pub fn create_watch(state: &AppState, screens: Vec<(usize, Arc<str>)>) -> Overla
|
||||
|
||||
canvas.bg_color = color_parse("#405060");
|
||||
|
||||
for (scr_idx, scr_name) in screens.into_iter() {
|
||||
let button = canvas.button(button_x + 2., 162., button_width - 4., 36., scr_name);
|
||||
for screen in screens.into_iter() {
|
||||
let button = canvas.button(
|
||||
button_x + 2.,
|
||||
162.,
|
||||
button_width - 4.,
|
||||
36.,
|
||||
screen.state.name.clone(),
|
||||
);
|
||||
button.state = Some(WatchButtonState {
|
||||
pressed_at: Instant::now(),
|
||||
scr_idx,
|
||||
scr_idx: screen.state.id,
|
||||
});
|
||||
|
||||
button.on_press = Some(|control, _data, _app| {
|
||||
@@ -136,13 +140,19 @@ pub fn create_watch(state: &AppState, screens: Vec<(usize, Arc<str>)>) -> Overla
|
||||
.as_millis()
|
||||
< 2000
|
||||
{
|
||||
app.tasks.push_back(Box::new(move |_app, o| {
|
||||
o[scr_idx].want_visible = !o[scr_idx].want_visible;
|
||||
}));
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(scr_idx),
|
||||
Box::new(|_app, o| {
|
||||
o.want_visible = !o.want_visible;
|
||||
}),
|
||||
));
|
||||
} else {
|
||||
app.tasks.push_back(Box::new(move |app, o| {
|
||||
o[scr_idx].reset(app);
|
||||
}));
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(scr_idx),
|
||||
Box::new(|app, o| {
|
||||
o.reset(app);
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -152,14 +162,17 @@ pub fn create_watch(state: &AppState, screens: Vec<(usize, Arc<str>)>) -> Overla
|
||||
let relative_to = RelativeTo::Hand(state.session.watch_hand);
|
||||
|
||||
OverlayData {
|
||||
name: "Watch".into(),
|
||||
size: (400, 200),
|
||||
width: 0.065,
|
||||
state: OverlayState {
|
||||
name: "Watch".into(),
|
||||
size: (400, 200),
|
||||
width: 0.065,
|
||||
want_visible: true,
|
||||
spawn_point: state.session.watch_pos.into(),
|
||||
spawn_rotation: state.session.watch_rot,
|
||||
relative_to,
|
||||
..Default::default()
|
||||
},
|
||||
backend: Box::new(canvas.build()),
|
||||
want_visible: true,
|
||||
relative_to,
|
||||
spawn_point: state.session.watch_pos,
|
||||
spawn_rotation: state.session.watch_rot,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user