use std::collections::HashMap;
use crate::core::Rect;
#[cfg(not(feature = "mini"))]
use super::view_widgets::data_grid::DataGrid;
#[cfg(not(feature = "mini"))]
use super::view_widgets::tree_table::TreeTable;
#[cfg(not(feature = "mini"))]
use super::view_widgets::virtual_table::VirtualTable;
use super::{Widget, WidgetKind};
pub mod types;
pub use types::*;
pub mod coercion;
pub use coercion::*;
pub mod constructors;
pub use constructors::*;
pub mod properties;
pub(crate) use properties::*;
pub mod access;
pub use access::*;
pub mod registration;
#[cfg(test)]
pub mod tests;
impl Default for WidgetFactory {
fn default() -> Self {
Self::new_with_defaults()
}
}
impl WidgetFactory {
pub fn new() -> Self {
Self {
capabilities: Vec::new(),
key_to_index: HashMap::new(),
kind_to_index: Vec::new(),
constructors: HashMap::new(),
}
}
pub fn new_with_defaults() -> Self {
let mut factory = Self::new();
factory.register_core_widgets();
factory
}
pub fn register(&mut self, capability: WidgetCapability, ctor: WidgetCtor) {
let idx = self.capabilities.len();
if self.kind_to_index.iter().all(|(kind, _)| *kind != capability.kind) {
self.kind_to_index.push((capability.kind, idx));
}
let canonical_key = normalize_key(capability.canonical_name);
self.key_to_index.insert(canonical_key.clone(), idx);
self.constructors.insert(canonical_key, ctor);
for alias in capability.aliases {
let key = normalize_key(alias);
self.key_to_index.insert(key.clone(), idx);
self.constructors.insert(key, ctor);
}
self.capabilities.push(capability);
}
pub fn create(
&self,
kind_or_name: &str,
geometry: Rect,
text: &str,
) -> Option<Box<dyn Widget>> {
let key = normalize_key(kind_or_name);
self.constructors.get(&key).map(|ctor| ctor(geometry, text))
}
pub fn create_by_kind(
&self,
kind: WidgetKind,
geometry: Rect,
text: &str,
) -> Option<Box<dyn Widget>> {
let capability = self.capability_by_kind(kind)?;
self.create(capability.canonical_name, geometry, text)
}
pub fn capability(&self, kind_or_name: &str) -> Option<&WidgetCapability> {
let key = normalize_key(kind_or_name);
let idx = self.key_to_index.get(&key).copied()?;
self.capabilities.get(idx)
}
pub fn capability_by_kind(&self, kind: WidgetKind) -> Option<&WidgetCapability> {
let idx = self
.kind_to_index
.iter()
.find(|(stored_kind, _)| *stored_kind == kind)
.map(|(_, index)| *index)?;
self.capabilities.get(idx)
}
pub fn capabilities(&self) -> &[WidgetCapability] {
&self.capabilities
}
pub fn read_property(
&self,
widget: &dyn Widget,
property_name: &str,
) -> Result<CapabilityValue, CapabilityAccessError> {
let capability =
self.capability_for_widget(widget).ok_or(CapabilityAccessError::UnknownWidget)?;
let normalized = normalize_key(property_name);
let Some(property) =
capability.properties.iter().find(|schema| normalize_key(schema.name) == normalized)
else {
return Err(CapabilityAccessError::UnknownProperty);
};
if !property.readable {
return Err(CapabilityAccessError::UnsupportedOnWidget);
}
read_widget_property_value(widget, property.name)
}
pub fn write_property(
&self,
widget: &mut dyn Widget,
property_name: &str,
value: CapabilityValue,
) -> Result<(), CapabilityAccessError> {
let capability =
self.capability_for_widget(widget).ok_or(CapabilityAccessError::UnknownWidget)?;
let normalized = normalize_key(property_name);
let Some(property) =
capability.properties.iter().find(|schema| normalize_key(schema.name) == normalized)
else {
return Err(CapabilityAccessError::UnknownProperty);
};
if !property.writable {
return Err(CapabilityAccessError::ReadOnlyProperty);
}
write_widget_property_value(widget, property.name, value)
}
fn capability_for_widget(&self, widget: &dyn Widget) -> Option<&WidgetCapability> {
#[cfg(not(feature = "mini"))]
if widget_as::<DataGrid>(widget).is_some() {
return self.capability("data_grid");
}
#[cfg(not(feature = "mini"))]
if widget_as::<VirtualTable>(widget).is_some() {
return self.capability("virtual_table");
}
#[cfg(not(feature = "mini"))]
if widget_as::<TreeTable>(widget).is_some() {
return self.capability("tree_table");
}
self.capability_by_kind(widget.kind())
}
pub fn default_property_value(
&self,
kind_or_name: &str,
property_name: &str,
) -> Result<CapabilityValue, CapabilityAccessError> {
let capability =
self.capability(kind_or_name).ok_or(CapabilityAccessError::UnknownWidget)?;
let normalized = normalize_key(property_name);
let Some(property) =
capability.properties.iter().find(|schema| normalize_key(schema.name) == normalized)
else {
return Err(CapabilityAccessError::UnknownProperty);
};
default_widget_property_value(capability.kind, property.name)
.ok_or(CapabilityAccessError::UnsupportedOnWidget)
}
pub fn property_schema(
&self,
kind_or_name: &str,
property_name: &str,
) -> Result<PropertySchema, CapabilityAccessError> {
let capability =
self.capability(kind_or_name).ok_or(CapabilityAccessError::UnknownWidget)?;
let normalized = normalize_key(property_name);
let Some(property) =
capability.properties.iter().find(|schema| normalize_key(schema.name) == normalized)
else {
return Err(CapabilityAccessError::UnknownProperty);
};
Ok(*property)
}
pub fn capability_manifest(
&self,
kind_or_name: &str,
) -> Result<WidgetCapabilityManifest, CapabilityAccessError> {
let capability =
self.capability(kind_or_name).ok_or(CapabilityAccessError::UnknownWidget)?;
let mut properties = Vec::with_capacity(capability.properties.len());
for property in capability.properties {
let default_value = default_widget_property_value(capability.kind, property.name)
.ok_or(CapabilityAccessError::UnsupportedOnWidget)?;
properties.push(CapabilityPropertyManifest { schema: *property, default_value });
}
Ok(WidgetCapabilityManifest {
kind: capability.kind,
canonical_name: capability.canonical_name,
aliases: capability.aliases.to_vec(),
properties,
events: capability.events.to_vec(),
commands: capability.commands.to_vec(),
})
}
}