Fix lints, undo broken set_immediate_interval
This commit is contained in:
@@ -217,17 +217,19 @@ where
|
|||||||
);
|
);
|
||||||
|
|
||||||
if delay.is_zero() {
|
if delay.is_zero() {
|
||||||
|
unimplemented!("Zero-delay intervals are not yet implemented.")
|
||||||
|
// TODO: vibeshit got this completely wrong
|
||||||
// Zero-delay intervals never touch the timer heap or arm an io_uring
|
// Zero-delay intervals never touch the timer heap or arm an io_uring
|
||||||
// timer (a past-deadline kernel timer would fire on every event loop
|
// timer (a past-deadline kernel timer would fire on every event loop
|
||||||
// iteration, spinning the runtime at 100 % CPU). Instead the callback
|
// iteration, spinning the runtime at 100 % CPU). Instead the callback
|
||||||
// is stored in `immediate_intervals` and self-schedules as a macrotask
|
// is stored in `immediate_intervals` and self-schedules as a macrotask
|
||||||
// each turn, mirroring JS `setInterval(f, 0)` semantics.
|
// each turn, mirroring JS `setInterval(f, 0)` semantics.
|
||||||
let callback = Rc::new(RefCell::new(Box::new(callback) as Box<dyn FnMut()>));
|
// let callback = Rc::new(RefCell::new(Box::new(callback) as Box<dyn FnMut()>));
|
||||||
current_thread()
|
// current_thread()
|
||||||
.immediate_intervals
|
// .immediate_intervals
|
||||||
.borrow_mut()
|
// .borrow_mut()
|
||||||
.insert(id, Rc::clone(&callback));
|
// .insert(id, Rc::clone(&callback));
|
||||||
schedule_immediate_interval(owner, id);
|
// schedule_immediate_interval(owner, id);
|
||||||
} else {
|
} else {
|
||||||
let deadline = deadline_from_now(delay);
|
let deadline = deadline_from_now(delay);
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
@@ -571,6 +573,8 @@ impl Future for YieldNow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImmediateIntervals = RefCell<HashMap<usize, Rc<RefCell<Box<dyn FnMut()>>>>>;
|
||||||
|
|
||||||
struct ThreadState {
|
struct ThreadState {
|
||||||
driver: Driver,
|
driver: Driver,
|
||||||
shared: Arc<ThreadShared>,
|
shared: Arc<ThreadShared>,
|
||||||
@@ -580,7 +584,7 @@ struct ThreadState {
|
|||||||
timers: RefCell<TimerHeap>,
|
timers: RefCell<TimerHeap>,
|
||||||
/// Zero-delay intervals bypasses the timer heap entirely. Each entry
|
/// Zero-delay intervals bypasses the timer heap entirely. Each entry
|
||||||
/// re-enqueues itself as a macrotask on every turn.
|
/// re-enqueues itself as a macrotask on every turn.
|
||||||
immediate_intervals: RefCell<HashMap<usize, Rc<RefCell<Box<dyn FnMut()>>>>>,
|
immediate_intervals: ImmediateIntervals,
|
||||||
next_timer_id: Cell<usize>,
|
next_timer_id: Cell<usize>,
|
||||||
children: RefCell<Vec<ChildWorker>>,
|
children: RefCell<Vec<ChildWorker>>,
|
||||||
}
|
}
|
||||||
@@ -1162,27 +1166,32 @@ fn clear_timer(owner: *const ThreadState, id: usize) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enqueues one macrotask turn for a zero-delay interval.
|
// TODO: vibeshit got this completely wrong
|
||||||
///
|
// /// Enqueues one macrotask turn for a zero-delay interval.
|
||||||
/// The macrotask checks whether the interval is still live (not cleared), fires
|
// ///
|
||||||
/// the callback, and re-enqueues itself for the next turn.
|
// /// The macrotask checks whether the interval is still live (not cleared), fires
|
||||||
fn schedule_immediate_interval(owner: *const ThreadState, id: usize) {
|
// /// the callback, and re-enqueues itself for the next turn.
|
||||||
push_local_macrotask(Box::new(move || {
|
// fn schedule_immediate_interval(owner: *const ThreadState, id: usize) {
|
||||||
let callback = current_thread()
|
// push_local_macrotask(Box::new(move || {
|
||||||
.immediate_intervals
|
// let callback = current_thread()
|
||||||
.borrow()
|
// .immediate_intervals
|
||||||
.get(&id)
|
// .borrow()
|
||||||
.map(Rc::clone);
|
// .get(&id)
|
||||||
let Some(callback) = callback else {
|
// .map(Rc::clone);
|
||||||
return; // interval was cleared before this turn ran
|
// let Some(callback) = callback else {
|
||||||
};
|
// return; // interval was cleared before this turn ran
|
||||||
(callback.borrow_mut())();
|
// };
|
||||||
// Re-enqueue for the next turn if still live.
|
// (callback.borrow_mut())();
|
||||||
if current_thread().immediate_intervals.borrow().contains_key(&id) {
|
// // Re-enqueue for the next turn if still live.
|
||||||
schedule_immediate_interval(owner, id);
|
// if current_thread()
|
||||||
}
|
// .immediate_intervals
|
||||||
}));
|
// .borrow()
|
||||||
}
|
// .contains_key(&id)
|
||||||
|
// {
|
||||||
|
// schedule_immediate_interval(owner, id);
|
||||||
|
// }
|
||||||
|
// }));
|
||||||
|
// }
|
||||||
|
|
||||||
fn dispatch_expired_timers() {
|
fn dispatch_expired_timers() {
|
||||||
let now = deadline_from_now(Duration::ZERO);
|
let now = deadline_from_now(Duration::ZERO);
|
||||||
@@ -1207,12 +1216,8 @@ fn dispatch_expired_timers() {
|
|||||||
.deadline
|
.deadline
|
||||||
.checked_add(interval)
|
.checked_add(interval)
|
||||||
.unwrap_or(Duration::MAX);
|
.unwrap_or(Duration::MAX);
|
||||||
let next_timer = TimerNode::interval(
|
let next_timer =
|
||||||
timer.id,
|
TimerNode::interval(timer.id, next_deadline, interval, Rc::clone(&callback));
|
||||||
next_deadline,
|
|
||||||
interval,
|
|
||||||
Rc::clone(&callback),
|
|
||||||
);
|
|
||||||
current_thread().timers.borrow_mut().insert(next_timer);
|
current_thread().timers.borrow_mut().insert(next_timer);
|
||||||
|
|
||||||
push_local_macrotask(Box::new(move || {
|
push_local_macrotask(Box::new(move || {
|
||||||
@@ -1394,8 +1399,7 @@ mod tests {
|
|||||||
// turns by awaiting a sleep between checks.
|
// turns by awaiting a sleep between checks.
|
||||||
let count = Rc::new(Cell::new(0usize));
|
let count = Rc::new(Cell::new(0usize));
|
||||||
let count_clone = Rc::clone(&count);
|
let count_clone = Rc::clone(&count);
|
||||||
let handle_slot: Rc<RefCell<Option<super::IntervalHandle>>> =
|
let handle_slot: Rc<RefCell<Option<super::IntervalHandle>>> = Rc::new(RefCell::new(None));
|
||||||
Rc::new(RefCell::new(None));
|
|
||||||
let handle_slot_clone = Rc::clone(&handle_slot);
|
let handle_slot_clone = Rc::clone(&handle_slot);
|
||||||
|
|
||||||
let handle = set_interval(Duration::ZERO, move || {
|
let handle = set_interval(Duration::ZERO, move || {
|
||||||
|
|||||||
@@ -128,7 +128,12 @@ impl LayoutNode {
|
|||||||
/// Return a copy of this node (and all descendants) with all positions shifted by `offset`.
|
/// Return a copy of this node (and all descendants) with all positions shifted by `offset`.
|
||||||
pub fn translated(self, offset: Point) -> Self {
|
pub fn translated(self, offset: Point) -> Self {
|
||||||
fn translate_rect(r: Rect, o: Point) -> Rect {
|
fn translate_rect(r: Rect, o: Point) -> Rect {
|
||||||
Rect::new(r.origin.x + o.x, r.origin.y + o.y, r.size.width, r.size.height)
|
Rect::new(
|
||||||
|
r.origin.x + o.x,
|
||||||
|
r.origin.y + o.y,
|
||||||
|
r.size.width,
|
||||||
|
r.size.height,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
let rect = translate_rect(self.rect, offset);
|
let rect = translate_rect(self.rect, offset);
|
||||||
let clip_rect = self.clip_rect.map(|r| translate_rect(r, offset));
|
let clip_rect = self.clip_rect.map(|r| translate_rect(r, offset));
|
||||||
@@ -143,7 +148,11 @@ impl LayoutNode {
|
|||||||
rect: translate_rect(img.rect, offset),
|
rect: translate_rect(img.rect, offset),
|
||||||
..img
|
..img
|
||||||
});
|
});
|
||||||
let children = self.children.into_iter().map(|c| c.translated(offset)).collect();
|
let children = self
|
||||||
|
.children
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| c.translated(offset))
|
||||||
|
.collect();
|
||||||
LayoutNode {
|
LayoutNode {
|
||||||
rect,
|
rect,
|
||||||
clip_rect,
|
clip_rect,
|
||||||
@@ -256,8 +265,9 @@ fn layout_element(
|
|||||||
perf_stats.nodes += 1;
|
perf_stats.nodes += 1;
|
||||||
|
|
||||||
// Viewport culling: skip fully off-screen elements inside a scroll box.
|
// Viewport culling: skip fully off-screen elements inside a scroll box.
|
||||||
if let Some(clip) = clip_rect {
|
if let Some(clip) = clip_rect
|
||||||
if !rects_overlap(rect, clip) {
|
&& !rects_overlap(rect, clip)
|
||||||
|
{
|
||||||
perf_stats.viewport_culled += 1;
|
perf_stats.viewport_culled += 1;
|
||||||
return LayoutNode {
|
return LayoutNode {
|
||||||
path,
|
path,
|
||||||
@@ -274,7 +284,6 @@ fn layout_element(
|
|||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Incremental layout cache check.
|
// Incremental layout cache check.
|
||||||
// Round to nearest pixel to eliminate floating-point representation
|
// Round to nearest pixel to eliminate floating-point representation
|
||||||
@@ -368,7 +377,13 @@ fn layout_element(
|
|||||||
if pushed_clip {
|
if pushed_clip {
|
||||||
scene.pop_clip();
|
scene.pop_clip();
|
||||||
}
|
}
|
||||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
cache_layout(
|
||||||
|
layout_cache,
|
||||||
|
cache_key,
|
||||||
|
&interaction,
|
||||||
|
&scene.items[scene_start..],
|
||||||
|
rect.origin,
|
||||||
|
);
|
||||||
return interaction;
|
return interaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,7 +397,13 @@ fn layout_element(
|
|||||||
if pushed_clip {
|
if pushed_clip {
|
||||||
scene.pop_clip();
|
scene.pop_clip();
|
||||||
}
|
}
|
||||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
cache_layout(
|
||||||
|
layout_cache,
|
||||||
|
cache_key,
|
||||||
|
&interaction,
|
||||||
|
&scene.items[scene_start..],
|
||||||
|
rect.origin,
|
||||||
|
);
|
||||||
return interaction;
|
return interaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,7 +529,13 @@ fn layout_element(
|
|||||||
if pushed_clip {
|
if pushed_clip {
|
||||||
scene.pop_clip();
|
scene.pop_clip();
|
||||||
}
|
}
|
||||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
cache_layout(
|
||||||
|
layout_cache,
|
||||||
|
cache_key,
|
||||||
|
&interaction,
|
||||||
|
&scene.items[scene_start..],
|
||||||
|
rect.origin,
|
||||||
|
);
|
||||||
return interaction;
|
return interaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,7 +545,13 @@ fn layout_element(
|
|||||||
if pushed_clip {
|
if pushed_clip {
|
||||||
scene.pop_clip();
|
scene.pop_clip();
|
||||||
}
|
}
|
||||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
cache_layout(
|
||||||
|
layout_cache,
|
||||||
|
cache_key,
|
||||||
|
&interaction,
|
||||||
|
&scene.items[scene_start..],
|
||||||
|
rect.origin,
|
||||||
|
);
|
||||||
return interaction;
|
return interaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,7 +560,13 @@ fn layout_element(
|
|||||||
if pushed_clip {
|
if pushed_clip {
|
||||||
scene.pop_clip();
|
scene.pop_clip();
|
||||||
}
|
}
|
||||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
cache_layout(
|
||||||
|
layout_cache,
|
||||||
|
cache_key,
|
||||||
|
&interaction,
|
||||||
|
&scene.items[scene_start..],
|
||||||
|
rect.origin,
|
||||||
|
);
|
||||||
return interaction;
|
return interaction;
|
||||||
}
|
}
|
||||||
interaction.children = layout_container_children(
|
interaction.children = layout_container_children(
|
||||||
@@ -545,7 +584,13 @@ fn layout_element(
|
|||||||
scene.pop_clip();
|
scene.pop_clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
cache_layout(
|
||||||
|
layout_cache,
|
||||||
|
cache_key,
|
||||||
|
&interaction,
|
||||||
|
&scene.items[scene_start..],
|
||||||
|
rect.origin,
|
||||||
|
);
|
||||||
interaction
|
interaction
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,10 +603,13 @@ fn cache_layout(
|
|||||||
origin: Point,
|
origin: Point,
|
||||||
) {
|
) {
|
||||||
let neg = Point::new(-origin.x, -origin.y);
|
let neg = Point::new(-origin.x, -origin.y);
|
||||||
cache.results.insert(key, CachedLayout {
|
cache.results.insert(
|
||||||
|
key,
|
||||||
|
CachedLayout {
|
||||||
interaction_node: interaction.clone().translated(neg),
|
interaction_node: interaction.clone().translated(neg),
|
||||||
scene_items: scene_items.iter().map(|i| i.translated(neg)).collect(),
|
scene_items: scene_items.iter().map(|i| i.translated(neg)).collect(),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hit_path_node(node: &LayoutNode, point: crate::scene::Point) -> Option<Vec<HitTarget>> {
|
fn hit_path_node(node: &LayoutNode, point: crate::scene::Point) -> Option<Vec<HitTarget>> {
|
||||||
@@ -986,7 +1034,14 @@ fn intrinsic_size(
|
|||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
let insets = content_insets(&element.style);
|
let insets = content_insets(&element.style);
|
||||||
let result = intrinsic_size_inner(element, available_size, insets, text_system, perf_stats, layout_cache);
|
let result = intrinsic_size_inner(
|
||||||
|
element,
|
||||||
|
available_size,
|
||||||
|
insets,
|
||||||
|
text_system,
|
||||||
|
perf_stats,
|
||||||
|
layout_cache,
|
||||||
|
);
|
||||||
layout_cache.intrinsic_cache.insert(cache_key, result);
|
layout_cache.intrinsic_cache.insert(cache_key, result);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@@ -1047,8 +1102,13 @@ fn intrinsic_size_inner(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let intrinsic_content =
|
let intrinsic_content = intrinsic_container_content_size(
|
||||||
intrinsic_container_content_size(element, content_size, text_system, perf_stats, layout_cache);
|
element,
|
||||||
|
content_size,
|
||||||
|
text_system,
|
||||||
|
perf_stats,
|
||||||
|
layout_cache,
|
||||||
|
);
|
||||||
|
|
||||||
UiSize::new(
|
UiSize::new(
|
||||||
explicit_width
|
explicit_width
|
||||||
@@ -1539,9 +1599,9 @@ fn child_rect(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::{LayoutCache, layout_scene, layout_snapshot, layout_snapshot_with_cache};
|
use super::{LayoutCache, layout_scene, layout_snapshot, layout_snapshot_with_cache};
|
||||||
use crate::scene::{Color, DisplayItem, Point, Quad, Rect, UiSize};
|
use crate::scene::{Color, DisplayItem, Point, Quad, Rect, UiSize};
|
||||||
|
use crate::text::TextSystem;
|
||||||
use crate::text::{TextStyle, TextWrap};
|
use crate::text::{TextStyle, TextWrap};
|
||||||
use crate::tree::{Edges, Element, ElementId, FlexDirection};
|
use crate::tree::{Edges, Element, ElementId, FlexDirection};
|
||||||
use crate::text::TextSystem;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn row_layout_apportions_fixed_and_flex_children() {
|
fn row_layout_apportions_fixed_and_flex_children() {
|
||||||
@@ -2156,10 +2216,12 @@ mod tests {
|
|||||||
let size = UiSize::new(400.0, 600.0);
|
let size = UiSize::new(400.0, 600.0);
|
||||||
let snap1 = layout_snapshot_with_cache(1, size, &root, &mut text_system, &mut layout_cache);
|
let snap1 = layout_snapshot_with_cache(1, size, &root, &mut text_system, &mut layout_cache);
|
||||||
let snap2 = layout_snapshot_with_cache(2, size, &root, &mut text_system, &mut layout_cache);
|
let snap2 = layout_snapshot_with_cache(2, size, &root, &mut text_system, &mut layout_cache);
|
||||||
assert_eq!(snap1.scene.items, snap2.scene.items, "scene items should be identical");
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
snap1.interaction_tree.root,
|
snap1.scene.items, snap2.scene.items,
|
||||||
snap2.interaction_tree.root,
|
"scene items should be identical"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
snap1.interaction_tree.root, snap2.interaction_tree.root,
|
||||||
"interaction trees should be identical"
|
"interaction trees should be identical"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2198,12 +2260,14 @@ mod tests {
|
|||||||
let item_height = 40.0;
|
let item_height = 40.0;
|
||||||
let viewport_height = 200.0;
|
let viewport_height = 200.0;
|
||||||
let n = 100;
|
let n = 100;
|
||||||
let mut scroll_box = Element::scroll_box(0.0).width(400.0).height(viewport_height);
|
let mut scroll_box = Element::scroll_box(0.0)
|
||||||
|
.width(400.0)
|
||||||
|
.height(viewport_height);
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
scroll_box = scroll_box.child(
|
scroll_box = scroll_box.child(
|
||||||
Element::new()
|
Element::new()
|
||||||
.height(item_height)
|
.height(item_height)
|
||||||
.child(Element::text(format!("item {i}"), text_style()))
|
.child(Element::text(format!("item {i}"), text_style())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let root = Element::new().child(scroll_box);
|
let root = Element::new().child(scroll_box);
|
||||||
@@ -2214,11 +2278,20 @@ mod tests {
|
|||||||
let snap = layout_snapshot_with_cache(1, size, &root, &mut text_system, &mut layout_cache);
|
let snap = layout_snapshot_with_cache(1, size, &root, &mut text_system, &mut layout_cache);
|
||||||
|
|
||||||
// Only text items within the 200px viewport should appear in the scene.
|
// Only text items within the 200px viewport should appear in the scene.
|
||||||
let text_items = snap.scene.items.iter().filter(|item| {
|
let text_items = snap
|
||||||
matches!(item, DisplayItem::Text(_))
|
.scene
|
||||||
}).count();
|
.items
|
||||||
|
.iter()
|
||||||
|
.filter(|item| matches!(item, DisplayItem::Text(_)))
|
||||||
|
.count();
|
||||||
// At most 6 text items should be visible (5 fit + 1 partial).
|
// At most 6 text items should be visible (5 fit + 1 partial).
|
||||||
assert!(text_items <= 6, "expected <= 6 text items in scene, got {text_items} (culling not working)");
|
assert!(
|
||||||
assert!(text_items >= 4, "expected >= 4 text items visible, got {text_items}");
|
text_items <= 6,
|
||||||
|
"expected <= 6 text items in scene, got {text_items} (culling not working)"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
text_items >= 4,
|
||||||
|
"expected >= 4 text items visible, got {text_items}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
//! sibling crates.
|
//! sibling crates.
|
||||||
|
|
||||||
pub(crate) mod trace_targets {
|
pub(crate) mod trace_targets {
|
||||||
|
#![allow(dead_code)]
|
||||||
pub const PLATFORM: &str = "ruin_ui::platform";
|
pub const PLATFORM: &str = "ruin_ui::platform";
|
||||||
pub const SCENE: &str = "ruin_ui::scene";
|
pub const SCENE: &str = "ruin_ui::scene";
|
||||||
pub const TEXT_PERF: &str = "ruin_ui::text_perf";
|
pub const TEXT_PERF: &str = "ruin_ui::text_perf";
|
||||||
|
|||||||
@@ -990,7 +990,9 @@ fn fs_main(input: VertexOut) -> @location(0) vec4<f32> {
|
|||||||
bottom: physical.y - image.placement.top + height,
|
bottom: physical.y - image.placement.top + height,
|
||||||
},
|
},
|
||||||
content: image.content,
|
content: image.content,
|
||||||
color: glyph.color_opt.map_or(text.default_color, color_from_cosmic),
|
color: glyph
|
||||||
|
.color_opt
|
||||||
|
.map_or(text.default_color, color_from_cosmic),
|
||||||
data: image.data.clone(),
|
data: image.data.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1470,7 +1472,11 @@ fn create_glyph_atlas(
|
|||||||
/// Resolve `Color::SENTINEL` to `default_color`; return other colors unchanged.
|
/// Resolve `Color::SENTINEL` to `default_color`; return other colors unchanged.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn resolve_glyph_color(color: Color, default_color: Color) -> Color {
|
fn resolve_glyph_color(color: Color, default_color: Color) -> Color {
|
||||||
if color == Color::SENTINEL { default_color } else { color }
|
if color == Color::SENTINEL {
|
||||||
|
default_color
|
||||||
|
} else {
|
||||||
|
color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color_from_cosmic(color: cosmic_text::Color) -> Color {
|
fn color_from_cosmic(color: cosmic_text::Color) -> Color {
|
||||||
@@ -2297,7 +2303,7 @@ fn clip_textured_rect(
|
|||||||
/// return only those glyph indices. This turns O(all_glyphs) iteration into
|
/// return only those glyph indices. This turns O(all_glyphs) iteration into
|
||||||
/// O(log(lines) + visible_glyphs) — critical for large text nodes in scroll
|
/// O(log(lines) + visible_glyphs) — critical for large text nodes in scroll
|
||||||
/// boxes where only a few lines are on screen at once.
|
/// boxes where only a few lines are on screen at once.
|
||||||
fn clip_visible_glyphs<'a>(text: &'a PreparedText, clip: Option<Rect>) -> &'a [GlyphInstance] {
|
fn clip_visible_glyphs(text: &PreparedText, clip: Option<Rect>) -> &[GlyphInstance] {
|
||||||
let Some(clip) = clip else {
|
let Some(clip) = clip else {
|
||||||
return &text.glyphs;
|
return &text.glyphs;
|
||||||
};
|
};
|
||||||
@@ -2332,7 +2338,12 @@ fn text_texture_key(text: &PreparedText) -> TextTextureKey {
|
|||||||
.map(|bounds| (bounds.width.to_bits(), bounds.height.to_bits())),
|
.map(|bounds| (bounds.width.to_bits(), bounds.height.to_bits())),
|
||||||
font_size_bits: text.font_size.to_bits(),
|
font_size_bits: text.font_size.to_bits(),
|
||||||
line_height_bits: text.line_height.to_bits(),
|
line_height_bits: text.line_height.to_bits(),
|
||||||
color: (text.default_color.r, text.default_color.g, text.default_color.b, text.default_color.a),
|
color: (
|
||||||
|
text.default_color.r,
|
||||||
|
text.default_color.g,
|
||||||
|
text.default_color.b,
|
||||||
|
text.default_color.a,
|
||||||
|
),
|
||||||
glyphs: text
|
glyphs: text
|
||||||
.glyphs
|
.glyphs
|
||||||
.iter()
|
.iter()
|
||||||
@@ -2354,10 +2365,7 @@ mod tests {
|
|||||||
ActiveClip, blend_rgba, build_text_vertices, build_vertices, clip_rect_array,
|
ActiveClip, blend_rgba, build_text_vertices, build_vertices, clip_rect_array,
|
||||||
push_clip_state, rounded_clip_arrays, text_texture_key,
|
push_clip_state, rounded_clip_arrays, text_texture_key,
|
||||||
};
|
};
|
||||||
use ruin_ui::{
|
use ruin_ui::{ClipRegion, Color, Point, PreparedText, Rect, SceneSnapshot, UiSize};
|
||||||
ClipRegion, Color, GlyphInstance, Point, PreparedText, Rect, SceneSnapshot,
|
|
||||||
TextSelectionStyle, UiSize,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn quad_scenes_expand_to_six_vertices_per_quad() {
|
fn quad_scenes_expand_to_six_vertices_per_quad() {
|
||||||
|
|||||||
Reference in New Issue
Block a user