mirror of
https://github.com/danbulant/cushy
synced 2026-07-05 03:00:43 +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::animation::easings::Linear;
|
||||||
use crate::styles::{Component, RequireInvalidation};
|
use crate::styles::{Component, RequireInvalidation};
|
||||||
use crate::utils::{IgnorePoison, UnwindsafeCondvar};
|
use crate::utils::{run_in_bg, IgnorePoison, UnwindsafeCondvar};
|
||||||
use crate::value::Dynamic;
|
use crate::value::Dynamic;
|
||||||
|
|
||||||
static ANIMATIONS: Mutex<Animating> = Mutex::new(Animating::new());
|
static ANIMATIONS: Mutex<Animating> = Mutex::new(Animating::new());
|
||||||
|
|
@ -488,6 +488,20 @@ impl AnimationHandle {
|
||||||
thread_state().run_unattached(id);
|
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 {
|
impl Drop for AnimationHandle {
|
||||||
|
|
@ -645,7 +659,7 @@ where
|
||||||
/// will be invoked again.
|
/// will be invoked again.
|
||||||
pub struct OnCompleteAnimation<A> {
|
pub struct OnCompleteAnimation<A> {
|
||||||
animation: A,
|
animation: A,
|
||||||
callback: Box<dyn FnMut() + Send + Sync + 'static>,
|
callback: Option<Box<dyn FnOnce() + Send + Sync + 'static>>,
|
||||||
completed: bool,
|
completed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -654,11 +668,11 @@ impl<A> OnCompleteAnimation<A> {
|
||||||
/// `on_complete`.
|
/// `on_complete`.
|
||||||
pub fn new<F>(animation: A, on_complete: F) -> Self
|
pub fn new<F>(animation: A, on_complete: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut() + Send + Sync + 'static,
|
F: FnOnce() + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
animation,
|
animation,
|
||||||
callback: Box::new(on_complete),
|
callback: Some(Box::new(on_complete)),
|
||||||
completed: false,
|
completed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -690,7 +704,9 @@ where
|
||||||
match self.animation.animate(elapsed) {
|
match self.animation.animate(elapsed) {
|
||||||
ControlFlow::Break(remaining) => {
|
ControlFlow::Break(remaining) => {
|
||||||
self.completed = true;
|
self.completed = true;
|
||||||
(self.callback)();
|
if let Some(callback) = self.callback.take() {
|
||||||
|
run_in_bg(callback);
|
||||||
|
}
|
||||||
ControlFlow::Break(remaining)
|
ControlFlow::Break(remaining)
|
||||||
}
|
}
|
||||||
ControlFlow::Continue(()) => ControlFlow::Continue(()),
|
ControlFlow::Continue(()) => ControlFlow::Continue(()),
|
||||||
|
|
|
||||||
28
src/utils.rs
28
src/utils.rs
|
|
@ -1,6 +1,8 @@
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::sync::mpsc::{self, SyncSender};
|
||||||
use std::sync::{Condvar, OnceLock, PoisonError};
|
use std::sync::{Condvar, OnceLock, PoisonError};
|
||||||
|
|
||||||
|
use intentional::Assert;
|
||||||
use kludgine::app::winit::event::Modifiers;
|
use kludgine::app::winit::event::Modifiers;
|
||||||
use kludgine::app::winit::keyboard::ModifiersState;
|
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)
|
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::sync::{Arc, Mutex, MutexGuard, TryLockError};
|
||||||
use std::task::{Poll, Waker};
|
use std::task::{Poll, Waker};
|
||||||
use std::thread::ThreadId;
|
use std::thread::ThreadId;
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use ahash::AHashSet;
|
use ahash::AHashSet;
|
||||||
|
|
||||||
use crate::animation::{DynamicTransition, IntoAnimate, LinearInterpolate, Spawn};
|
use crate::animation::{DynamicTransition, LinearInterpolate};
|
||||||
use crate::context::sealed::WindowHandle;
|
use crate::context::sealed::WindowHandle;
|
||||||
use crate::context::{self, WidgetContext};
|
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::widget::{MakeWidget, WidgetId, WidgetInstance};
|
||||||
use crate::widgets::{Radio, Switcher};
|
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>
|
impl<T> Default for Dynamic<T>
|
||||||
where
|
where
|
||||||
T: Default,
|
T: Default,
|
||||||
|
|
@ -939,9 +944,7 @@ impl<T> Drop for DynamicGuard<'_, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.accessed_mut {
|
if self.accessed_mut {
|
||||||
let mut callbacks = Some(self.guard.note_changed());
|
let mut callbacks = Some(self.guard.note_changed());
|
||||||
Duration::ZERO
|
run_in_bg(move || drop(callbacks.take()));
|
||||||
.on_complete(move || drop(callbacks.take()))
|
|
||||||
.launch();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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> {
|
impl<T> IntoValue<T> for Dynamic<T> {
|
||||||
fn into_value(self) -> Value<T> {
|
fn into_value(self) -> Value<T> {
|
||||||
Value::Dynamic(self)
|
Value::Dynamic(self)
|
||||||
|
|
@ -1760,6 +1769,18 @@ impl Validation {
|
||||||
pub const fn is_error(&self) -> bool {
|
pub const fn is_error(&self) -> bool {
|
||||||
matches!(self, Self::Invalid(_))
|
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.
|
/// A grouping of validations that can be checked simultaneously.
|
||||||
|
|
@ -1864,11 +1885,10 @@ impl Validations {
|
||||||
ValidationsState::Resetting => {
|
ValidationsState::Resetting => {
|
||||||
initial_generation = dynamic.generation();
|
initial_generation = dynamic.generation();
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
Duration::ZERO
|
|
||||||
.on_complete(move || {
|
run_in_bg(move || {
|
||||||
state.set(ValidationsState::Initial);
|
state.set(ValidationsState::Initial);
|
||||||
})
|
});
|
||||||
.launch();
|
|
||||||
Validation::None
|
Validation::None
|
||||||
}
|
}
|
||||||
ValidationsState::Initial if initial_generation == dynamic.generation() => {
|
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 {
|
impl<'a> From<&'a String> for $type {
|
||||||
fn from(s: &'a String) -> Self {
|
fn from(s: &'a String) -> Self {
|
||||||
Self::new(s.as_str())
|
Self::new(s.as_str())
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue