|
|
|
@ -9,39 +9,35 @@ use std::{
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use crossbeam::{
|
|
|
|
|
channel::{Receiver, Sender},
|
|
|
|
|
channel::{Receiver, Select, Sender},
|
|
|
|
|
select,
|
|
|
|
|
};
|
|
|
|
|
use serde::Serialize;
|
|
|
|
|
// The prelude module re-exports the most common used items from statig.
|
|
|
|
|
use statig::prelude::*;
|
|
|
|
|
use tauri::{PhysicalPosition, PhysicalSize, Window};
|
|
|
|
|
use tauri::{Manager, PhysicalPosition, PhysicalSize, Window};
|
|
|
|
|
use underlayer::{Bounds, UnderlayEvent};
|
|
|
|
|
|
|
|
|
|
use crate::overlay;
|
|
|
|
|
|
|
|
|
|
pub enum StateEvent {
|
|
|
|
|
Visible,
|
|
|
|
|
Hidden,
|
|
|
|
|
Interactable,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub enum EnforceableEvent {
|
|
|
|
|
Force(StateEvent),
|
|
|
|
|
Request(StateEvent),
|
|
|
|
|
Relax,
|
|
|
|
|
pub enum Event {
|
|
|
|
|
State(State),
|
|
|
|
|
Bounds(Bounds),
|
|
|
|
|
AutoHide(bool),
|
|
|
|
|
Return
|
|
|
|
|
Return,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct Overlay {
|
|
|
|
|
bounds: Bounds,
|
|
|
|
|
window: Window,
|
|
|
|
|
auto_hide: bool,
|
|
|
|
|
previous: State,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct OverlaySettings {
|
|
|
|
|
pub auto_hide: bool,
|
|
|
|
|
}
|
|
|
|
|
pub type LockedOverlaySettings = Mutex<OverlaySettings>;
|
|
|
|
|
|
|
|
|
|
fn wrap_underlay_rx(rx: MpscReceiver<UnderlayEvent>) -> Receiver<UnderlayEvent> {
|
|
|
|
|
let (cb_underlay_tx, cb_underlay_rx) = crossbeam::channel::unbounded();
|
|
|
|
|
std::thread::spawn(move || {
|
|
|
|
@ -53,15 +49,18 @@ fn wrap_underlay_rx(rx: MpscReceiver<UnderlayEvent>) -> Receiver<UnderlayEvent>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Overlay {
|
|
|
|
|
pub fn new(window: Window, target_title: &str) -> Sender<EnforceableEvent> {
|
|
|
|
|
pub fn new(window: Window, target_title: &str) -> Sender<Event> {
|
|
|
|
|
log::warn!("New overlay object");
|
|
|
|
|
let mut overlay = Self {
|
|
|
|
|
window: window.clone(),
|
|
|
|
|
bounds: Bounds::default(),
|
|
|
|
|
auto_hide: true,
|
|
|
|
|
previous: State::hidden(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
window.manage(Mutex::new(OverlaySettings {
|
|
|
|
|
auto_hide: true,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
let mut fsm = Overlay::uninitialized_state_machine(overlay).init();
|
|
|
|
|
|
|
|
|
|
let underlay_rx = wrap_underlay_rx(underlayer::register(target_title));
|
|
|
|
@ -88,7 +87,7 @@ impl Overlay {
|
|
|
|
|
recv(ee_rx) -> msg => {
|
|
|
|
|
match msg {
|
|
|
|
|
Ok(event) => {
|
|
|
|
|
Self::handle_enforceable_event(&mut fsm, event);
|
|
|
|
|
Self::handle_event(&mut fsm, event);
|
|
|
|
|
},
|
|
|
|
|
Err(_) => break,
|
|
|
|
|
}
|
|
|
|
@ -107,60 +106,54 @@ impl Overlay {
|
|
|
|
|
|
|
|
|
|
fn handle_overlay_focused(fsm: &mut InitializedStateMachine<Overlay>, focus: bool) {
|
|
|
|
|
match fsm.state() {
|
|
|
|
|
State::Visible {} | State::EnforcedVisible {} => {
|
|
|
|
|
State::Visible {} => {
|
|
|
|
|
if focus {
|
|
|
|
|
fsm.window.set_ignore_cursor_events(true);
|
|
|
|
|
underlayer::focus_target();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn handle_underlay_event(fsm: &mut InitializedStateMachine<Overlay>, event: UnderlayEvent) {
|
|
|
|
|
log::info!("EVENT: {event:?}");
|
|
|
|
|
let settings = fsm.window.state::<LockedOverlaySettings>();
|
|
|
|
|
match event {
|
|
|
|
|
UnderlayEvent::Attach {
|
|
|
|
|
bounds,
|
|
|
|
|
has_access,
|
|
|
|
|
is_fullscreen,
|
|
|
|
|
} => {
|
|
|
|
|
fsm.handle(&EnforceableEvent::Bounds(bounds));
|
|
|
|
|
fsm.handle(&Event::Bounds(bounds));
|
|
|
|
|
}
|
|
|
|
|
UnderlayEvent::MoveResize(bounds) => fsm.handle(&Event::Bounds(bounds)),
|
|
|
|
|
UnderlayEvent::Detach => fsm.handle(&Event::State(State::hidden())),
|
|
|
|
|
UnderlayEvent::Focus => {
|
|
|
|
|
fsm.handle(&Event::State(State::Visible {}))
|
|
|
|
|
}
|
|
|
|
|
UnderlayEvent::MoveResize(bounds) => fsm.handle(&EnforceableEvent::Bounds(bounds)),
|
|
|
|
|
UnderlayEvent::Detach => fsm.handle(&EnforceableEvent::Request(StateEvent::Hidden)),
|
|
|
|
|
UnderlayEvent::Focus => fsm.handle(&EnforceableEvent::Request(StateEvent::Visible)),
|
|
|
|
|
UnderlayEvent::Blur => {
|
|
|
|
|
if !fsm.window.is_focused().unwrap() && fsm.auto_hide {
|
|
|
|
|
fsm.handle(&EnforceableEvent::Request(StateEvent::Hidden))
|
|
|
|
|
if !fsm.window.is_focused().unwrap()
|
|
|
|
|
&& fsm
|
|
|
|
|
.window
|
|
|
|
|
.state::<LockedOverlaySettings>()
|
|
|
|
|
.lock()
|
|
|
|
|
.is_ok_and(|s| s.auto_hide)
|
|
|
|
|
{
|
|
|
|
|
fsm.handle(&Event::State(State::Hidden {}))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
UnderlayEvent::X11FullscreenEvent { is_fullscreen } => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn handle_enforceable_event(
|
|
|
|
|
fsm: &mut InitializedStateMachine<Overlay>,
|
|
|
|
|
event: EnforceableEvent,
|
|
|
|
|
) {
|
|
|
|
|
fn handle_event(fsm: &mut InitializedStateMachine<Overlay>, event: Event) {
|
|
|
|
|
fsm.handle(&event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// let mut fsm = fsm::Overlay::uninitialized_state_machine(Default::default()).init();
|
|
|
|
|
// fsm.handle(&EnforceableEvent::Force(fsm::StateEvent::Visible));
|
|
|
|
|
// fsm.handle(&EnforceableEvent::Relax);
|
|
|
|
|
// fsm.handle(&EnforceableEvent::Request(fsm::StateEvent::Interactable));
|
|
|
|
|
// fsm.handle(&EnforceableEvent::Request(fsm::StateEvent::Hidden));
|
|
|
|
|
|
|
|
|
|
/// The `state_machine` procedural macro generates the `State` and `Superstate`
|
|
|
|
|
/// enums by parsing the function signatures with a `state`, `superstate` or
|
|
|
|
|
/// `action` attribute. It also implements the `statig::State` and
|
|
|
|
|
/// `statig::Superstate` traits. We also pass an argument that will add the
|
|
|
|
|
/// derive macro with the Debug trait to the `State` enum.
|
|
|
|
|
#[state_machine(
|
|
|
|
|
initial = "State::hidden()",
|
|
|
|
|
state(derive(Debug, Clone, PartialEq, Eq)),
|
|
|
|
|
state(derive(Debug, Clone, PartialEq, Eq, Serialize)),
|
|
|
|
|
superstate(derive(Debug)),
|
|
|
|
|
on_transition = "Self::on_transition"
|
|
|
|
|
)]
|
|
|
|
@ -177,114 +170,68 @@ impl Overlay {
|
|
|
|
|
.set_size(PhysicalSize::new(self.bounds.width, self.bounds.height));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log::info!("on_enter_visible");
|
|
|
|
|
log::trace!("on_enter_visible");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[action]
|
|
|
|
|
fn on_enter_interactable(&mut self) {
|
|
|
|
|
log::info!("on_enter_interactable");
|
|
|
|
|
log::trace!("on_enter_interactable");
|
|
|
|
|
self.window.set_ignore_cursor_events(false);
|
|
|
|
|
self.window.set_focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[action]
|
|
|
|
|
fn on_enter_hidden(&mut self) {
|
|
|
|
|
log::info!("on_enter_hidden");
|
|
|
|
|
log::trace!("on_enter_hidden");
|
|
|
|
|
self.window.hide();
|
|
|
|
|
self.window.set_resizable(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[action]
|
|
|
|
|
fn on_exit_interactable(&mut self) {
|
|
|
|
|
log::info!("on_exit_interactable");
|
|
|
|
|
log::trace!("on_exit_interactable");
|
|
|
|
|
self.window.set_ignore_cursor_events(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[state(superstate = "super_visible")]
|
|
|
|
|
fn enforced_visible(&mut self, event: &EnforceableEvent) -> Response<State> {
|
|
|
|
|
fn visible(&mut self, event: &Event) -> Response<State> {
|
|
|
|
|
match event {
|
|
|
|
|
EnforceableEvent::Force(StateEvent::Visible) => Super,
|
|
|
|
|
EnforceableEvent::Force(event) => Self::match_forced(event),
|
|
|
|
|
EnforceableEvent::Request(_) => Handled,
|
|
|
|
|
EnforceableEvent::Relax => Transition(State::visible()),
|
|
|
|
|
_ => Super,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[state(superstate = "super_visible")]
|
|
|
|
|
fn visible(&mut self, event: &EnforceableEvent) -> Response<State> {
|
|
|
|
|
match event {
|
|
|
|
|
EnforceableEvent::Force(underlying) => Self::match_forced(underlying),
|
|
|
|
|
EnforceableEvent::Request(underlying) => match underlying {
|
|
|
|
|
StateEvent::Visible => Super,
|
|
|
|
|
StateEvent::Hidden => Transition(State::hidden()),
|
|
|
|
|
StateEvent::Interactable => Transition(State::interactable()),
|
|
|
|
|
Event::State(underlying) => match underlying {
|
|
|
|
|
State::Visible {} => Handled,
|
|
|
|
|
State::Hidden {} => Transition(State::hidden()),
|
|
|
|
|
State::Interactable {} => Transition(State::interactable()),
|
|
|
|
|
},
|
|
|
|
|
EnforceableEvent::Relax => Super,
|
|
|
|
|
_ => Super,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[state(superstate = "super_hidden")]
|
|
|
|
|
fn enforced_hidden(&mut self, event: &EnforceableEvent) -> Response<State> {
|
|
|
|
|
match event {
|
|
|
|
|
EnforceableEvent::Force(StateEvent::Hidden) => Super,
|
|
|
|
|
EnforceableEvent::Force(event) => Self::match_forced(event),
|
|
|
|
|
EnforceableEvent::Request(_) => Handled,
|
|
|
|
|
EnforceableEvent::Relax => Transition(State::hidden()),
|
|
|
|
|
_ => Super,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[state(superstate = "super_hidden")]
|
|
|
|
|
fn hidden(&mut self, event: &EnforceableEvent) -> Response<State> {
|
|
|
|
|
fn hidden(&mut self, event: &Event) -> Response<State> {
|
|
|
|
|
match event {
|
|
|
|
|
EnforceableEvent::Force(underlying) => Self::match_forced(underlying),
|
|
|
|
|
EnforceableEvent::Request(underlying) => match underlying {
|
|
|
|
|
StateEvent::Visible => Transition(State::visible()),
|
|
|
|
|
StateEvent::Hidden => Super,
|
|
|
|
|
StateEvent::Interactable => Transition(State::interactable()),
|
|
|
|
|
Event::State(underlying) => match underlying {
|
|
|
|
|
State::Visible {} => Transition(State::visible()),
|
|
|
|
|
State::Hidden {} => Handled,
|
|
|
|
|
State::Interactable {} => Transition(State::interactable()),
|
|
|
|
|
},
|
|
|
|
|
EnforceableEvent::Relax => Super,
|
|
|
|
|
_ => Super,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[state(superstate = "super_interactable")]
|
|
|
|
|
fn enforced_interactable(&mut self, event: &EnforceableEvent) -> Response<State> {
|
|
|
|
|
match event {
|
|
|
|
|
EnforceableEvent::Force(StateEvent::Interactable) => Super,
|
|
|
|
|
EnforceableEvent::Force(event) => Self::match_forced(event),
|
|
|
|
|
EnforceableEvent::Request(_) => Handled,
|
|
|
|
|
EnforceableEvent::Relax => Transition(State::interactable()),
|
|
|
|
|
_ => Super,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[state(superstate = "super_interactable")]
|
|
|
|
|
fn interactable(&mut self, event: &EnforceableEvent) -> Response<State> {
|
|
|
|
|
fn interactable(&mut self, event: &Event) -> Response<State> {
|
|
|
|
|
match event {
|
|
|
|
|
EnforceableEvent::Force(underlying) => Self::match_forced(underlying),
|
|
|
|
|
EnforceableEvent::Request(underlying) => match underlying {
|
|
|
|
|
StateEvent::Visible => Transition(State::visible()),
|
|
|
|
|
StateEvent::Hidden => Transition(State::hidden()),
|
|
|
|
|
StateEvent::Interactable => Super,
|
|
|
|
|
Event::State(underlying) => match underlying {
|
|
|
|
|
State::Visible {} => Transition(State::visible()),
|
|
|
|
|
State::Hidden {} => Transition(State::hidden()),
|
|
|
|
|
State::Interactable {} => Handled,
|
|
|
|
|
},
|
|
|
|
|
EnforceableEvent::Relax => Super,
|
|
|
|
|
_ => Super,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn match_forced(event: &StateEvent) -> Response<State> {
|
|
|
|
|
match event {
|
|
|
|
|
StateEvent::Hidden => Transition(State::enforced_hidden()),
|
|
|
|
|
StateEvent::Interactable => Transition(State::enforced_interactable()),
|
|
|
|
|
StateEvent::Visible => Transition(State::enforced_visible()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[superstate(superstate = "common", entry_action = "on_enter_visible")]
|
|
|
|
|
fn super_visible(&mut self, event: &EnforceableEvent) -> Response<State> {
|
|
|
|
|
log::info!("in super_visible");
|
|
|
|
|
fn super_visible(&mut self, event: &Event) -> Response<State> {
|
|
|
|
|
log::trace!("in super_visible");
|
|
|
|
|
Super
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -293,8 +240,8 @@ impl Overlay {
|
|
|
|
|
entry_action = "on_enter_interactable",
|
|
|
|
|
exit_action = "on_exit_interactable"
|
|
|
|
|
)]
|
|
|
|
|
fn super_interactable(&mut self, event: &EnforceableEvent) -> Response<State> {
|
|
|
|
|
log::info!("in super_interactable");
|
|
|
|
|
fn super_interactable(&mut self, event: &Event) -> Response<State> {
|
|
|
|
|
log::trace!("in super_interactable");
|
|
|
|
|
Super
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -303,16 +250,16 @@ impl Overlay {
|
|
|
|
|
entry_action = "on_visible",
|
|
|
|
|
entry_action = "on_enter_hidden"
|
|
|
|
|
)]
|
|
|
|
|
fn super_hidden(&mut self, event: &EnforceableEvent) -> Response<State> {
|
|
|
|
|
log::info!("in super_hidden");
|
|
|
|
|
fn super_hidden(&mut self, event: &Event) -> Response<State> {
|
|
|
|
|
log::trace!("in super_hidden");
|
|
|
|
|
Super
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[superstate]
|
|
|
|
|
fn common(&mut self, event: &EnforceableEvent) -> Response<State> {
|
|
|
|
|
log::info!("in common");
|
|
|
|
|
fn common(&mut self, event: &Event) -> Response<State> {
|
|
|
|
|
log::trace!("in common");
|
|
|
|
|
match event {
|
|
|
|
|
EnforceableEvent::Bounds(bounds) => {
|
|
|
|
|
Event::Bounds(bounds) => {
|
|
|
|
|
self.bounds = *bounds;
|
|
|
|
|
if bounds.width > 0 && bounds.height > 0 {
|
|
|
|
|
self.window
|
|
|
|
@ -322,21 +269,19 @@ impl Overlay {
|
|
|
|
|
}
|
|
|
|
|
Super
|
|
|
|
|
}
|
|
|
|
|
EnforceableEvent::Relax => Super,
|
|
|
|
|
EnforceableEvent::AutoHide(auto_hide) => {
|
|
|
|
|
self.auto_hide = *auto_hide;
|
|
|
|
|
Super
|
|
|
|
|
}
|
|
|
|
|
EnforceableEvent::Return => {
|
|
|
|
|
log::info!("Returning to: {:?}", self.previous);
|
|
|
|
|
Event::Return => {
|
|
|
|
|
log::trace!("Returning to: {:?}", self.previous);
|
|
|
|
|
Transition(self.previous.clone())
|
|
|
|
|
}
|
|
|
|
|
_ => Super,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn on_transition(&mut self, src: &State, dest: &State) {
|
|
|
|
|
log::info!("from {src:?} to {dest:?}");
|
|
|
|
|
self.previous = src.clone();
|
|
|
|
|
fn on_transition(&mut self, src: &State, dest: &State) {
|
|
|
|
|
log::trace!("from {src:?} to {dest:?}");
|
|
|
|
|
if src != dest {
|
|
|
|
|
self.previous = src.clone();
|
|
|
|
|
}
|
|
|
|
|
self.window.emit("OverlayStateChange", dest);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|