mirror of
https://github.com/danbulant/cushy
synced 2026-06-15 12:31:11 +00:00
run_in_bg and Dynamic<&'static str>
This commit is contained in:
parent
4d31719392
commit
aeb55e0b94
4 changed files with 92 additions and 16 deletions
|
|
@ -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(()),
|
||||
|
|
|
|||
28
src/utils.rs
28
src/utils.rs
|
|
@ -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 {}
|
||||
|
|
|
|||
42
src/value.rs
42
src/value.rs
|
|
@ -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() => {
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
Loading…
Reference in a new issue