Abstract time-travel feature in TimeMachine struct
This commit is contained in:
parent
7c6155242c
commit
5ce3892a1e
2 changed files with 116 additions and 48 deletions
|
|
@ -7,6 +7,7 @@ use iced_widget::runtime;
|
||||||
use iced_widget::runtime::futures;
|
use iced_widget::runtime::futures;
|
||||||
|
|
||||||
mod executor;
|
mod executor;
|
||||||
|
mod time_machine;
|
||||||
|
|
||||||
use crate::core::keyboard;
|
use crate::core::keyboard;
|
||||||
use crate::core::theme::{self, Base, Theme};
|
use crate::core::theme::{self, Base, Theme};
|
||||||
|
|
@ -16,6 +17,7 @@ use crate::core::{Color, Element, Length::Fill};
|
||||||
use crate::futures::Subscription;
|
use crate::futures::Subscription;
|
||||||
use crate::program::Program;
|
use crate::program::Program;
|
||||||
use crate::runtime::Task;
|
use crate::runtime::Task;
|
||||||
|
use crate::time_machine::TimeMachine;
|
||||||
use crate::widget::{
|
use crate::widget::{
|
||||||
bottom_right, button, center, column, container, horizontal_space, opaque,
|
bottom_right, button, center, column, container, horizontal_space, opaque,
|
||||||
row, scrollable, stack, text, themer,
|
row, scrollable, stack, text, themer,
|
||||||
|
|
@ -115,8 +117,7 @@ where
|
||||||
state: P::State,
|
state: P::State,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
show_notification: bool,
|
show_notification: bool,
|
||||||
rewind: Option<P::State>,
|
time_machine: TimeMachine<P>,
|
||||||
messages: Vec<P::Message>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -143,14 +144,13 @@ impl<P> DevTools<P>
|
||||||
where
|
where
|
||||||
P: Program + 'static,
|
P: Program + 'static,
|
||||||
{
|
{
|
||||||
pub fn new(state: P::State) -> (Self, Task<Message>) {
|
fn new(state: P::State) -> (Self, Task<Message>) {
|
||||||
(
|
(
|
||||||
Self {
|
Self {
|
||||||
state,
|
state,
|
||||||
mode: Mode::None,
|
mode: Mode::None,
|
||||||
show_notification: true,
|
show_notification: true,
|
||||||
rewind: None,
|
time_machine: TimeMachine::new(),
|
||||||
messages: Vec::new(),
|
|
||||||
},
|
},
|
||||||
executor::spawn_blocking(|mut sender| {
|
executor::spawn_blocking(|mut sender| {
|
||||||
thread::sleep(seconds(2));
|
thread::sleep(seconds(2));
|
||||||
|
|
@ -160,11 +160,11 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn title(&self, program: &P, window: window::Id) -> String {
|
fn title(&self, program: &P, window: window::Id) -> String {
|
||||||
program.title(&self.state, window)
|
program.title(&self.state, window)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, program: &P, event: Event<P>) -> Task<Event<P>> {
|
fn update(&mut self, program: &P, event: Event<P>) -> Task<Event<P>> {
|
||||||
match event {
|
match event {
|
||||||
Event::Message(message) => match message {
|
Event::Message(message) => match message {
|
||||||
Message::HideNotification => {
|
Message::HideNotification => {
|
||||||
|
|
@ -254,13 +254,12 @@ where
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Event::Program(message) => {
|
Event::Program(message) => {
|
||||||
#[cfg(feature = "time-travel")]
|
|
||||||
{
|
{
|
||||||
self.messages.push(message.clone());
|
self.time_machine.push(&message);
|
||||||
}
|
|
||||||
|
|
||||||
if self.rewind.is_some() {
|
if self.time_machine.is_rewinding() {
|
||||||
debug::enable();
|
debug::enable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = debug::update(&message);
|
let span = debug::update(&message);
|
||||||
|
|
@ -268,7 +267,7 @@ where
|
||||||
debug::tasks_spawned(task.units());
|
debug::tasks_spawned(task.units());
|
||||||
span.finish();
|
span.finish();
|
||||||
|
|
||||||
if self.rewind.is_some() {
|
if self.time_machine.is_rewinding() {
|
||||||
debug::disable();
|
debug::disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,31 +276,10 @@ where
|
||||||
Event::Command(command) => {
|
Event::Command(command) => {
|
||||||
match command {
|
match command {
|
||||||
debug::Command::RewindTo { message } => {
|
debug::Command::RewindTo { message } => {
|
||||||
#[cfg(feature = "time-travel")]
|
self.time_machine.rewind(program, message);
|
||||||
{
|
|
||||||
let (mut state, _) = program.boot();
|
|
||||||
|
|
||||||
if message < self.messages.len() {
|
|
||||||
// TODO: Run concurrently (?)
|
|
||||||
for message in &self.messages[0..message] {
|
|
||||||
let _ = program
|
|
||||||
.update(&mut state, message.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.rewind = Some(state);
|
|
||||||
debug::disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "time-travel"))]
|
|
||||||
let _ = message;
|
|
||||||
}
|
}
|
||||||
debug::Command::GoLive => {
|
debug::Command::GoLive => {
|
||||||
#[cfg(feature = "time-travel")]
|
self.time_machine.go_to_present();
|
||||||
{
|
|
||||||
self.rewind = None;
|
|
||||||
debug::enable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -311,17 +289,17 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(
|
fn view(
|
||||||
&self,
|
&self,
|
||||||
program: &P,
|
program: &P,
|
||||||
window: window::Id,
|
window: window::Id,
|
||||||
) -> Element<'_, Event<P>, P::Theme, P::Renderer> {
|
) -> Element<'_, Event<P>, P::Theme, P::Renderer> {
|
||||||
let state = self.rewind.as_ref().unwrap_or(&self.state);
|
let state = self.state();
|
||||||
|
|
||||||
let view = {
|
let view = {
|
||||||
let view = program.view(state, window);
|
let view = program.view(state, window);
|
||||||
|
|
||||||
if self.rewind.is_some() {
|
if self.time_machine.is_rewinding() {
|
||||||
view.map(|_| Event::Discard)
|
view.map(|_| Event::Discard)
|
||||||
} else {
|
} else {
|
||||||
view.map(Event::Program)
|
view.map(Event::Program)
|
||||||
|
|
@ -436,10 +414,9 @@ where
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscription(&self, program: &P) -> Subscription<Event<P>> {
|
fn subscription(&self, program: &P) -> Subscription<Event<P>> {
|
||||||
let subscription =
|
let subscription =
|
||||||
program.subscription(&self.state).map(Event::Program);
|
program.subscription(&self.state).map(Event::Program);
|
||||||
|
|
||||||
debug::subscriptions_tracked(subscription.units());
|
debug::subscriptions_tracked(subscription.units());
|
||||||
|
|
||||||
let hotkeys =
|
let hotkeys =
|
||||||
|
|
@ -456,17 +433,20 @@ where
|
||||||
Subscription::batch([subscription, hotkeys, commands])
|
Subscription::batch([subscription, hotkeys, commands])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn theme(&self, program: &P, window: window::Id) -> P::Theme {
|
fn theme(&self, program: &P, window: window::Id) -> P::Theme {
|
||||||
program.theme(self.rewind.as_ref().unwrap_or(&self.state), window)
|
program.theme(self.state(), window)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn style(&self, program: &P, theme: &P::Theme) -> theme::Style {
|
fn style(&self, program: &P, theme: &P::Theme) -> theme::Style {
|
||||||
program.style(self.rewind.as_ref().unwrap_or(&self.state), theme)
|
program.style(self.state(), theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scale_factor(&self, program: &P, window: window::Id) -> f64 {
|
fn scale_factor(&self, program: &P, window: window::Id) -> f64 {
|
||||||
program
|
program.scale_factor(self.state(), window)
|
||||||
.scale_factor(self.rewind.as_ref().unwrap_or(&self.state), window)
|
}
|
||||||
|
|
||||||
|
fn state(&self) -> &P::State {
|
||||||
|
self.time_machine.state().unwrap_or(&self.state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
88
devtools/src/time_machine.rs
Normal file
88
devtools/src/time_machine.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
use crate::Program;
|
||||||
|
|
||||||
|
#[cfg(feature = "time-travel")]
|
||||||
|
pub struct TimeMachine<P>
|
||||||
|
where
|
||||||
|
P: Program,
|
||||||
|
{
|
||||||
|
state: Option<P::State>,
|
||||||
|
messages: Vec<P::Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time-travel")]
|
||||||
|
impl<P> TimeMachine<P>
|
||||||
|
where
|
||||||
|
P: Program,
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
state: None,
|
||||||
|
messages: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_rewinding(&self) -> bool {
|
||||||
|
self.state.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, message: &P::Message) {
|
||||||
|
self.messages.push(message.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rewind(&mut self, program: &P, message: usize) {
|
||||||
|
let (mut state, _) = program.boot();
|
||||||
|
|
||||||
|
if message < self.messages.len() {
|
||||||
|
// TODO: Run concurrently (?)
|
||||||
|
for message in &self.messages[0..message] {
|
||||||
|
let _ = program.update(&mut state, message.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state = Some(state);
|
||||||
|
crate::debug::disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_to_present(&mut self) {
|
||||||
|
self.state = None;
|
||||||
|
crate::debug::enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state(&self) -> Option<&P::State> {
|
||||||
|
self.state.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "time-travel"))]
|
||||||
|
pub struct TimeMachine<P>
|
||||||
|
where
|
||||||
|
P: Program,
|
||||||
|
{
|
||||||
|
_program: std::marker::PhantomData<P>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "time-travel"))]
|
||||||
|
impl<P> TimeMachine<P>
|
||||||
|
where
|
||||||
|
P: Program,
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
_program: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_rewinding(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, _message: &P::Message) {}
|
||||||
|
|
||||||
|
pub fn rewind(&mut self, _program: &P, _message: usize) {}
|
||||||
|
|
||||||
|
pub fn go_to_present(&mut self) {}
|
||||||
|
|
||||||
|
pub fn state(&self) -> Option<&P::State> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue