use std::{
any::TypeId,
collections::HashMap,
ops::{Add, AddAssign},
sync::Arc,
time::Instant,
};
use dashmap::DashMap;
use indextree::NodeId;
use parking_lot::RwLock;
use rayon::prelude::*;
use smallvec::SmallVec;
use tracing::debug;
use winit::window::CursorIcon;
use crate::{
Clipboard, ComputeCommand, ComputeResourceManager, DrawCommand, Px,
accessibility::{AccessibilityActionHandler, AccessibilityNode},
cursor::CursorEvent,
px::{PxPosition, PxSize},
renderer::Command,
};
use super::constraint::{Constraint, DimensionValue};
pub struct AccessibilityBuilderGuard<'a> {
node_id: NodeId,
metadatas: &'a ComponentNodeMetaDatas,
node: AccessibilityNode,
}
impl<'a> AccessibilityBuilderGuard<'a> {
pub fn role(mut self, role: accesskit::Role) -> Self {
self.node.role = Some(role);
self
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.node.label = Some(label.into());
self
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.node.description = Some(description.into());
self
}
pub fn value(mut self, value: impl Into<String>) -> Self {
self.node.value = Some(value.into());
self
}
pub fn numeric_value(mut self, value: f64) -> Self {
self.node.numeric_value = Some(value);
self
}
pub fn numeric_range(mut self, min: f64, max: f64) -> Self {
self.node.min_numeric_value = Some(min);
self.node.max_numeric_value = Some(max);
self
}
pub fn focusable(mut self) -> Self {
self.node.focusable = true;
self
}
pub fn focused(mut self) -> Self {
self.node.focused = true;
self
}
pub fn toggled(mut self, toggled: accesskit::Toggled) -> Self {
self.node.toggled = Some(toggled);
self
}
pub fn disabled(mut self) -> Self {
self.node.disabled = true;
self
}
pub fn hidden(mut self) -> Self {
self.node.hidden = true;
self
}
pub fn action(mut self, action: accesskit::Action) -> Self {
self.node.actions.push(action);
self
}
pub fn actions(mut self, actions: impl IntoIterator<Item = accesskit::Action>) -> Self {
self.node.actions.extend(actions);
self
}
pub fn key(mut self, key: impl Into<String>) -> Self {
self.node.key = Some(key.into());
self
}
pub fn commit(self) {
drop(self);
}
}
impl Drop for AccessibilityBuilderGuard<'_> {
fn drop(&mut self) {
if let Some(mut metadata) = self.metadatas.get_mut(&self.node_id) {
metadata.accessibility = Some(self.node.clone());
}
}
}
pub struct ComponentNode {
pub fn_name: String,
pub measure_fn: Option<Box<MeasureFn>>,
pub input_handler_fn: Option<Box<InputHandlerFn>>,
}
#[derive(Default)]
pub struct ComponentNodeMetaData {
pub computed_data: Option<ComputedData>,
pub rel_position: Option<PxPosition>,
pub abs_position: Option<PxPosition>,
pub event_clip_rect: Option<crate::PxRect>,
pub(crate) commands: SmallVec<[(Command, TypeId); 4]>,
pub clips_children: bool,
pub accessibility: Option<AccessibilityNode>,
pub accessibility_action_handler: Option<AccessibilityActionHandler>,
}
impl ComponentNodeMetaData {
pub fn none() -> Self {
Self {
computed_data: None,
rel_position: None,
abs_position: None,
event_clip_rect: None,
commands: SmallVec::new(),
clips_children: false,
accessibility: None,
accessibility_action_handler: None,
}
}
pub fn push_draw_command<C: DrawCommand + 'static>(&mut self, command: C) {
let command = Box::new(command);
let command = command as Box<dyn DrawCommand>;
let command = Command::Draw(command);
self.commands.push((command, TypeId::of::<C>()));
}
pub fn push_compute_command<C: ComputeCommand + 'static>(&mut self, command: C) {
let command = Box::new(command);
let command = command as Box<dyn ComputeCommand>;
let command = Command::Compute(command);
self.commands.push((command, TypeId::of::<C>()));
}
}
pub type ComponentNodeTree = indextree::Arena<ComponentNode>;
pub type ComponentNodeMetaDatas = DashMap<NodeId, ComponentNodeMetaData>;
#[derive(Debug, Clone, PartialEq)]
pub enum MeasurementError {
NodeNotFoundInTree,
NodeNotFoundInMeta,
MeasureFnFailed(String),
ChildMeasurementFailed(NodeId),
}
pub type MeasureFn =
dyn Fn(&MeasureInput<'_>) -> Result<ComputedData, MeasurementError> + Send + Sync;
pub struct MeasureInput<'a> {
pub current_node_id: indextree::NodeId,
pub tree: &'a ComponentNodeTree,
pub parent_constraint: &'a Constraint,
pub children_ids: &'a [indextree::NodeId],
pub metadatas: &'a ComponentNodeMetaDatas,
pub compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
pub gpu: &'a wgpu::Device,
}
impl<'a> MeasureInput<'a> {
pub fn metadata_mut(&self) -> dashmap::mapref::one::RefMut<'_, NodeId, ComponentNodeMetaData> {
self.metadatas
.get_mut(&self.current_node_id)
.expect("Metadata for current node must exist during measure")
}
pub fn measure_children(
&self,
nodes_to_measure: Vec<(NodeId, Constraint)>,
) -> Result<HashMap<NodeId, ComputedData>, MeasurementError> {
let results = measure_nodes(
nodes_to_measure,
self.tree,
self.metadatas,
self.compute_resource_manager.clone(),
self.gpu,
);
let mut successful_results = HashMap::new();
for (child_id, result) in results {
match result {
Ok(size) => successful_results.insert(child_id, size),
Err(e) => {
debug!("Measurement error for child {child_id:?}: {e:?}");
return Err(e);
}
};
}
Ok(successful_results)
}
pub fn measure_child(
&self,
child_id: NodeId,
constraint: &Constraint,
) -> Result<ComputedData, MeasurementError> {
measure_node(
child_id,
constraint,
self.tree,
self.metadatas,
self.compute_resource_manager.clone(),
self.gpu,
)
}
pub fn place_child(&self, child_id: NodeId, position: PxPosition) {
place_node(child_id, position, self.metadatas);
}
pub fn enable_clipping(&self) {
self.metadata_mut().clips_children = true;
}
pub fn disable_clipping(&self) {
self.metadata_mut().clips_children = false;
}
}
pub type InputHandlerFn = dyn Fn(InputHandlerInput) + Send + Sync;
pub struct InputHandlerInput<'a> {
pub computed_data: ComputedData,
pub cursor_position_rel: Option<PxPosition>,
pub(crate) cursor_position_abs: &'a mut Option<PxPosition>,
pub cursor_events: &'a mut Vec<CursorEvent>,
pub keyboard_events: &'a mut Vec<winit::event::KeyEvent>,
pub ime_events: &'a mut Vec<winit::event::Ime>,
pub key_modifiers: winit::keyboard::ModifiersState,
pub requests: &'a mut WindowRequests,
pub clipboard: &'a mut Clipboard,
pub(crate) current_node_id: indextree::NodeId,
pub(crate) metadatas: &'a ComponentNodeMetaDatas,
}
impl InputHandlerInput<'_> {
pub fn block_cursor(&mut self) {
self.cursor_position_abs.take();
self.cursor_events.clear();
}
pub fn block_keyboard(&mut self) {
self.keyboard_events.clear();
}
pub fn block_ime(&mut self) {
self.ime_events.clear();
}
pub fn block_all(&mut self) {
self.block_cursor();
self.block_keyboard();
self.block_ime();
}
pub fn accessibility(&self) -> AccessibilityBuilderGuard<'_> {
AccessibilityBuilderGuard {
node_id: self.current_node_id,
metadatas: self.metadatas,
node: AccessibilityNode::new(),
}
}
pub fn set_accessibility_action_handler(
&self,
handler: impl Fn(accesskit::Action) + Send + Sync + 'static,
) {
if let Some(mut metadata) = self.metadatas.get_mut(&self.current_node_id) {
metadata.accessibility_action_handler = Some(Box::new(handler));
}
}
}
#[derive(Default, Debug)]
pub struct WindowRequests {
pub cursor_icon: CursorIcon,
pub ime_request: Option<ImeRequest>,
}
#[derive(Debug)]
pub struct ImeRequest {
pub size: PxSize,
pub(crate) position: Option<PxPosition>, }
impl ImeRequest {
pub fn new(size: PxSize) -> Self {
Self {
size,
position: None, }
}
}
pub fn measure_node(
node_id: NodeId,
parent_constraint: &Constraint,
tree: &ComponentNodeTree,
component_node_metadatas: &ComponentNodeMetaDatas,
compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
gpu: &wgpu::Device,
) -> Result<ComputedData, MeasurementError> {
component_node_metadatas.insert(node_id, Default::default());
let node_data_ref = tree
.get(node_id)
.ok_or(MeasurementError::NodeNotFoundInTree)?;
let node_data = node_data_ref.get();
let children: Vec<_> = node_id.children(tree).collect(); let timer = Instant::now();
debug!(
"Measuring node {} with {} children, parent constraint: {:?}",
node_data.fn_name,
children.len(),
parent_constraint
);
let size = if let Some(measure_fn) = &node_data.measure_fn {
measure_fn(&MeasureInput {
current_node_id: node_id,
tree,
parent_constraint,
children_ids: &children,
metadatas: component_node_metadatas,
compute_resource_manager,
gpu,
})
} else {
DEFAULT_LAYOUT_DESC(&MeasureInput {
current_node_id: node_id,
tree,
parent_constraint,
children_ids: &children,
metadatas: component_node_metadatas,
compute_resource_manager,
gpu,
})
}?;
debug!(
"Measured node {} in {:?} with size {:?}",
node_data.fn_name,
timer.elapsed(),
size
);
let mut metadata = component_node_metadatas.entry(node_id).or_default();
metadata.computed_data = Some(size);
Ok(size)
}
pub fn place_node(
node: indextree::NodeId,
rel_position: PxPosition,
component_node_metadatas: &ComponentNodeMetaDatas,
) {
component_node_metadatas
.entry(node)
.or_default()
.rel_position = Some(rel_position);
}
pub const DEFAULT_LAYOUT_DESC: &MeasureFn = &|input| {
if input.children_ids.is_empty() {
return Ok(ComputedData::min_from_constraint(input.parent_constraint));
}
let nodes_to_measure: Vec<(NodeId, Constraint)> = input
.children_ids
.iter()
.map(|&child_id| (child_id, *input.parent_constraint)) .collect();
let children_results_map = measure_nodes(
nodes_to_measure,
input.tree,
input.metadatas,
input.compute_resource_manager.clone(),
input.gpu,
);
let mut aggregate_size = ComputedData::ZERO;
let mut first_error: Option<MeasurementError> = None;
let mut successful_children_data = Vec::new();
for &child_id in input.children_ids {
match children_results_map.get(&child_id) {
Some(Ok(child_size)) => {
successful_children_data.push((child_id, *child_size));
}
Some(Err(e)) => {
debug!(
"Child node {child_id:?} measurement failed for parent {:?}: {e:?}",
input.current_node_id
);
if first_error.is_none() {
first_error = Some(MeasurementError::ChildMeasurementFailed(child_id));
}
}
None => {
debug!(
"Child node {child_id:?} was not found in measure_nodes results for parent {:?}",
input.current_node_id
);
if first_error.is_none() {
first_error = Some(MeasurementError::MeasureFnFailed(format!(
"Result for child {child_id:?} missing"
)));
}
}
}
}
if let Some(error) = first_error {
return Err(error);
}
if successful_children_data.is_empty() && !input.children_ids.is_empty() {
return Err(MeasurementError::MeasureFnFailed(
"All children failed to measure or results missing in DEFAULT_LAYOUT_DESC".to_string(),
));
}
for (child_id, child_size) in successful_children_data {
aggregate_size = aggregate_size.max(child_size);
place_node(child_id, PxPosition::ZERO, input.metadatas); }
let mut final_width = aggregate_size.width;
let mut final_height = aggregate_size.height;
match input.parent_constraint.width {
DimensionValue::Fixed(w) => final_width = w,
DimensionValue::Wrap { min, max } => {
if let Some(min_w) = min {
final_width = final_width.max(min_w);
}
if let Some(max_w) = max {
final_width = final_width.min(max_w);
}
}
DimensionValue::Fill { min, max } => {
if let Some(min_w) = min {
final_width = final_width.max(min_w);
}
if let Some(max_w) = max {
final_width = final_width.min(max_w);
}
}
}
match input.parent_constraint.height {
DimensionValue::Fixed(h) => final_height = h,
DimensionValue::Wrap { min, max } => {
if let Some(min_h) = min {
final_height = final_height.max(min_h);
}
if let Some(max_h) = max {
final_height = final_height.min(max_h);
}
}
DimensionValue::Fill { min, max } => {
if let Some(min_h) = min {
final_height = final_height.max(min_h);
}
if let Some(max_h) = max {
final_height = final_height.min(max_h);
}
}
}
Ok(ComputedData {
width: final_width,
height: final_height,
})
};
pub fn measure_nodes(
nodes_to_measure: Vec<(NodeId, Constraint)>,
tree: &ComponentNodeTree,
component_node_metadatas: &ComponentNodeMetaDatas,
compute_resource_manager: Arc<RwLock<ComputeResourceManager>>,
gpu: &wgpu::Device,
) -> HashMap<NodeId, Result<ComputedData, MeasurementError>> {
if nodes_to_measure.is_empty() {
return HashMap::new();
}
for (node_id, _) in &nodes_to_measure {
component_node_metadatas.insert(*node_id, Default::default());
}
nodes_to_measure
.into_par_iter()
.map(|(node_id, parent_constraint)| {
let result = measure_node(
node_id,
&parent_constraint,
tree,
component_node_metadatas,
compute_resource_manager.clone(),
gpu,
);
(node_id, result)
})
.collect::<HashMap<NodeId, Result<ComputedData, MeasurementError>>>()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ComputedData {
pub width: Px,
pub height: Px,
}
impl Add for ComputedData {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
width: self.width + rhs.width,
height: self.height + rhs.height,
}
}
}
impl AddAssign for ComputedData {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl ComputedData {
pub const ZERO: Self = Self {
width: Px(0),
height: Px(0),
};
pub fn min_from_constraint(constraint: &Constraint) -> Self {
let width = match constraint.width {
DimensionValue::Fixed(w) => w,
DimensionValue::Wrap { min, .. } => min.unwrap_or(Px(0)),
DimensionValue::Fill { min, .. } => min.unwrap_or(Px(0)),
};
let height = match constraint.height {
DimensionValue::Fixed(h) => h,
DimensionValue::Wrap { min, .. } => min.unwrap_or(Px(0)),
DimensionValue::Fill { min, .. } => min.unwrap_or(Px(0)),
};
Self { width, height }
}
pub fn min(self, rhs: Self) -> Self {
Self {
width: self.width.min(rhs.width),
height: self.height.min(rhs.height),
}
}
pub fn max(self, rhs: Self) -> Self {
Self {
width: self.width.max(rhs.width),
height: self.height.max(rhs.height),
}
}
}