91 lines
2.6 KiB
Rust
91 lines
2.6 KiB
Rust
use std::{time::Duration, thread::ThreadId};
|
|
use adw::prelude::ButtonExt;
|
|
use gtk::prelude::WidgetExt;
|
|
use relm4::*;
|
|
|
|
/// A message that can be provided to the runner
|
|
#[derive(Debug)]
|
|
pub enum RunMsg {
|
|
Toggle,
|
|
Pass(ThreadId),
|
|
}
|
|
|
|
/// A message that the runner can forward to the app
|
|
#[derive(Debug)]
|
|
pub enum RunOutput {
|
|
Advance,
|
|
}
|
|
|
|
/// The runner's internal state
|
|
pub struct RunModel {
|
|
running: bool,
|
|
id: Option<ThreadId>,
|
|
}
|
|
|
|
#[relm4::component(pub)]
|
|
impl SimpleComponent for RunModel {
|
|
type Widgets = RunWidgets;
|
|
type Init = ();
|
|
type Input = RunMsg;
|
|
type Output = RunOutput;
|
|
|
|
view! {
|
|
#[name = "button"]
|
|
>k::Button {
|
|
#[watch]
|
|
set_tooltip_text: Some(if model.running { "Pause" } else { "Play" }),
|
|
#[watch]
|
|
set_icon_name: if model.running { "media-playback-stop-symbolic" } else { "media-playback-start-symbolic" },
|
|
connect_clicked[sender] => move |_| {
|
|
sender.input(RunMsg::Toggle);
|
|
},
|
|
}
|
|
}
|
|
|
|
fn init(
|
|
_init: Self::Init,
|
|
root: &Self::Root,
|
|
sender: ComponentSender<Self>,
|
|
) -> ComponentParts<Self> {
|
|
let model = RunModel {
|
|
running: false,
|
|
id: None,
|
|
};
|
|
|
|
let widgets = view_output!();
|
|
|
|
ComponentParts { model, widgets }
|
|
}
|
|
|
|
fn update(
|
|
&mut self,
|
|
input: RunMsg,
|
|
sender: ComponentSender<Self>,
|
|
) {
|
|
match input {
|
|
// Toggle the play-pause state of the runner
|
|
RunMsg::Toggle => {
|
|
self.running = !self.running;
|
|
}
|
|
// Called every 500ms when running
|
|
RunMsg::Pass(id) => {
|
|
if self.id != Some(id) {return}
|
|
}
|
|
}
|
|
/* Here, to prevent the possibility of a user pressing the play button twice in quick
|
|
* succession, spawning two threads that are both advancing the game, the runner keeps
|
|
* track of which thread id is the most recently spawned. If a thread detects that it is
|
|
* not the most recent, it will close.
|
|
*/
|
|
if self.running {
|
|
sender.output(RunOutput::Advance).unwrap();
|
|
self.id = Some(std::thread::spawn(move || {
|
|
// Note that timing is handled by the application, not the library. Another
|
|
// developer could change it
|
|
std::thread::sleep(Duration::from_millis(500));
|
|
sender.input(RunMsg::Pass(std::thread::current().id()));
|
|
}).thread().id());
|
|
}
|
|
}
|
|
}
|