Compare commits
No commits in common. "8f4a8b77bcf013faa3eefc90077a6e9fa1a73bf6" and "4da592ad45db0e5847eb1b45bb8dc365c03078c1" have entirely different histories.
8f4a8b77bc
...
4da592ad45
|
@ -2,12 +2,11 @@ use cairo::{
|
||||||
Context, FontSlant, FontWeight, Format, ImageSurface, LineCap, LinearGradient, Pattern,
|
Context, FontSlant, FontWeight, Format, ImageSurface, LineCap, LinearGradient, Pattern,
|
||||||
TextExtents,
|
TextExtents,
|
||||||
};
|
};
|
||||||
use glib::{GString, Object};
|
use glib::Object;
|
||||||
use gtk::{gdk::Key, prelude::*, subclass::prelude::*, EventControllerKey};
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{Arc, RwLock},
|
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,85 +19,7 @@ enum Event {
|
||||||
Time(Duration),
|
Time(Duration),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
struct TimeoutAnimation {
|
||||||
pub enum State {
|
|
||||||
Running {
|
|
||||||
last_update: Instant,
|
|
||||||
deadline: Instant,
|
|
||||||
timeout: Option<TimeoutAnimation>,
|
|
||||||
},
|
|
||||||
Paused {
|
|
||||||
time_remaining: Duration,
|
|
||||||
timeout: Option<TimeoutAnimation>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
fn new(countdown: Duration) -> Self {
|
|
||||||
Self::Paused {
|
|
||||||
time_remaining: countdown,
|
|
||||||
timeout: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start(&mut self) {
|
|
||||||
if let Self::Paused {
|
|
||||||
time_remaining,
|
|
||||||
timeout,
|
|
||||||
} = self
|
|
||||||
{
|
|
||||||
*self = Self::Running {
|
|
||||||
last_update: Instant::now(),
|
|
||||||
deadline: Instant::now() + *time_remaining,
|
|
||||||
timeout: timeout.clone(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pause(&mut self) {
|
|
||||||
if let Self::Running {
|
|
||||||
deadline, timeout, ..
|
|
||||||
} = self
|
|
||||||
{
|
|
||||||
*self = Self::Paused {
|
|
||||||
time_remaining: *deadline - Instant::now(),
|
|
||||||
timeout: timeout.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_pause(&mut self) {
|
|
||||||
match self {
|
|
||||||
Self::Running { .. } => self.pause(),
|
|
||||||
Self::Paused { .. } => self.start(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(&mut self, now: Instant) {
|
|
||||||
if let Self::Running {
|
|
||||||
last_update,
|
|
||||||
deadline,
|
|
||||||
timeout,
|
|
||||||
} = self
|
|
||||||
{
|
|
||||||
*last_update = now;
|
|
||||||
if let Some(ref mut timeout) = timeout {
|
|
||||||
// TODO: figure out the actual number of frames
|
|
||||||
timeout.tick(1);
|
|
||||||
}
|
|
||||||
if *last_update > *deadline && timeout.is_none() {
|
|
||||||
*timeout = Some(TimeoutAnimation {
|
|
||||||
intensity: 1.,
|
|
||||||
duration: 1.,
|
|
||||||
ascending: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct TimeoutAnimation {
|
|
||||||
intensity: f64,
|
intensity: f64,
|
||||||
duration: f64,
|
duration: f64,
|
||||||
ascending: bool,
|
ascending: bool,
|
||||||
|
@ -125,12 +46,13 @@ impl TimeoutAnimation {
|
||||||
|
|
||||||
pub struct SplashPrivate {
|
pub struct SplashPrivate {
|
||||||
text: Rc<RefCell<String>>,
|
text: Rc<RefCell<String>>,
|
||||||
|
time: Rc<RefCell<Duration>>,
|
||||||
background: Rc<RefCell<Pattern>>,
|
background: Rc<RefCell<Pattern>>,
|
||||||
time_extents: Rc<RefCell<Option<TextExtents>>>,
|
time_extents: Rc<RefCell<Option<TextExtents>>>,
|
||||||
width: Rc<RefCell<i32>>,
|
width: Rc<RefCell<i32>>,
|
||||||
height: Rc<RefCell<i32>>,
|
height: Rc<RefCell<i32>>,
|
||||||
|
|
||||||
state: Rc<RefCell<State>>,
|
timeout_animation: Rc<RefCell<Option<TimeoutAnimation>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SplashPrivate {
|
impl SplashPrivate {
|
||||||
|
@ -138,8 +60,8 @@ impl SplashPrivate {
|
||||||
*self.text.borrow_mut() = text;
|
*self.text.borrow_mut() = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_state(&self, state: State) {
|
fn set_time(&self, time: Duration) {
|
||||||
*self.state.borrow_mut() = state;
|
*self.time.borrow_mut() = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn redraw_background(&self) {
|
fn redraw_background(&self) {
|
||||||
|
@ -252,12 +174,12 @@ impl ObjectSubclass for SplashPrivate {
|
||||||
|
|
||||||
SplashPrivate {
|
SplashPrivate {
|
||||||
text: Rc::new(RefCell::new(String::from(""))),
|
text: Rc::new(RefCell::new(String::from(""))),
|
||||||
|
time: Rc::new(RefCell::new(Duration::ZERO)),
|
||||||
background: Rc::new(RefCell::new(background)),
|
background: Rc::new(RefCell::new(background)),
|
||||||
time_extents: Rc::new(RefCell::new(None)),
|
time_extents: Rc::new(RefCell::new(None)),
|
||||||
width: Rc::new(RefCell::new(WIDTH)),
|
width: Rc::new(RefCell::new(WIDTH)),
|
||||||
height: Rc::new(RefCell::new(HEIGHT)),
|
height: Rc::new(RefCell::new(HEIGHT)),
|
||||||
|
timeout_animation: Rc::new(RefCell::new(None)),
|
||||||
state: Rc::new(RefCell::new(State::new(Duration::ZERO))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,13 +192,13 @@ glib::wrapper! {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Splash {
|
impl Splash {
|
||||||
pub fn new(text: String, state: State) -> Self {
|
pub fn new(text: String, time: Duration) -> Self {
|
||||||
let s: Self = Object::builder().build();
|
let s: Self = Object::builder().build();
|
||||||
s.set_width_request(WIDTH);
|
s.set_width_request(WIDTH);
|
||||||
s.set_height_request(HEIGHT);
|
s.set_height_request(HEIGHT);
|
||||||
|
|
||||||
s.imp().set_text(text);
|
s.imp().set_text(text);
|
||||||
s.imp().set_state(state);
|
s.imp().set_time(time);
|
||||||
s.imp().redraw_background();
|
s.imp().redraw_background();
|
||||||
|
|
||||||
s.set_draw_func({
|
s.set_draw_func({
|
||||||
|
@ -286,18 +208,15 @@ impl Splash {
|
||||||
let _ = context.set_source(&*background);
|
let _ = context.set_source(&*background);
|
||||||
let _ = context.paint();
|
let _ = context.paint();
|
||||||
|
|
||||||
let state = s.imp().state.borrow().clone();
|
let time = s.imp().time.borrow().clone();
|
||||||
|
|
||||||
let time = match state {
|
|
||||||
State::Running { deadline, .. } => deadline - Instant::now(),
|
|
||||||
State::Paused { time_remaining, .. } => time_remaining,
|
|
||||||
};
|
|
||||||
let minutes = time.as_secs() / 60;
|
let minutes = time.as_secs() / 60;
|
||||||
let seconds = time.as_secs() % 60;
|
let seconds = time.as_secs() % 60;
|
||||||
|
|
||||||
let center_x = width as f64 / 2.;
|
let center_x = width as f64 / 2.;
|
||||||
let center_y = height as f64 / 2.;
|
let center_y = height as f64 / 2.;
|
||||||
|
|
||||||
|
{}
|
||||||
|
|
||||||
{
|
{
|
||||||
context.select_font_face(
|
context.select_font_face(
|
||||||
"Alegreya Sans SC",
|
"Alegreya Sans SC",
|
||||||
|
@ -318,30 +237,39 @@ impl Splash {
|
||||||
let time_baseline_x = center_x - time_extents.width() / 2.;
|
let time_baseline_x = center_x - time_extents.width() / 2.;
|
||||||
let time_baseline_y = center_y + 100.;
|
let time_baseline_y = center_y + 100.;
|
||||||
|
|
||||||
|
/*
|
||||||
|
match *s.imp().timeout_animation.borrow() {
|
||||||
|
Some(ref animation) => {
|
||||||
|
context.set_source_rgb(animation.intensity / 2., 0., 0.);
|
||||||
|
RoundedRectangle {
|
||||||
|
x: time_baseline_x - 5.,
|
||||||
|
y: time_baseline_y + 10.,
|
||||||
|
width: time_extents.width() + 15.,
|
||||||
|
height: time_extents.height() + 15.,
|
||||||
|
}
|
||||||
|
.draw(&context);
|
||||||
|
// let _ = context.fill();
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
let gradient = LinearGradient::new(
|
let gradient = LinearGradient::new(
|
||||||
time_baseline_x,
|
time_baseline_x,
|
||||||
time_baseline_y - time_extents.height(),
|
time_baseline_y - time_extents.height(),
|
||||||
time_baseline_x,
|
time_baseline_x,
|
||||||
time_baseline_y,
|
time_baseline_y,
|
||||||
);
|
);
|
||||||
let (running, timeout_animation) = match state {
|
match *s.imp().timeout_animation.borrow() {
|
||||||
State::Running { timeout, .. } => (true, timeout.clone()),
|
|
||||||
State::Paused { timeout, .. } => (false, timeout.clone()),
|
|
||||||
};
|
|
||||||
match timeout_animation {
|
|
||||||
Some(ref animation) => {
|
Some(ref animation) => {
|
||||||
gradient.add_color_stop_rgba(0.2, 0.2, 0.0, 1.0, animation.intensity);
|
gradient.add_color_stop_rgba(0.2, 0.2, 0.0, 1.0, animation.intensity);
|
||||||
gradient.add_color_stop_rgba(0.8, 0.7, 0.0, 1.0, animation.intensity);
|
gradient.add_color_stop_rgba(0.8, 0.7, 0.0, 1.0, animation.intensity);
|
||||||
let _ = context.set_source(gradient);
|
let _ = context.set_source(gradient);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if running {
|
|
||||||
gradient.add_color_stop_rgb(0.2, 0.2, 0.0, 1.0);
|
gradient.add_color_stop_rgb(0.2, 0.2, 0.0, 1.0);
|
||||||
gradient.add_color_stop_rgb(0.8, 0.7, 0.0, 1.0);
|
gradient.add_color_stop_rgb(0.8, 0.7, 0.0, 1.0);
|
||||||
let _ = context.set_source(gradient);
|
let _ = context.set_source(gradient);
|
||||||
} else {
|
|
||||||
context.set_source_rgb(0.3, 0.3, 0.3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.move_to(time_baseline_x, time_baseline_y);
|
context.move_to(time_baseline_x, time_baseline_y);
|
||||||
|
@ -376,10 +304,28 @@ impl Splash {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_state(&self, state: State) {
|
pub fn set_time(&self, time: Duration) {
|
||||||
self.imp().set_state(state);
|
self.imp().set_time(time);
|
||||||
|
if time == Duration::ZERO {
|
||||||
|
*self.imp().timeout_animation.borrow_mut() = Some(TimeoutAnimation {
|
||||||
|
intensity: 1.,
|
||||||
|
duration: 1.,
|
||||||
|
ascending: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
self.queue_draw();
|
self.queue_draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tick(&self, frames_elapsed: u8) {
|
||||||
|
let mut animation = self.imp().timeout_animation.borrow_mut();
|
||||||
|
match *animation {
|
||||||
|
Some(ref mut animation) => {
|
||||||
|
animation.tick(frames_elapsed);
|
||||||
|
self.queue_draw();
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AsymLineCutout {
|
struct AsymLineCutout {
|
||||||
|
@ -558,55 +504,37 @@ fn main() {
|
||||||
|
|
||||||
app.connect_activate(move |app| {
|
app.connect_activate(move |app| {
|
||||||
let (gtk_tx, gtk_rx) =
|
let (gtk_tx, gtk_rx) =
|
||||||
gtk::glib::MainContext::channel::<State>(gtk::glib::PRIORITY_DEFAULT);
|
gtk::glib::MainContext::channel::<Event>(gtk::glib::PRIORITY_DEFAULT);
|
||||||
let window = gtk::ApplicationWindow::new(app);
|
let window = gtk::ApplicationWindow::new(app);
|
||||||
window.present();
|
window.present();
|
||||||
|
|
||||||
let state = State::new(Duration::from_secs(5 * 60));
|
let mut countdown = Duration::from_secs(5);
|
||||||
let splash = Splash::new("GTK Kifu".to_owned(), state.clone());
|
let mut next_tick = Instant::now() + Duration::from_secs(1);
|
||||||
let state = Arc::new(RwLock::new(state));
|
|
||||||
|
let splash = Splash::new("GTK Kifu".to_owned(), countdown.clone());
|
||||||
|
|
||||||
window.set_child(Some(&splash));
|
window.set_child(Some(&splash));
|
||||||
|
// window.fullscreen();
|
||||||
|
|
||||||
window.connect_maximized_notify(|window| {
|
window.connect_maximized_notify(|window| {
|
||||||
window.fullscreen();
|
window.fullscreen();
|
||||||
});
|
});
|
||||||
|
|
||||||
let keyboard_events = EventControllerKey::new();
|
gtk_rx.attach(None, move |event| {
|
||||||
keyboard_events.connect_key_released({
|
|
||||||
let window = window.clone();
|
|
||||||
let state = state.clone();
|
|
||||||
move |_, key, _, _| {
|
|
||||||
let name = key
|
|
||||||
.name()
|
|
||||||
.map(|s| s.as_str().to_owned())
|
|
||||||
.unwrap_or("".to_owned());
|
|
||||||
match name.as_ref() {
|
|
||||||
"Escape" => window.unfullscreen(),
|
|
||||||
"space" => state.write().unwrap().start_pause(),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.add_controller(keyboard_events);
|
|
||||||
|
|
||||||
gtk_rx.attach(None, move |state| {
|
|
||||||
/*
|
|
||||||
match event {
|
match event {
|
||||||
Event::Frames(frames) => splash.tick(frames),
|
Event::Frames(frames) => splash.tick(frames),
|
||||||
Event::Time(time) => splash.set_time(time),
|
Event::Time(time) => splash.set_time(time),
|
||||||
};
|
};
|
||||||
*/
|
|
||||||
splash.set_state(state);
|
|
||||||
Continue(true)
|
Continue(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || loop {
|
||||||
state.write().unwrap().start();
|
|
||||||
loop {
|
|
||||||
std::thread::sleep(Duration::from_millis(1000 / 60));
|
std::thread::sleep(Duration::from_millis(1000 / 60));
|
||||||
state.write().unwrap().run(Instant::now());
|
let _ = gtk_tx.send(Event::Frames(1));
|
||||||
let _ = gtk_tx.send(state.read().unwrap().clone());
|
if Instant::now() >= next_tick && countdown > Duration::from_secs(0) {
|
||||||
|
countdown = countdown - Duration::from_secs(1);
|
||||||
|
let _ = gtk_tx.send(Event::Time(countdown));
|
||||||
|
next_tick = next_tick + Duration::from_secs(1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue