mod init;
mod render;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use crate::event::{Handlers, ImeHandlers, KeyHandlers, MouseHandlers};
use crate::executor::Executor;
use crate::focus::FocusRegistry;
use crate::focus_ring::FocusBounds;
use crate::hit_test::HitTestList;
use crate::image_cache::ImageCache;
use crate::ime::ImeRegistry;
use crate::layout::LayoutTree;
use crate::reactive_state::StateRegistry;
use crate::text_system::TextSystem;
use crate::types::{AccessibilityNode, ElementId};
use slate_reactive::{ObserverId, Runtime};
use slate_renderer::Scene;
use slate_renderer::atlas::Atlas;
use slate_renderer::glyph_pipeline::GlyphPipeline;
use slate_renderer::image_pipeline::ImagePipeline;
use slate_renderer::instanced_rect_pipeline::InstancedRectPipeline;
use slate_renderer::shadow_pipeline::ShadowPipeline;
use slate_text::GlyphCache;
use wgpu::{BindGroup, Buffer, Device, Queue, RequestDeviceError, Texture, TextureView};
pub struct HeadlessApp {
device: Device,
queue: Queue,
width: u32,
height: u32,
scale_factor: f64,
target_texture: Texture,
target_view: TextureView,
staging_buffer: Buffer,
#[allow(dead_code)]
viewport_buf: Buffer,
viewport_bg: BindGroup,
unit_quad: Buffer,
image_atlas: Atlas,
glyph_atlas: Atlas,
rect_pipeline: InstancedRectPipeline,
shadow_pipeline: ShadowPipeline,
image_pipeline: ImagePipeline,
glyph_pipeline: GlyphPipeline,
text_system: TextSystem,
layout_tree: LayoutTree,
hit_test_list: HitTestList,
a11y_nodes: Vec<AccessibilityNode>,
scene: Scene,
executor: Executor,
runtime: Arc<Runtime>,
observer_id: ObserverId,
state_registry: StateRegistry,
text_shaping_cache: crate::paint_cache::TextShapingCache,
image_cache: ImageCache,
glyph_cache: GlyphCache,
handler_map: HashMap<ElementId, Handlers>,
mouse_handler_map: HashMap<ElementId, MouseHandlers>,
parent_map: HashMap<ElementId, ElementId>,
key_handler_map: HashMap<ElementId, KeyHandlers>,
focus_registry: FocusRegistry,
focus_bounds: HashMap<ElementId, FocusBounds>,
ime_registry: RefCell<ImeRegistry>,
ime_handler_map: HashMap<ElementId, ImeHandlers>,
ime_registered_ids: HashSet<ElementId>,
}
#[derive(thiserror::Error, Debug)]
pub enum HeadlessError {
#[error("no compatible GPU adapter found")]
NoAdapter,
#[error("failed to open GPU device: {0}")]
Device(#[from] RequestDeviceError),
#[error("text system init failed: {0}")]
TextSystem(String),
#[error("buffer mapping failed")]
BufferMapping,
#[error("invalid image dimensions")]
InvalidDimensions,
}
impl HeadlessApp {
pub fn new(width: u32, height: u32) -> Result<Self, HeadlessError> {
Self::with_scale_factor(width, height, 1.0)
}
fn aligned_bytes_per_row(width: u32) -> u32 {
let bytes_per_pixel = 4u32; let unaligned = width * bytes_per_pixel;
let alignment = 256u32;
unaligned.div_ceil(alignment) * alignment
}
pub fn size(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn scale_factor(&self) -> f64 {
self.scale_factor
}
pub fn text_system_mut(&mut self) -> &mut TextSystem {
&mut self.text_system
}
pub fn scene(&self) -> &Scene {
&self.scene
}
pub fn a11y_nodes(&self) -> Vec<AccessibilityNode> {
self.a11y_nodes.clone()
}
pub fn runtime(&self) -> Arc<Runtime> {
self.runtime.clone()
}
pub fn text_shaping_cache_hits(&self) -> u64 {
self.text_shaping_cache.hit_count()
}
pub fn text_shaping_cache_misses(&self) -> u64 {
self.text_shaping_cache.miss_count()
}
pub fn text_shaping_cache_len(&self) -> usize {
self.text_shaping_cache.len()
}
pub fn text_shaping_cache_memory(&self) -> usize {
self.text_shaping_cache.memory_used()
}
#[cfg(any(test, feature = "test-hooks"))]
pub fn set_ime_state(&mut self, id: ElementId, state: crate::ime::ImeState) {
let rc = self.ime_registry.borrow_mut().register(id);
*rc.borrow_mut() = state;
}
}