Expose Timer API to rust API

And use it in the puzzle demo to implement the auto play mode
This commit is contained in:
Olivier Goffart 2020-11-16 12:14:27 +01:00
parent eadfdbe5fb
commit 0672f4b3cd
5 changed files with 66 additions and 4 deletions

View file

@ -156,6 +156,7 @@ pub use sixtyfps_corelib::model::{
};
pub use sixtyfps_corelib::sharedarray::SharedArray;
pub use sixtyfps_corelib::string::SharedString;
pub use sixtyfps_corelib::timers::{Timer, TimerMode};
pub use sixtyfps_corelib::{ARGBColor, Color};
/// internal re_exports used by the macro generated

View file

@ -13,8 +13,9 @@ Remaining feature to implement to have parity:
Seatle). Note that this feature is kind of broken in the flutter example as it is only applied
when changing themes
* Expanding cirle animation when pressing a tile.
* Auto-play mode. Including animation of the auto-play checkbox
* Animation of the auto-play checkbox.
* When the puzzle is finished, the last tile is added, and the tiles are growing in the Seatle theme,
or a hand apears, and the puzzle cannot be moved.
* The different styles are well separated in different files.
* Shadow on the tiles

View file

@ -48,6 +48,7 @@ struct AppState {
/// An array of 16 values wixh represent a 4x4 matrix containing the piece number in that
/// position. -1 is no piece.
positions: Vec<i8>,
auto_play_timer: sixtyfps::Timer,
}
impl AppState {
@ -99,6 +100,24 @@ impl AppState {
self.set_pieces_pos(self.positions[swap as usize] as _, swap);
}
}
fn random_move(&mut self) {
let mut rng = rand::thread_rng();
let hole = self.positions.iter().position(|x| *x == -1).unwrap() as i8;
let mut p;
loop {
p = rand::Rng::gen_range(&mut rng, 0, 16);
if hole == p {
continue;
} else if hole % 4 == p % 4 {
break;
} else if hole / 4 == p / 4 {
break;
}
}
let p = self.positions[p as usize];
self.piece_clicked(p)
}
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))]
@ -113,12 +132,34 @@ pub fn main() {
pieces: Rc::new(sixtyfps::VecModel::<Piece>::from(vec![Piece::default(); 15])),
main_window: main_window.as_weak(),
positions: vec![],
auto_play_timer: Default::default(),
}));
state.borrow_mut().randomize();
main_window.as_ref().set_pieces(sixtyfps::ModelHandle::new(state.borrow().pieces.clone()));
let state_copy = state.clone();
main_window.as_ref().on_piece_cliked(move |p| state_copy.borrow_mut().piece_clicked(p as i8));
main_window.as_ref().on_piece_cliked(move |p| {
state_copy.borrow().auto_play_timer.stop();
state_copy.borrow().main_window.upgrade().map(|x| x.as_ref().set_auto_play(false));
state_copy.borrow_mut().piece_clicked(p as i8);
});
let state_copy = state.clone();
main_window.as_ref().on_reset(move || state_copy.borrow_mut().randomize());
let state_copy = state.clone();
main_window.as_ref().on_enable_auto_mode(move |enabled| {
if enabled {
let state_weak = Rc::downgrade(&state_copy);
state_copy.borrow().auto_play_timer.start(
sixtyfps::TimerMode::Repeated,
std::time::Duration::from_millis(200),
Box::new(move || {
if let Some(state) = state_weak.upgrade() {
state.borrow_mut().random_move();
}
}),
);
} else {
state_copy.borrow().auto_play_timer.stop();
}
});
main_window.run();
}

View file

@ -33,6 +33,8 @@ struct Theme := {
export MainWindow := Window {
signal piece_cliked(int);
signal reset();
signal enable_auto_mode(bool);
property <bool> auto_play;
property <int> moves;
property <int> tiles-left;
property <[Piece]> pieces: [
@ -206,7 +208,7 @@ export MainWindow := Window {
+ (parent.width - (4*pieces_size + 3*pieces_spacing))/2;
y: px * (pieces_size + pieces_spacing)
+ (parent.height - (4*pieces_size + 3*pieces_spacing))/2;
animate px , py { duration: 200ms; easing: cubic-bezier(0.17,0.76,0.4,1.9); }
animate px , py { duration: 170ms; easing: cubic-bezier(0.17,0.76,0.4,1.75); }
animate border-width, border-radius { duration: 500ms; easing: ease-out; }
if (current-theme-index == 1) : Rectangle {
@ -259,6 +261,21 @@ export MainWindow := Window {
clicked => { root.reset(); }
}
}
Text {
// FIXME: this should be a rectangle with an animated ✓
text: auto_play ? " ☑ " : " ☐ ";
color: auto_play ? current-theme.game-highlight-color : current-theme.game-text-color;
animate color { duration: 200ms; }
vertical-alignment: align-center;
TouchArea {
width: 100%;
height: 100%;
clicked => {
auto_play = !auto_play;
root.enable_auto_mode(auto_play);
}
}
}
Rectangle {} // stretch
Text {
text: root.moves;

View file

@ -19,6 +19,8 @@ use std::cell::{Cell, RefCell};
type TimerCallback = Box<dyn Fn()>;
/// The TimerMode specifies what should happen after the timer fired.
///
/// Used by the [`Timer::start`] function.
#[derive(Copy, Clone)]
pub enum TimerMode {
/// A SingleShot timer is fired only once.
@ -40,7 +42,7 @@ impl Timer {
///
/// Arguments:
/// * `mode`: The timer mode to apply, i.e. whether to repeatedly fire the timer or just once.
/// * `duration`: The duration from now until when the fire should fire.
/// * `duration`: The duration from now until when the timer should fire.
/// * `callback`: The function to call when the time has been reached or exceeded.
pub fn start(&self, mode: TimerMode, duration: std::time::Duration, callback: TimerCallback) {
CURRENT_TIMERS.with(|timers| {