first pass of merging in warp (doesn't build)

This commit is contained in:
Ryan Ward
2026-07-01 16:08:58 -05:00
parent 2f64909469
commit 4770ac06b5
3662 changed files with 414574 additions and 89772 deletions
+174 -15
View File
@@ -1,18 +1,39 @@
use std::collections::HashMap;
use std::hash::{DefaultHasher, Hash, Hasher};
use std::sync::Arc;
use std::time::Duration;
use instant::Instant;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{vec2f, Vector2F, Vector2I};
use super::{CornerRadius, Element, Point};
use crate::assets::asset_cache::{AssetCache, AssetSource, AssetState};
use crate::event::DispatchedEvent;
pub use crate::image_cache::CacheOption;
use crate::image_cache::{AnimatedImage, AnimatedImageBehavior, FitType, ImageCache, StaticImage};
use crate::{
assets::asset_cache::{AssetCache, AssetSource, AssetState},
event::DispatchedEvent,
image_cache::{AnimatedImage, AnimatedImageBehavior, FitType, ImageCache, StaticImage},
AfterLayoutContext, AppContext, EventContext, LayoutContext, PaintContext, SingletonEntity,
SizeConstraint,
};
pub use crate::image_cache::CacheOption;
use instant::Instant;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{vec2f, Vector2F, Vector2I};
use std::sync::Arc;
use std::time::Duration;
lazy_static! {
static ref IMAGE_LOAD_TIMEOUT_STARTED_AT: Mutex<HashMap<u64, Instant>> =
Mutex::new(HashMap::new());
}
struct LoadTimeout {
timeout: Duration,
element: Box<dyn Element>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum BackupElementKind {
BeforeLoad,
FailedToLoad,
LoadTimeout,
}
pub struct Image {
source: AssetSource,
@@ -20,6 +41,9 @@ pub struct Image {
size: Option<Vector2F>,
origin: Option<Point>,
fit_type: FitType,
/// If true, layout will produce a size based on the bounds at which the image will
/// be painted instead of simply using the incoming max constraint.
layout_using_paint_bounds: bool,
animated_image_behavior: AnimatedImageBehavior,
cache_option: CacheOption,
started_at: Option<Instant>,
@@ -31,6 +55,8 @@ pub struct Image {
/// This could be None in two situations: (1) the caller does not provide a before_load_element
/// or (2) the caller provided one but it's no longer needed due to the image having loaded.
before_load_element: Option<Box<dyn Element>>,
failed_to_load_element: Option<Box<dyn Element>>,
load_timeout: Option<LoadTimeout>,
/// To avoid duplicating delayed repaint, we store whether or not we've requested a
/// repaint on behalf of this element.
@@ -65,7 +91,10 @@ impl Image {
top_aligned: false,
right_aligned: false,
before_load_element: None,
failed_to_load_element: None,
load_timeout: None,
requested_repaint_after_load: false,
layout_using_paint_bounds: false,
#[cfg(debug_assertions)]
constructor_location: Some(std::panic::Location::caller()),
}
@@ -119,6 +148,16 @@ impl Image {
self
}
/// Uses the paint boundary of the image as the laid-out size.
///
/// By default, the [`Image`] element bounds will be the incoming max constraint.
/// For some cases, we'd prefer for the element's laid-out size to match the painted
/// image size.
pub fn layout_using_paint_bounds(mut self) -> Self {
self.layout_using_paint_bounds = true;
self
}
/// Enables animated images for the current image element. The start time indicates
/// the timestamp at which the animated image started rendering. The element uses
/// this timestamp to calculate which frame of the animation to display at a given
@@ -134,6 +173,96 @@ impl Image {
self.before_load_element = Some(element);
self
}
pub fn on_load_failure(mut self, element: Box<dyn Element>) -> Self {
self.failed_to_load_element = Some(element);
self
}
pub fn on_load_timeout(mut self, timeout: Duration, element: Box<dyn Element>) -> Self {
self.load_timeout = Some(LoadTimeout { timeout, element });
self
}
fn load_timeout_key(&self) -> u64 {
let mut hasher = DefaultHasher::new();
self.source.hash(&mut hasher);
hasher.finish()
}
fn load_started_at(&self, now: Instant) -> Instant {
*IMAGE_LOAD_TIMEOUT_STARTED_AT
.lock()
.entry(self.load_timeout_key())
.or_insert(now)
}
fn clear_load_timeout_started_at(&self) {
IMAGE_LOAD_TIMEOUT_STARTED_AT
.lock()
.remove(&self.load_timeout_key());
}
fn loading_backup_element_kind(
&mut self,
now: Instant,
) -> (Option<BackupElementKind>, Option<Duration>) {
let Some(load_timeout) = self.load_timeout.as_ref() else {
return (
self.before_load_element
.as_ref()
.map(|_| BackupElementKind::BeforeLoad),
None,
);
};
let started_at = self.load_started_at(now);
let elapsed = now.duration_since(started_at);
if elapsed >= load_timeout.timeout {
return (Some(BackupElementKind::LoadTimeout), None);
}
(
self.before_load_element
.as_ref()
.map(|_| BackupElementKind::BeforeLoad),
Some(load_timeout.timeout - elapsed),
)
}
fn failed_to_load_backup_element_kind(&self) -> Option<BackupElementKind> {
if self.failed_to_load_element.is_some() {
Some(BackupElementKind::FailedToLoad)
} else if self.before_load_element.is_some() {
Some(BackupElementKind::BeforeLoad)
} else {
None
}
}
fn paint_backup_element(
&mut self,
kind: BackupElementKind,
origin: Vector2F,
ctx: &mut PaintContext,
app: &AppContext,
) {
match kind {
BackupElementKind::BeforeLoad => {
if let Some(before_load_element) = self.before_load_element.as_mut() {
before_load_element.paint(origin, ctx, app);
}
}
BackupElementKind::FailedToLoad => {
if let Some(failed_to_load_element) = self.failed_to_load_element.as_mut() {
failed_to_load_element.paint(origin, ctx, app);
}
}
BackupElementKind::LoadTimeout => {
if let Some(load_timeout) = self.load_timeout.as_mut() {
load_timeout.element.paint(origin, ctx, app);
}
}
}
}
fn paint_static_image(
&mut self,
@@ -278,12 +407,27 @@ impl Element for Image {
ctx: &mut LayoutContext,
app: &AppContext,
) -> Vector2F {
let size = constraint.max;
let mut size = constraint.max;
if self.layout_using_paint_bounds {
let asset_cache = AssetCache::as_ref(app);
let image_size = ImageCache::as_ref(app).image_size(self.source.clone(), asset_cache);
if let Some(image_size) = image_size {
size = dimensions(image_size.to_f32(), size, self.fit_type);
}
}
self.size = Some(size);
if let Some(before_load_element) = self.before_load_element.as_mut() {
before_load_element.layout(constraint, ctx, app);
}
if let Some(failed_to_load_element) = self.failed_to_load_element.as_mut() {
failed_to_load_element.layout(constraint, ctx, app);
}
if let Some(load_timeout) = self.load_timeout.as_mut() {
load_timeout.element.layout(constraint, ctx, app);
}
size
}
@@ -292,6 +436,12 @@ impl Element for Image {
if let Some(before_load_element) = self.before_load_element.as_mut() {
before_load_element.after_layout(ctx, app);
}
if let Some(failed_to_load_element) = self.failed_to_load_element.as_mut() {
failed_to_load_element.after_layout(ctx, app);
}
if let Some(load_timeout) = self.load_timeout.as_mut() {
load_timeout.element.after_layout(ctx, app);
}
}
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
@@ -335,25 +485,34 @@ impl Element for Image {
ctx.repaint_after_load(handle);
self.requested_repaint_after_load = true;
}
if let Some(before_load_element) = self.before_load_element.as_mut() {
before_load_element.paint(origin, ctx, app);
let (backup_element_kind, repaint_after) =
self.loading_backup_element_kind(Instant::now());
if let Some(repaint_after) = repaint_after {
ctx.repaint_after(repaint_after);
}
if let Some(kind) = backup_element_kind {
self.paint_backup_element(kind, origin, ctx, app);
}
}
AssetState::Evicted => {
self.clear_load_timeout_started_at();
if let Some(before_load_element) = self.before_load_element.as_mut() {
before_load_element.paint(origin, ctx, app);
}
}
AssetState::FailedToLoad(_) => {
if let Some(before_load_element) = self.before_load_element.as_mut() {
before_load_element.paint(origin, ctx, app);
self.clear_load_timeout_started_at();
if let Some(kind) = self.failed_to_load_backup_element_kind() {
self.paint_backup_element(kind, origin, ctx, app);
}
}
AssetState::Loaded { data } => {
// Don't waste time calling layout() and after_layout() on the backup element once the main
// one has loaded.
self.clear_load_timeout_started_at();
self.before_load_element = None;
self.failed_to_load_element = None;
self.load_timeout = None;
match data.as_ref() {
crate::image_cache::Image::Static(static_image) => {