run_in_bg and Dynamic<&'static str>

This commit is contained in:
Jonathan Johnson 2023-11-27 09:27:37 -08:00
parent 4d31719392
commit aeb55e0b94
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
4 changed files with 92 additions and 16 deletions

View file

@ -58,7 +58,7 @@ use kludgine::Color;
use crate::animation::easings::Linear;
use crate::styles::{Component, RequireInvalidation};
use crate::utils::{IgnorePoison, UnwindsafeCondvar};
use crate::utils::{run_in_bg, IgnorePoison, UnwindsafeCondvar};
use crate::value::Dynamic;
static ANIMATIONS: Mutex<Animating> = Mutex::new(Animating::new());
@ -488,6 +488,20 @@ impl AnimationHandle {
thread_state().run_unattached(id);
}
}
/// Returns true if this animation is still running.
#[must_use]
pub fn is_running(&self) -> bool {
let Some(id) = self.0 else { return false };
thread_state().running.contains(&id)
}
/// Returns true if this animation is complete.
#[must_use]
pub fn is_complete(&self) -> bool {
!self.is_running()
}
}
impl Drop for AnimationHandle {
@ -645,7 +659,7 @@ where
/// will be invoked again.
pub struct OnCompleteAnimation<A> {
animation: A,
callback: Box<dyn FnMut() + Send + Sync + 'static>,
callback: Option<Box<dyn FnOnce() + Send + Sync + 'static>>,
completed: bool,
}
@ -654,11 +668,11 @@ impl<A> OnCompleteAnimation<A> {
/// `on_complete`.
pub fn new<F>(animation: A, on_complete: F) -> Self
where
F: FnMut() + Send + Sync + 'static,
F: FnOnce() + Send + Sync + 'static,
{
Self {
animation,
callback: Box::new(on_complete),
callback: Some(Box::new(on_complete)),
completed: false,
}
}
@ -690,7 +704,9 @@ where
match self.animation.animate(elapsed) {
ControlFlow::Break(remaining) => {
self.completed = true;
(self.callback)();
if let Some(callback) = self.callback.take() {
run_in_bg(callback);
}
ControlFlow::Break(remaining)
}
ControlFlow::Continue(()) => ControlFlow::Continue(()),

View file

@ -1,6 +1,8 @@
use std::ops::Deref;
use std::sync::mpsc::{self, SyncSender};
use std::sync::{Condvar, OnceLock, PoisonError};
use intentional::Assert;
use kludgine::app::winit::event::Modifiers;
use kludgine::app::winit::keyboard::ModifiersState;
@ -173,3 +175,29 @@ impl<T> IgnorePoison for Result<T, PoisonError<T>> {
self.map_or_else(PoisonError::into_inner, |g| g)
}
}
pub trait BgFunction: FnOnce() + Send + 'static {}
pub fn run_in_bg<F>(f: F)
where
F: BgFunction,
{
static BG_THREAD: Lazy<SyncSender<Box<dyn BgFunction>>> = Lazy::new(|| {
let (sender, receiver) = mpsc::sync_channel::<Box<dyn BgFunction>>(16);
std::thread::Builder::new()
.name(String::from("background"))
.spawn(move || {
while let Ok(callback) = receiver.recv() {
(callback)();
}
})
.assert("error spawning bg thread");
sender
});
BG_THREAD
.send(Box::new(f))
.assert("background thread not running");
}
impl<T> BgFunction for T where T: FnOnce() + Send + 'static {}

View file

@ -9,14 +9,13 @@ use std::sync::atomic::{self, AtomicBool};
use std::sync::{Arc, Mutex, MutexGuard, TryLockError};
use std::task::{Poll, Waker};
use std::thread::ThreadId;
use std::time::Duration;
use ahash::AHashSet;
use crate::animation::{DynamicTransition, IntoAnimate, LinearInterpolate, Spawn};
use crate::animation::{DynamicTransition, LinearInterpolate};
use crate::context::sealed::WindowHandle;
use crate::context::{self, WidgetContext};
use crate::utils::{IgnorePoison, UnwindsafeCondvar, WithClone};
use crate::utils::{run_in_bg, IgnorePoison, UnwindsafeCondvar, WithClone};
use crate::widget::{MakeWidget, WidgetId, WidgetInstance};
use crate::widgets::{Radio, Switcher};
@ -572,6 +571,12 @@ impl<T> context::sealed::Trackable for Dynamic<T> {
}
}
impl<T> PartialEq for Dynamic<T> {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl<T> Default for Dynamic<T>
where
T: Default,
@ -939,9 +944,7 @@ impl<T> Drop for DynamicGuard<'_, T> {
fn drop(&mut self) {
if self.accessed_mut {
let mut callbacks = Some(self.guard.note_changed());
Duration::ZERO
.on_complete(move || drop(callbacks.take()))
.launch();
run_in_bg(move || drop(callbacks.take()));
}
}
}
@ -1420,6 +1423,12 @@ impl<'a> IntoValue<String> for &'a str {
}
}
impl IntoValue<String> for Dynamic<&'static str> {
fn into_value(self) -> Value<String> {
self.map_each(ToString::to_string).into_value()
}
}
impl<T> IntoValue<T> for Dynamic<T> {
fn into_value(self) -> Value<T> {
Value::Dynamic(self)
@ -1760,6 +1769,18 @@ impl Validation {
pub const fn is_error(&self) -> bool {
matches!(self, Self::Invalid(_))
}
/// Returns the result of merging both validations.
#[must_use]
pub fn and(&self, other: &Self) -> Self {
match (self, other) {
(Validation::Valid, Validation::Valid) => Validation::Valid,
(Validation::Invalid(error), _) | (_, Validation::Invalid(error)) => {
Validation::Invalid(error.clone())
}
(Validation::None, _) | (_, Validation::None) => Validation::None,
}
}
}
/// A grouping of validations that can be checked simultaneously.
@ -1864,11 +1885,10 @@ impl Validations {
ValidationsState::Resetting => {
initial_generation = dynamic.generation();
let state = state.clone();
Duration::ZERO
.on_complete(move || {
state.set(ValidationsState::Initial);
})
.launch();
run_in_bg(move || {
state.set(ValidationsState::Initial);
});
Validation::None
}
ValidationsState::Initial if initial_generation == dynamic.generation() => {

View file

@ -1424,6 +1424,18 @@ macro_rules! impl_cow_string {
}
}
impl IntoValue<$type> for Dynamic<String> {
fn into_value(self) -> Value<$type> {
Value::Dynamic(self.map_each_to())
}
}
impl IntoValue<$type> for Dynamic<&'static str> {
fn into_value(self) -> Value<$type> {
Value::Dynamic(self.map_each(|s| <$type>::from(*s)))
}
}
impl<'a> From<&'a String> for $type {
fn from(s: &'a String) -> Self {
Self::new(s.as_str())