Refactored task module into own crate.
This commit is contained in:
parent
5de8e3097f
commit
8faceea6cf
|
@ -8,6 +8,7 @@ members = [
|
|||
"zero-ui-clone_move",
|
||||
"zero-ui-proc-macros",
|
||||
"zero-ui-var-proc-macros",
|
||||
"zero-ui-task-proc-macros",
|
||||
|
||||
# types:
|
||||
"zero-ui-handle",
|
||||
|
@ -17,6 +18,7 @@ members = [
|
|||
"zero-ui-app_context",
|
||||
"zero-ui-var",
|
||||
"zero-ui-layout",
|
||||
"zero-ui-task",
|
||||
|
||||
# view:
|
||||
"zero-ui-view-api",
|
||||
|
|
|
@ -31,9 +31,11 @@ TextInput! {
|
|||
|
||||
# Split Core
|
||||
|
||||
- Tasks.
|
||||
- Mostly decoupled, need app_context.
|
||||
- UiTask needs WidgetId, can be decoupled.
|
||||
* Color.
|
||||
- Filter needs layout::Length.
|
||||
- Needs impl_from_and_into_var.
|
||||
- Into view_api::RenderColor.
|
||||
- Can be decoupled.
|
||||
|
||||
* App API.
|
||||
- Needs UpdateDeliveryList, that needs WidgetInfo.
|
||||
|
@ -54,12 +56,6 @@ TextInput! {
|
|||
- Does not provide App::default()?
|
||||
- Could be on a feature flag.
|
||||
|
||||
* Color.
|
||||
- Filter needs layout::Length.
|
||||
- Needs impl_from_and_into_var.
|
||||
- Into view_api::RenderColor.
|
||||
- Can be decoupled.
|
||||
|
||||
* Config.
|
||||
- Needs app API.
|
||||
- Needs var.
|
||||
|
|
|
@ -16,7 +16,7 @@ ipc = ["zero-ui-view-api/ipc"]
|
|||
# Enables http tasks.
|
||||
#
|
||||
# Enabled by default.
|
||||
http = ["isahc", "http-cache-semantics", "http-serde"]
|
||||
http = ["zero-ui-task/http"]
|
||||
|
||||
# Signal the build script to enable the `dyn_*`, `inspector` and `trace_widget` features for debug builds.
|
||||
#
|
||||
|
@ -97,6 +97,7 @@ zero-ui-handle = { path = "../zero-ui-handle" }
|
|||
zero-ui-var = { path = "../zero-ui-var", default-features = false }
|
||||
zero-ui-layout = { path = "../zero-ui-layout" }
|
||||
zero-ui-state_map = { path = "../zero-ui-state_map" }
|
||||
zero-ui-task = { path = "../zero-ui-task" }
|
||||
|
||||
# text
|
||||
font-kit = "0.12"
|
||||
|
|
|
@ -24,6 +24,7 @@ use crate::{
|
|||
event::{Event, EventArgs, EventHandle, EventHandles, EventUpdate, EVENTS, EVENTS_SV},
|
||||
handler::{AppHandler, AppHandlerArgs, AppWeakHandle},
|
||||
render::ReuseRange,
|
||||
task::ui::UiTask,
|
||||
text::Txt,
|
||||
timer::TIMERS_SV,
|
||||
var::{AnyVar, AnyVarSubscribe, Var, VarHandle, VarHandles, VarSubscribe, VarValue, VARS},
|
||||
|
@ -2355,3 +2356,26 @@ pub(crate) fn into_harf_direction(d: LayoutDirection) -> harfbuzz_rs::Direction
|
|||
LayoutDirection::RTL => harfbuzz_rs::Direction::Rtl,
|
||||
}
|
||||
}
|
||||
|
||||
/// Integrate [`UiTask`] with widget updates.
|
||||
pub trait UiTaskWidget<R> {
|
||||
/// Create a UI bound future executor.
|
||||
///
|
||||
/// The `task` is inert and must be polled using [`update`] to start, and it must be polled every
|
||||
/// [`UiNode::update`] after that, in widgets the `target` can be set so that the update requests are received.
|
||||
///
|
||||
/// [`update`]: UiTask::update
|
||||
/// [`UiNode::update`]: crate::widget_instance::UiNode::update
|
||||
/// [`UiNode::info`]: crate::widget_instance::UiNode::info
|
||||
fn new<F>(target: Option<WidgetId>, task: F) -> Self
|
||||
where
|
||||
F: std::future::Future<Output = R> + Send + 'static;
|
||||
}
|
||||
impl<R> UiTaskWidget<R> for UiTask<R> {
|
||||
fn new<F>(target: Option<WidgetId>, task: F) -> Self
|
||||
where
|
||||
F: std::future::Future<Output = R> + Send + 'static,
|
||||
{
|
||||
UiTask::new_raw(UPDATES.waker(target), task)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,23 +51,9 @@ impl<F: FnOnce()> Drop for RunOnDrop<F> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts a [`std::panic::catch_unwind`] payload to a str.
|
||||
pub fn panic_str<'s>(payload: &'s Box<dyn std::any::Any + Send + 'static>) -> &'s str {
|
||||
if let Some(s) = payload.downcast_ref::<&str>() {
|
||||
s
|
||||
} else if let Some(s) = payload.downcast_ref::<String>() {
|
||||
s
|
||||
} else {
|
||||
"<unknown-panic-message-type>"
|
||||
}
|
||||
}
|
||||
|
||||
/// Type alias for the *error* of [`PanicResult`].
|
||||
pub type PanicPayload = Box<dyn std::any::Any + Send + 'static>;
|
||||
|
||||
/// The result that is returned by [`std::panic::catch_unwind`].
|
||||
pub type PanicResult<R> = thread::Result<R>;
|
||||
|
||||
// this is the FxHasher with random const init.
|
||||
#[derive(Clone)]
|
||||
pub struct BuildFxHasher(usize);
|
||||
|
@ -304,7 +290,6 @@ pub fn test_log() {
|
|||
}
|
||||
|
||||
/// Calls [`fs4::FileExt::unlock`] and ignores "already unlocked" errors.
|
||||
#[allow(unused)] // http only
|
||||
pub fn unlock_ok(file: &impl fs4::FileExt) -> std::io::Result<()> {
|
||||
if let Err(e) = file.unlock() {
|
||||
if let Some(code) = e.raw_os_error() {
|
||||
|
|
|
@ -17,7 +17,7 @@ use std::time::{Duration, Instant};
|
|||
use std::{mem, thread};
|
||||
|
||||
use crate::app::HeadlessApp;
|
||||
use crate::context::{UPDATES, WIDGET};
|
||||
use crate::context::{UiTaskWidget, UPDATES, WIDGET};
|
||||
use crate::crate_util::{Handle, WeakHandle};
|
||||
use crate::task;
|
||||
use crate::task::ui::UiTask;
|
||||
|
|
|
@ -22,6 +22,7 @@ use crate::{
|
|||
view_process::{ImageRequest, ViewImage, ViewProcessOffline, VIEW_PROCESS, VIEW_PROCESS_INITED_EVENT},
|
||||
AppExtension,
|
||||
},
|
||||
context::UiTaskWidget,
|
||||
crate_util::IdMap,
|
||||
event::EventUpdate,
|
||||
task::{self, fs, io::*, ui::UiTask},
|
||||
|
|
|
@ -35,6 +35,9 @@ pub use paste::paste;
|
|||
#[doc(inline)]
|
||||
pub use zero_ui_layout::units;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use zero_ui_task as task;
|
||||
|
||||
#[macro_use]
|
||||
pub mod handler;
|
||||
|
||||
|
@ -57,7 +60,6 @@ pub mod l10n;
|
|||
pub mod mouse;
|
||||
pub mod pointer_capture;
|
||||
pub mod render;
|
||||
pub mod task;
|
||||
pub mod text;
|
||||
pub mod timer;
|
||||
pub mod var;
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::app::{
|
|||
use crate::app::{APP_PROCESS, EXIT_REQUESTED_EVENT};
|
||||
use crate::color::COLOR_SCHEME_VAR;
|
||||
use crate::context::{RenderUpdates, UpdateOp, WidgetUpdates, WindowCtx};
|
||||
use crate::context::{UPDATES, WINDOW};
|
||||
use crate::context::{UiTaskWidget, UPDATES, WINDOW};
|
||||
use crate::crate_util::{IdMap, IdSet};
|
||||
use crate::event::{AnyEventArgs, EventUpdate};
|
||||
use crate::image::ImageMaskMode;
|
||||
|
|
|
@ -11,9 +11,6 @@ pub use angle::*;
|
|||
mod constraints;
|
||||
pub use constraints::*;
|
||||
|
||||
mod byte;
|
||||
pub use byte::*;
|
||||
|
||||
mod factor;
|
||||
pub use factor::*;
|
||||
|
||||
|
|
|
@ -22,8 +22,6 @@ mod wgt_property_attrs;
|
|||
mod widget;
|
||||
mod widget_util;
|
||||
|
||||
mod any_all;
|
||||
|
||||
mod l10n;
|
||||
mod lang;
|
||||
|
||||
|
@ -101,12 +99,6 @@ pub fn widget_new(input: TokenStream) -> TokenStream {
|
|||
widget::expand_new(input)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[proc_macro]
|
||||
pub fn task_any_all(input: TokenStream) -> TokenStream {
|
||||
any_all::expand(input)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[proc_macro]
|
||||
pub fn trace(input: TokenStream) -> TokenStream {
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "zero-ui-task-proc-macros"
|
||||
version = "0.1.0"
|
||||
authors = ["Samuel Guerra <sam.rodr.g@gmail.com>", "Well <well-r@hotmail.com>"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = "2"
|
|
@ -0,0 +1,15 @@
|
|||
use proc_macro::TokenStream;
|
||||
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
mod any_all;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[proc_macro]
|
||||
pub fn task_any_all(input: TokenStream) -> TokenStream {
|
||||
any_all::expand(input)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/// `Ident` with custom span.
|
||||
macro_rules! ident_spanned {
|
||||
($span:expr=> $($format_name:tt)+) => {
|
||||
proc_macro2::Ident::new(&format!($($format_name)+), $span)
|
||||
};
|
||||
}
|
||||
|
||||
/// `Ident` with call_site span.
|
||||
macro_rules! ident {
|
||||
($($tt:tt)*) => {
|
||||
ident_spanned!(proc_macro2::Span::call_site()=> $($tt)*)
|
||||
};
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
[package]
|
||||
name = "zero-ui-task"
|
||||
version = "0.1.0"
|
||||
authors = ["Samuel Guerra <sam.rodr.g@gmail.com>", "Well <well-r@hotmail.com>"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[features]
|
||||
# Enables http tasks.
|
||||
http = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"isahc",
|
||||
"http-cache-semantics",
|
||||
"http-serde",
|
||||
"once_cell",
|
||||
"zero-ui-txt",
|
||||
"async-recursion",
|
||||
"async-trait",
|
||||
"sha2",
|
||||
"base64",
|
||||
"fs4",
|
||||
"remove_dir_all",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
zero-ui-task-proc-macros = { path = "../zero-ui-task-proc-macros" }
|
||||
zero-ui-clone_move = { path = "../zero-ui-clone_move" }
|
||||
zero-ui-units = { path = "../zero-ui-units" }
|
||||
zero-ui-app_context = { path = "../zero-ui-app_context" }
|
||||
zero-ui-var = { path = "../zero-ui-var" }
|
||||
|
||||
zero-ui-txt = { path = "../zero-ui-txt", optional = true }
|
||||
|
||||
tracing = "0.1"
|
||||
pretty-type-name = "1"
|
||||
flume = { version = "0.11", default-features = false, features = ["async"] }
|
||||
rayon = "1"
|
||||
blocking = "1"
|
||||
parking_lot = "0.12"
|
||||
futures-timer = "3"
|
||||
|
||||
isahc = { version = "1", features = ["cookies", "json"], optional = true }
|
||||
futures-lite = "2"
|
||||
async-fs = "2"
|
||||
serde = { version = "1", optional = true }
|
||||
serde_json = { version = "1", optional = true }
|
||||
http-cache-semantics = { version = "1", optional = true } # isahc needs this version
|
||||
http-serde = { version = "1", optional = true }
|
||||
once_cell = { version = "1", optional = true }
|
||||
async-recursion = { version = "1", optional = true }
|
||||
async-trait = { version = "0.1", optional = true }
|
||||
sha2 = { version = "0.10", optional = true }
|
||||
base64 = { version = "0.21", optional = true }
|
||||
fs4 = { version = "0.7", optional = true }
|
||||
remove_dir_all = { version = "0.8", optional = true }
|
|
@ -32,7 +32,7 @@ use std::{convert::TryFrom, fmt};
|
|||
|
||||
pub use flume::{RecvError, RecvTimeoutError, SendError, SendTimeoutError};
|
||||
|
||||
use crate::units::Deadline;
|
||||
use zero_ui_units::Deadline;
|
||||
|
||||
/// The transmitting end of an unbounded channel.
|
||||
///
|
||||
|
@ -289,7 +289,7 @@ impl<T> Receiver<T> {
|
|||
///
|
||||
/// [`send`]: UnboundSender::send
|
||||
/// [received]: Receiver::recv
|
||||
/// [spawns]: crate::task::spawn
|
||||
/// [spawns]: crate::spawn
|
||||
pub fn unbounded<T>() -> (UnboundSender<T>, Receiver<T>) {
|
||||
let (s, r) = flume::unbounded();
|
||||
(UnboundSender(s), Receiver(r))
|
||||
|
@ -336,7 +336,7 @@ pub fn unbounded<T>() -> (UnboundSender<T>, Receiver<T>) {
|
|||
///
|
||||
/// [`send`]: UnboundSender::send
|
||||
/// [received]: Receiver::recv
|
||||
/// [spawns]: crate::task::spawn
|
||||
/// [spawns]: crate::spawn
|
||||
pub fn bounded<T>(capacity: usize) -> (Sender<T>, Receiver<T>) {
|
||||
let (s, r) = flume::bounded(capacity);
|
||||
(Sender(s), Receiver(r))
|
||||
|
@ -386,7 +386,7 @@ pub fn bounded<T>(capacity: usize) -> (Sender<T>, Receiver<T>) {
|
|||
///
|
||||
/// [`send`]: UnboundSender::send
|
||||
/// [received]: Receiver::recv
|
||||
/// [spawns]: crate::task::spawn
|
||||
/// [spawns]: crate::spawn
|
||||
pub fn rendezvous<T>() -> (Sender<T>, Receiver<T>) {
|
||||
bounded::<T>(0)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/// Converts a [`std::panic::catch_unwind`] payload to a str.
|
||||
pub fn panic_str<'s>(payload: &'s Box<dyn std::any::Any + Send + 'static>) -> &'s str {
|
||||
if let Some(s) = payload.downcast_ref::<&str>() {
|
||||
s
|
||||
} else if let Some(s) = payload.downcast_ref::<String>() {
|
||||
s
|
||||
} else {
|
||||
"<unknown-panic-message-type>"
|
||||
}
|
||||
}
|
||||
|
||||
/// The result that is returned by [`std::panic::catch_unwind`].
|
||||
pub type PanicResult<R> = std::thread::Result<R>;
|
|
@ -1,4 +1,6 @@
|
|||
#![cfg(http)]
|
||||
#![cfg(feature = "http")]
|
||||
// suppress nag about very simple boxed closure signatures.
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
//! HTTP client.
|
||||
//!
|
||||
|
@ -20,6 +22,7 @@
|
|||
//! [`isahc`]: https://docs.rs/isahc
|
||||
|
||||
mod cache;
|
||||
mod util;
|
||||
|
||||
pub use cache::*;
|
||||
|
||||
|
@ -41,8 +44,8 @@ use futures_lite::io::{AsyncReadExt, BufReader};
|
|||
use isahc::{AsyncReadResponseExt, ResponseExt};
|
||||
use parking_lot::{const_mutex, Mutex};
|
||||
|
||||
use crate::text::Txt;
|
||||
use crate::units::*;
|
||||
use zero_ui_txt::Txt;
|
||||
use zero_ui_units::*;
|
||||
|
||||
/// Marker trait for types that try-to-convert to [`Uri`].
|
||||
///
|
|
@ -4,9 +4,9 @@ use std::{
|
|||
};
|
||||
|
||||
use super::{Body, Error};
|
||||
use crate::units::*;
|
||||
use async_trait::async_trait;
|
||||
use serde::*;
|
||||
use zero_ui_units::*;
|
||||
|
||||
use http_cache_semantics as hcs;
|
||||
|
||||
|
@ -139,7 +139,7 @@ impl From<hcs::AfterResponse> for AfterResponse {
|
|||
///
|
||||
/// Cache implementers must store a [`CachePolicy`] and [`Body`] for a given [`CacheKey`].
|
||||
///
|
||||
/// [`Client`]: crate::task::http::Client
|
||||
/// [`Client`]: crate::http::Client
|
||||
#[async_trait]
|
||||
pub trait CacheDb: Send + Sync + 'static {
|
||||
/// Dynamic clone.
|
||||
|
@ -181,9 +181,9 @@ pub trait CacheDb: Send + Sync + 'static {
|
|||
///
|
||||
/// See [`ClientBuilder::cache_mode`] for more information.
|
||||
///
|
||||
/// [`Uri`]: crate::task::http::Uri
|
||||
/// [`Uri`]: crate::http::Uri
|
||||
///
|
||||
/// [`ClientBuilder::cache_mode`]: crate::task::http::ClientBuilder::cache_mode
|
||||
/// [`ClientBuilder::cache_mode`]: crate::http::ClientBuilder::cache_mode
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub enum CacheMode {
|
||||
/// Always requests the server, never caches the response.
|
||||
|
@ -260,16 +260,14 @@ mod file_cache {
|
|||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::http::util::{lock_exclusive, lock_shared, unlock_ok};
|
||||
use crate::{
|
||||
crate_util::{lock_exclusive, lock_shared, unlock_ok},
|
||||
task::{
|
||||
self,
|
||||
io::{McBufErrorExt, McBufReader},
|
||||
},
|
||||
units::TimeUnits,
|
||||
self as task,
|
||||
io::{McBufErrorExt, McBufReader},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use fs4::FileExt;
|
||||
use zero_ui_units::TimeUnits;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -289,8 +287,8 @@ mod file_cache {
|
|||
/// The cache does not pull data, only data read by the returned body is written to the cache, dropping the body without reading
|
||||
/// to end cancels the cache entry.
|
||||
///
|
||||
/// [`Client`]: crate::task::http::Client
|
||||
/// [`set`]: crate::task::http::CacheDb::set
|
||||
/// [`Client`]: crate::http::Client
|
||||
/// [`set`]: crate::http::CacheDb::set
|
||||
#[derive(Clone)]
|
||||
pub struct FileSystemCache {
|
||||
dir: PathBuf,
|
||||
|
@ -653,13 +651,10 @@ mod tests {
|
|||
use zero_ui_clone_move::async_clmv;
|
||||
|
||||
use crate::{
|
||||
crate_util::{test_log, TestTempDir},
|
||||
task::{
|
||||
self,
|
||||
http::{header::*, *},
|
||||
},
|
||||
units::*,
|
||||
self as task,
|
||||
http::{header::*, util::*, *},
|
||||
};
|
||||
use zero_ui_units::*;
|
||||
|
||||
#[test]
|
||||
pub fn file_cache_miss() {
|
|
@ -0,0 +1,196 @@
|
|||
use std::time::Duration;
|
||||
|
||||
/// Calls [`fs4::FileExt::lock_exclusive`] with a timeout.
|
||||
pub fn lock_exclusive(file: &impl fs4::FileExt, timeout: Duration) -> std::io::Result<()> {
|
||||
lock_timeout(file, |f| f.try_lock_exclusive(), timeout)
|
||||
}
|
||||
|
||||
/// Calls [`fs4::FileExt::lock_shared`] with a timeout.
|
||||
pub fn lock_shared(file: &impl fs4::FileExt, timeout: Duration) -> std::io::Result<()> {
|
||||
lock_timeout(file, |f| f.try_lock_shared(), timeout)
|
||||
}
|
||||
|
||||
fn lock_timeout<F: fs4::FileExt>(file: &F, try_lock: impl Fn(&F) -> std::io::Result<()>, mut timeout: Duration) -> std::io::Result<()> {
|
||||
let mut locked_error = None;
|
||||
loop {
|
||||
match try_lock(file) {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(e) => {
|
||||
if e.raw_os_error() != locked_error.get_or_insert_with(fs4::lock_contended_error).raw_os_error() {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
const INTERVAL: Duration = Duration::from_millis(10);
|
||||
timeout = timeout.saturating_sub(INTERVAL);
|
||||
if timeout.is_zero() {
|
||||
return Err(e);
|
||||
} else {
|
||||
std::thread::sleep(INTERVAL.min(timeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls [`fs4::FileExt::unlock`] and ignores "already unlocked" errors.
|
||||
pub fn unlock_ok(file: &impl fs4::FileExt) -> std::io::Result<()> {
|
||||
if let Err(e) = file.unlock() {
|
||||
if let Some(code) = e.raw_os_error() {
|
||||
#[cfg(windows)]
|
||||
if code == 158 {
|
||||
// ERROR_NOT_LOCKED
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
if code == 22 {
|
||||
// EINVAL
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a `tracing` subscriber that writes warnings to stderr and panics on errors.
|
||||
///
|
||||
/// Panics if another different subscriber is already set.
|
||||
#[cfg(test)]
|
||||
pub fn test_log() {
|
||||
use std::sync::atomic::*;
|
||||
|
||||
use std::fmt;
|
||||
use tracing::*;
|
||||
|
||||
struct TestSubscriber;
|
||||
impl Subscriber for TestSubscriber {
|
||||
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
|
||||
metadata.is_event() && metadata.level() < &Level::WARN
|
||||
}
|
||||
|
||||
fn new_span(&self, _span: &span::Attributes<'_>) -> span::Id {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn record(&self, _span: &span::Id, _values: &span::Record<'_>) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn event(&self, event: &Event<'_>) {
|
||||
struct MsgCollector<'a>(&'a mut String);
|
||||
impl<'a> field::Visit for MsgCollector<'a> {
|
||||
fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
|
||||
use std::fmt::Write;
|
||||
write!(self.0, "\n {} = {:?}", field.name(), value).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let meta = event.metadata();
|
||||
let file = meta.file().unwrap_or("");
|
||||
let line = meta.line().unwrap_or(0);
|
||||
|
||||
let mut msg = format!("[{file}:{line}]");
|
||||
event.record(&mut MsgCollector(&mut msg));
|
||||
|
||||
if meta.level() == &Level::ERROR {
|
||||
panic!("[LOG-ERROR]{msg}");
|
||||
} else {
|
||||
eprintln!("[LOG-WARN]{msg}");
|
||||
}
|
||||
}
|
||||
|
||||
fn enter(&self, _span: &span::Id) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn exit(&self, _span: &span::Id) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
static IS_SET: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
if !IS_SET.swap(true, Ordering::Relaxed) {
|
||||
if let Err(e) = subscriber::set_global_default(TestSubscriber) {
|
||||
panic!("failed to set test log subscriber, {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A temporary directory for unit tests.
|
||||
///
|
||||
/// Directory is "target/tmp/unit_tests/<name>" with fallback to system temporary if the target folder is not found.
|
||||
///
|
||||
/// Auto cleanup on drop.
|
||||
#[cfg(test)]
|
||||
pub struct TestTempDir {
|
||||
path: Option<std::path::PathBuf>,
|
||||
}
|
||||
#[cfg(test)]
|
||||
impl Drop for TestTempDir {
|
||||
fn drop(&mut self) {
|
||||
if let Some(path) = self.path.take() {
|
||||
let _ = remove_dir_all::remove_dir_all(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
impl TestTempDir {
|
||||
/// Create temporary directory for the unique teste name.
|
||||
pub fn new(name: &str) -> Self {
|
||||
let path = Self::try_target().unwrap_or_else(Self::fallback).join(name);
|
||||
std::fs::create_dir_all(&path).unwrap_or_else(|e| panic!("failed to create temp `{}`, {e:?}", path.display()));
|
||||
TestTempDir { path: Some(path) }
|
||||
}
|
||||
fn try_target() -> Option<std::path::PathBuf> {
|
||||
let p = std::env::current_exe().ok()?;
|
||||
// target/debug/deps/../../..
|
||||
let target = p.parent()?.parent()?.parent()?;
|
||||
if target.file_name()?.to_str()? != "target" {
|
||||
return None;
|
||||
}
|
||||
Some(target.join("tmp/unit_tests"))
|
||||
}
|
||||
fn fallback() -> std::path::PathBuf {
|
||||
tracing::warn!("using fallback temporary directory");
|
||||
std::env::temp_dir().join("zero_ui/unit_tests")
|
||||
}
|
||||
|
||||
/// Dereferences the temporary directory path.
|
||||
pub fn path(&self) -> &std::path::Path {
|
||||
self.path.as_deref().unwrap()
|
||||
}
|
||||
|
||||
/// Drop `self` without removing the temporary files.
|
||||
///
|
||||
/// Returns the path to the temporary directory.
|
||||
pub fn keep(mut self) -> std::path::PathBuf {
|
||||
self.path.take().unwrap()
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
impl std::ops::Deref for TestTempDir {
|
||||
type Target = std::path::Path;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.path()
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
impl std::convert::AsRef<std::path::Path> for TestTempDir {
|
||||
fn as_ref(&self) -> &std::path::Path {
|
||||
self.path()
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
impl<'a> From<&'a TestTempDir> for std::path::PathBuf {
|
||||
fn from(a: &'a TestTempDir) -> Self {
|
||||
a.path.as_ref().unwrap().clone()
|
||||
}
|
||||
}
|
|
@ -8,11 +8,12 @@ use std::{
|
|||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::{task::McWaker, units::*};
|
||||
use crate::McWaker;
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use futures_lite::io::*;
|
||||
use parking_lot::Mutex;
|
||||
use zero_ui_units::{ByteLength, ByteUnits};
|
||||
|
||||
/// Measure read/write of an async task.
|
||||
///
|
||||
|
@ -567,7 +568,8 @@ enum ReadState {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::task;
|
||||
use crate as task;
|
||||
use zero_ui_units::TimeUnits;
|
||||
|
||||
#[test]
|
||||
pub fn mc_buf_reader_parallel() {
|
|
@ -3,21 +3,16 @@
|
|||
//! Use [`run`], [`respond`] or [`spawn`] to run parallel tasks, use [`wait`], [`io`] and [`fs`] to unblock
|
||||
//! IO operations, use [`http`] for async HTTP, and use [`ui`] to create async properties.
|
||||
//!
|
||||
//! All functions of this module propagate the [`LocalContext`].
|
||||
//! All functions of this crate propagate the [`LocalContext`].
|
||||
//!
|
||||
//! This module also re-exports the [`rayon`] and [`parking_lot`] crates for convenience. You can use the
|
||||
//! This crate also re-exports the [`rayon`] and [`parking_lot`] crates for convenience. You can use the
|
||||
//! [`ParallelIteratorExt::with_ctx`] adapter in rayon iterators to propagate the [`LocalContext`]. You can
|
||||
//! also use [`join`] to propagate thread context for a raw rayon join operation.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! # use zero_ui_core::{*, var::*, gesture::*, task::{self, rayon::prelude::*}, widget_instance::*};
|
||||
//! # #[widget($crate::Button)] pub struct Button(widget_base::WidgetBase);
|
||||
//! # event_property! { pub fn click { event: CLICK_EVENT, args: ClickArgs, } }
|
||||
//! # #[property(CONTEXT)]
|
||||
//! # fn enabled(child: impl UiNode, enabled: impl IntoVar<bool>) -> impl UiNode { child }
|
||||
//! # fn main() {
|
||||
//! # macro_rules! demo { () => {
|
||||
//! let enabled = var(false);
|
||||
//! Button! {
|
||||
//! on_click = async_hn!(enabled, |_| {
|
||||
|
@ -34,12 +29,13 @@
|
|||
//! });
|
||||
//! enabled;
|
||||
//! }
|
||||
//! # ; }
|
||||
//!
|
||||
//! async fn read_numbers() -> Vec<usize> {
|
||||
//! let raw = task::wait(|| std::fs::read_to_string("numbers.txt").unwrap()).await;
|
||||
//! raw.par_split(',').map(|s| s.trim().parse::<usize>().unwrap()).collect()
|
||||
//! }
|
||||
//!
|
||||
//! # }}
|
||||
//! ```
|
||||
//!
|
||||
//! The example demonstrates three different ***tasks***, the first is a [`ui::UiTask`] in the `async_hn` handler,
|
||||
|
@ -87,13 +83,7 @@
|
|||
//! implementing operations such as loading an image from a given URL, the module is a thin wrapper around the [`isahc`] crate.
|
||||
//!
|
||||
//! ```
|
||||
//! # use zero_ui_core::{*, var::*, handler::*, text::*, gesture::*, widget_instance::*};
|
||||
//! # #[widget($crate::Button)]
|
||||
//! # pub struct Button(widget_base::WidgetBase);
|
||||
//! # event_property! { pub fn click { event: CLICK_EVENT, args: ClickArgs, } }
|
||||
//! # #[property(CONTEXT)]
|
||||
//! # fn enabled(child: impl UiNode, enabled: impl IntoVar<bool>) -> impl UiNode { child }
|
||||
//! # fn main() {
|
||||
//! # macro_rules! demo { () => {
|
||||
//! let enabled = var(false);
|
||||
//! let msg = var("loading..".to_text());
|
||||
//! Button! {
|
||||
|
@ -108,7 +98,7 @@
|
|||
//! enabled.set(true);
|
||||
//! });
|
||||
//! }
|
||||
//! # ; }
|
||||
//! # }}
|
||||
//! ```
|
||||
//!
|
||||
//! For other protocols or alternative HTTP clients you can use [external crates](#async-crates-integration).
|
||||
|
@ -157,12 +147,12 @@ use std::{
|
|||
pub use parking_lot;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use crate::{
|
||||
context::LocalContext,
|
||||
crate_util::{panic_str, PanicResult},
|
||||
units::Deadline,
|
||||
var::{response_done_var, response_var, ResponseVar, VarValue},
|
||||
};
|
||||
mod crate_util;
|
||||
|
||||
use crate::crate_util::PanicResult;
|
||||
use zero_ui_app_context::LocalContext;
|
||||
use zero_ui_units::Deadline;
|
||||
use zero_ui_var::{response_done_var, response_var, ResponseVar, VarValue};
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use rayon;
|
||||
|
@ -170,14 +160,14 @@ pub use rayon;
|
|||
#[doc(no_inline)]
|
||||
pub use async_fs as fs;
|
||||
|
||||
pub use crate::handler::async_clmv;
|
||||
pub use zero_ui_clone_move::async_clmv;
|
||||
|
||||
pub mod channel;
|
||||
pub mod io;
|
||||
pub mod ui;
|
||||
|
||||
pub mod http;
|
||||
mod rayon_ctx;
|
||||
pub mod ui;
|
||||
|
||||
pub use rayon_ctx::*;
|
||||
|
||||
|
@ -328,7 +318,7 @@ impl RayonTask {
|
|||
}
|
||||
}));
|
||||
if let Err(p) = r {
|
||||
tracing::error!("panic in `task::spawn`: {}", panic_str(&p));
|
||||
tracing::error!("panic in `task::spawn`: {}", crate_util::panic_str(&p));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -658,7 +648,7 @@ where
|
|||
{
|
||||
enum QuickResponse<R: VarValue> {
|
||||
Quick(Option<R>),
|
||||
Response(crate::var::ResponderVar<R>),
|
||||
Response(zero_ui_var::ResponderVar<R>),
|
||||
}
|
||||
|
||||
let q = Arc::new(Mutex::new(QuickResponse::Quick(None)));
|
||||
|
@ -769,7 +759,7 @@ where
|
|||
{
|
||||
spawn(async move {
|
||||
if let Err(p) = wait_catch(task).await {
|
||||
tracing::error!("parallel `spawn_wait` task panicked: {}", panic_str(&p))
|
||||
tracing::error!("parallel `spawn_wait` task panicked: {}", crate_util::panic_str(&p))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -791,7 +781,7 @@ where
|
|||
/// Blocks the thread until the `task` future finishes.
|
||||
///
|
||||
/// This function is useful for implementing async tests, using it in an app will probably cause
|
||||
/// the app to stop responding. To test UI task use [`HeadlessApp::block_on`].
|
||||
/// the app to stop responding.
|
||||
///
|
||||
/// The crate [`futures-lite`] is used to execute the task.
|
||||
///
|
||||
|
@ -818,7 +808,6 @@ where
|
|||
/// # run_ok();
|
||||
/// ```
|
||||
///
|
||||
/// [`HeadlessApp::block_on`]: crate::app::HeadlessApp::block_on
|
||||
/// [`futures-lite`]: https://docs.rs/futures-lite/
|
||||
pub fn block_on<F>(task: F) -> F::Output
|
||||
where
|
||||
|
@ -857,7 +846,7 @@ pub fn doc_test<F>(spin: bool, task: F) -> F::Output
|
|||
where
|
||||
F: Future,
|
||||
{
|
||||
use crate::units::TimeUnits;
|
||||
use zero_ui_units::TimeUnits;
|
||||
|
||||
if spin {
|
||||
spin_on(with_deadline(task, 500.ms())).expect("async doc-test timeout")
|
||||
|
@ -943,12 +932,11 @@ pub async fn yield_now() {
|
|||
///
|
||||
/// # UI Async
|
||||
///
|
||||
/// This timer works in UI async tasks too, but you should use the [`TIMERS`] instead, as they are implemented using only
|
||||
/// the app loop they use the same *executor* as the app or widget tasks.
|
||||
/// This timer works in UI async tasks too, but in a full app prefer `TIMERS` instead, as it is implemented using only
|
||||
/// the app loop it avoids spawning the [`futures_timer`] executor.
|
||||
///
|
||||
/// [`Pending`]: std::task::Poll::Pending
|
||||
/// [`futures_timer`]: https://docs.rs/futures-timer
|
||||
/// [`TIMERS`]: crate::timer::TIMERS#async
|
||||
pub async fn deadline(deadline: impl Into<Deadline>) {
|
||||
let deadline = deadline.into();
|
||||
if let Some(timeout) = deadline.time_left() {
|
||||
|
@ -1103,10 +1091,8 @@ macro_rules! all {
|
|||
fut7: $fut7;
|
||||
}
|
||||
};
|
||||
($($fut:expr),+ $(,)?) => { $crate::task::__proc_any_all!{ $crate::__all; $($fut),+ } }
|
||||
($($fut:expr),+ $(,)?) => { $crate::__proc_any_all!{ $crate::__all; $($fut),+ } }
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub use crate::all;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
|
@ -1114,7 +1100,7 @@ macro_rules! __all {
|
|||
($($ident:ident: $fut:expr;)+) => {
|
||||
{
|
||||
$(let mut $ident = (Some($fut), None);)+
|
||||
$crate::task::future_fn(move |cx| {
|
||||
$crate::future_fn(move |cx| {
|
||||
use std::task::Poll;
|
||||
use std::future::Future;
|
||||
|
||||
|
@ -1240,10 +1226,8 @@ macro_rules! any {
|
|||
fut7: $fut7;
|
||||
}
|
||||
};
|
||||
($($fut:expr),+ $(,)?) => { $crate::task::__proc_any_all!{ $crate::__any; $($fut),+ } }
|
||||
($($fut:expr),+ $(,)?) => { $crate::__proc_any_all!{ $crate::__any; $($fut),+ } }
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub use crate::any;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
|
@ -1251,7 +1235,7 @@ macro_rules! __any {
|
|||
($($ident:ident: $fut:expr;)+) => {
|
||||
{
|
||||
$(let mut $ident = $fut;)+
|
||||
$crate::task::future_fn(move |cx| {
|
||||
$crate::future_fn(move |cx| {
|
||||
use std::task::Poll;
|
||||
use std::future::Future;
|
||||
$(
|
||||
|
@ -1270,7 +1254,7 @@ macro_rules! __any {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use zero_ui_proc_macros::task_any_all as __proc_any_all;
|
||||
pub use zero_ui_task_proc_macros::task_any_all as __proc_any_all;
|
||||
|
||||
/// <span data-del-macro-root></span> A future that waits for the first future that is ready with an `Ok(T)` result.
|
||||
///
|
||||
|
@ -1371,10 +1355,8 @@ macro_rules! any_ok {
|
|||
fut7: $fut7;
|
||||
}
|
||||
};
|
||||
($($fut:expr),+ $(,)?) => { $crate::task::__proc_any_all!{ $crate::__any_ok; $($fut),+ } }
|
||||
($($fut:expr),+ $(,)?) => { $crate::__proc_any_all!{ $crate::__any_ok; $($fut),+ } }
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub use crate::any_ok;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
|
@ -1382,7 +1364,7 @@ macro_rules! __any_ok {
|
|||
($($ident:ident: $fut: expr;)+) => {
|
||||
{
|
||||
$(let mut $ident = (Some($fut), None);)+
|
||||
$crate::task::future_fn(move |cx| {
|
||||
$crate::future_fn(move |cx| {
|
||||
use std::task::Poll;
|
||||
use std::future::Future;
|
||||
|
||||
|
@ -1516,10 +1498,8 @@ macro_rules! any_some {
|
|||
fut7: $fut7;
|
||||
}
|
||||
};
|
||||
($($fut:expr),+ $(,)?) => { $crate::task::__proc_any_all!{ $crate::__any_some; $($fut),+ } }
|
||||
($($fut:expr),+ $(,)?) => { $crate::__proc_any_all!{ $crate::__any_some; $($fut),+ } }
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub use crate::any_some;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
|
@ -1527,7 +1507,7 @@ macro_rules! __any_some {
|
|||
($($ident:ident: $fut: expr;)+) => {
|
||||
{
|
||||
$(let mut $ident = Some($fut);)+
|
||||
$crate::task::future_fn(move |cx| {
|
||||
$crate::future_fn(move |cx| {
|
||||
use std::task::Poll;
|
||||
use std::future::Future;
|
||||
|
||||
|
@ -1674,10 +1654,8 @@ macro_rules! all_ok {
|
|||
fut7: $fut7;
|
||||
}
|
||||
};
|
||||
($($fut:expr),+ $(,)?) => { $crate::task::__proc_any_all!{ $crate::__all_ok; $($fut),+ } }
|
||||
($($fut:expr),+ $(,)?) => { $crate::__proc_any_all!{ $crate::__all_ok; $($fut),+ } }
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub use crate::all_ok;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
|
@ -1686,7 +1664,7 @@ macro_rules! __all_ok {
|
|||
{
|
||||
$(let mut $ident = (Some($fut), None);)+
|
||||
|
||||
$crate::task::future_fn(move |cx| {
|
||||
$crate::future_fn(move |cx| {
|
||||
use std::task::Poll;
|
||||
use std::future::Future;
|
||||
|
||||
|
@ -1834,10 +1812,8 @@ macro_rules! all_some {
|
|||
fut7: $fut7;
|
||||
}
|
||||
};
|
||||
($($fut:expr),+ $(,)?) => { $crate::task::__proc_any_all!{ $crate::__all_some; $($fut),+ } }
|
||||
($($fut:expr),+ $(,)?) => { $crate::__proc_any_all!{ $crate::__all_some; $($fut),+ } }
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub use crate::all_some;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
|
@ -1845,7 +1821,7 @@ macro_rules! __all_some {
|
|||
($($ident:ident: $fut: expr;)+) => {
|
||||
{
|
||||
$(let mut $ident = (Some($fut), None);)+
|
||||
$crate::task::future_fn(move |cx| {
|
||||
$crate::future_fn(move |cx| {
|
||||
use std::task::Poll;
|
||||
use std::future::Future;
|
||||
|
||||
|
@ -2028,7 +2004,7 @@ pub mod tests {
|
|||
use rayon::prelude::*;
|
||||
|
||||
use super::*;
|
||||
use crate::units::TimeUnits;
|
||||
use zero_ui_units::TimeUnits;
|
||||
|
||||
#[track_caller]
|
||||
fn async_test<F>(test: F) -> F::Output
|
|
@ -3,7 +3,7 @@ use rayon::{
|
|||
prelude::{IndexedParallelIterator, ParallelIterator},
|
||||
};
|
||||
|
||||
use crate::context::LocalContext;
|
||||
use zero_ui_app_context::LocalContext;
|
||||
|
||||
/// Extends [`ParallelIterator`] with thread context.
|
||||
pub trait ParallelIteratorExt: ParallelIterator {
|
||||
|
@ -13,8 +13,8 @@ pub trait ParallelIteratorExt: ParallelIterator {
|
|||
/// Without this adapter all closures in the iterator chain that use [`context_local!`] and
|
||||
/// [`app_local!`] will probably not work correctly.
|
||||
///
|
||||
/// [`context_local!`]: crate::context::context_local
|
||||
/// [`app_local!`]: crate::context::app_local
|
||||
/// [`context_local!`]: zero_ui_app_context::context_local
|
||||
/// [`app_local!`]: zero_ui_app_context::app_local
|
||||
fn with_ctx(self) -> ParallelIteratorWithCtx<Self> {
|
||||
ParallelIteratorWithCtx {
|
||||
base: self,
|
||||
|
@ -230,7 +230,7 @@ mod tests {
|
|||
use super::*;
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::{app::App, context::*};
|
||||
use zero_ui_app_context::*;
|
||||
|
||||
context_local! {
|
||||
static VALUE: u32 = 0u32;
|
||||
|
@ -238,7 +238,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn map_and_sum_with_context() {
|
||||
let _app = App::minimal().run_headless(false);
|
||||
let _app = LocalContext::start_app(AppId::new_unique());
|
||||
let thread_id = std::thread::current().id();
|
||||
let used_other_thread = Arc::new(AtomicBool::new(false));
|
||||
|
||||
|
@ -261,7 +261,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn for_each_with_context() {
|
||||
let _app = App::minimal().run_headless(false);
|
||||
let _app = LocalContext::start_app(AppId::new_unique());
|
||||
let thread_id = std::thread::current().id();
|
||||
let used_other_thread = Arc::new(AtomicBool::new(false));
|
||||
|
||||
|
@ -282,7 +282,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn chain_for_each_with_context() {
|
||||
let _app = App::minimal().run_headless(false);
|
||||
let _app = LocalContext::start_app(AppId::new_unique());
|
||||
let thread_id = std::thread::current().id();
|
||||
let used_other_thread = Arc::new(AtomicBool::new(false));
|
||||
|
||||
|
@ -307,7 +307,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn chain_for_each_with_context_inverted() {
|
||||
let _app = App::minimal().run_headless(false);
|
||||
let _app = LocalContext::start_app(AppId::new_unique());
|
||||
let thread_id = std::thread::current().id();
|
||||
let used_other_thread = Arc::new(AtomicBool::new(false));
|
||||
|
|
@ -7,14 +7,12 @@ use std::{
|
|||
task::{Poll, Waker},
|
||||
};
|
||||
|
||||
use crate::{context::*, widget_instance::WidgetId};
|
||||
|
||||
enum UiTaskState<R> {
|
||||
Pending {
|
||||
future: Pin<Box<dyn Future<Output = R> + Send>>,
|
||||
event_loop_waker: Waker,
|
||||
#[cfg(debug_assertions)]
|
||||
last_update: Option<crate::var::VarUpdateId>,
|
||||
last_update: Option<zero_ui_var::VarUpdateId>,
|
||||
},
|
||||
Ready(R),
|
||||
}
|
||||
|
@ -37,21 +35,16 @@ impl<R: fmt::Debug> fmt::Debug for UiTaskState<R> {
|
|||
#[derive(Debug)]
|
||||
pub struct UiTask<R>(UiTaskState<R>);
|
||||
impl<R> UiTask<R> {
|
||||
/// Create a UI bound future executor.
|
||||
/// New task with already build event-loop waker.
|
||||
///
|
||||
/// The `task` is inert and must be polled using [`update`] to start, and it must be polled every
|
||||
/// [`UiNode::update`] after that, in widgets the `target` can be set so that the update requests are received.
|
||||
///
|
||||
/// [`update`]: UiTask::update
|
||||
/// [`UiNode::update`]: crate::widget_instance::UiNode::update
|
||||
/// [`UiNode::info`]: crate::widget_instance::UiNode::info
|
||||
pub fn new<F>(target: Option<WidgetId>, task: F) -> Self
|
||||
/// App crate provides an integrated `UiTaskWidget::new` that creates the waker for widgets.
|
||||
pub fn new_raw<F>(event_loop_waker: Waker, task: F) -> Self
|
||||
where
|
||||
F: Future<Output = R> + Send + 'static,
|
||||
{
|
||||
UiTask(UiTaskState::Pending {
|
||||
future: Box::pin(task),
|
||||
event_loop_waker: UPDATES.waker(target),
|
||||
event_loop_waker,
|
||||
#[cfg(debug_assertions)]
|
||||
last_update: None,
|
||||
})
|
||||
|
@ -69,7 +62,7 @@ impl<R> UiTask<R> {
|
|||
///
|
||||
/// In debug builds this is validated and an error message is logged if incorrect updates are detected.
|
||||
///
|
||||
/// [`task::yield_now`]: crate::task::yield_now
|
||||
/// [`task::yield_now`]: crate::yield_now
|
||||
pub fn update(&mut self) -> Option<&R> {
|
||||
if let UiTaskState::Pending {
|
||||
future,
|
||||
|
@ -81,7 +74,7 @@ impl<R> UiTask<R> {
|
|||
{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let update = Some(crate::var::VARS.update_id());
|
||||
let update = Some(zero_ui_var::VARS.update_id());
|
||||
if *last_update == update {
|
||||
tracing::error!("UiTask::update called twice in the same update");
|
||||
}
|
|
@ -7,4 +7,4 @@ license = "Apache-2.0"
|
|||
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde = "1"
|
|
@ -1,7 +1,5 @@
|
|||
use std::{fmt, ops};
|
||||
|
||||
use zero_ui_var::{animation::Transitionable, impl_from_and_into_var};
|
||||
|
||||
use super::Factor;
|
||||
|
||||
/// Extension methods for initializing [`ByteLength`] values.
|
||||
|
@ -88,12 +86,12 @@ impl ByteUnits for usize {
|
|||
///
|
||||
/// The value is stored in bytes, you can use associated functions to convert from other units or
|
||||
/// you can use the [`ByteUnits`] extension methods to initialize from an integer literal.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, serde::Serialize, serde::Deserialize, Transitionable)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct ByteLength(pub usize);
|
||||
impl_from_and_into_var! {
|
||||
fn from(bytes: usize) -> ByteLength {
|
||||
ByteLength(bytes)
|
||||
impl From<usize> for ByteLength {
|
||||
fn from(value: usize) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
impl ops::Add for ByteLength {
|
|
@ -1,3 +1,4 @@
|
|||
mod byte;
|
||||
mod corner_radius;
|
||||
mod distance_key;
|
||||
mod factor;
|
||||
|
@ -10,6 +11,7 @@ mod transform;
|
|||
|
||||
pub use euclid;
|
||||
|
||||
pub use byte::*;
|
||||
pub use corner_radius::*;
|
||||
pub use distance_key::*;
|
||||
pub use factor::*;
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::{
|
|||
};
|
||||
|
||||
use zero_ui_txt::Txt;
|
||||
use zero_ui_units::{euclid, CornerRadius2D, Deadline, Dip, Factor, FactorPercent, FactorUnits, Px};
|
||||
use zero_ui_units::{euclid, ByteLength, CornerRadius2D, Deadline, Dip, Factor, FactorPercent, FactorUnits, Px};
|
||||
|
||||
use crate::{animation::Transitionable, easing::EasingStep, impl_from_and_into_var};
|
||||
|
||||
|
@ -184,6 +184,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl Transitionable for ByteLength {
|
||||
fn lerp(self, to: &Self, step: EasingStep) -> Self {
|
||||
Self(self.0.lerp(&to.0, step))
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_and_into_var! {
|
||||
fn from(s: &'static str) -> Txt;
|
||||
fn from(s: String) -> Txt;
|
||||
|
@ -200,6 +206,8 @@ impl_from_and_into_var! {
|
|||
|
||||
fn from(d: Instant) -> Deadline;
|
||||
fn from(d: Duration) -> Deadline;
|
||||
|
||||
fn from(b: usize) -> ByteLength;
|
||||
}
|
||||
|
||||
macro_rules! impl_into_var_option {
|
||||
|
|
Loading…
Reference in New Issue