use crate::{
ActiveMasksSecondary, CapacityConfig, EntityId, ExternalTexture, ExternalVisual, InputContents,
LayoutRect, MichiuString, TextLayoutSize, TextSpan, VisualPropertiesSecondary,
define_sparse_secondary, soa::MichiuSoA,
};
use slotmap::SparseSecondaryMap;
use std::sync::Arc;
define_sparse_secondary!(pub struct TextContentsSparse(MichiuString));
define_sparse_secondary!(pub struct TextSpansSparse(Vec<TextSpan>));
define_sparse_secondary!(pub struct InputContentsSparse(InputContents));
impl InputContentsSparse {
pub(crate) fn measure_content<F>(
&mut self,
id: EntityId,
known_dimensions: taffy::Size<Option<f32>>,
available_space: taffy::Size<taffy::AvailableSpace>,
topo_active_masks: &ActiveMasksSecondary,
rnd_visual: &VisualPropertiesSecondary,
measure_text: F,
) -> taffy::LayoutOutput
where
F: FnOnce(bool, Option<f32>) -> TextLayoutSize,
{
let mask = topo_active_masks.at(id);
let has_input = mask.has_input_content();
let has_text = mask.has_text_content();
let last_layout = if has_input {
self.at(id).last_layout
} else {
None
};
if last_layout.is_none() && !has_text {
return taffy::LayoutOutput::from_outer_size(taffy::Size {
width: known_dimensions.width.unwrap_or(0.0),
height: known_dimensions.height.unwrap_or(0.0),
});
}
let auto_wrap = rnd_visual.auto_wrap(id);
let max_width = if auto_wrap {
known_dimensions.width.or({
if let taffy::AvailableSpace::Definite(w) = available_space.width {
Some(w)
} else {
None
}
})
} else {
None
};
if !auto_wrap && let Some(layout) = last_layout {
return taffy::LayoutOutput::from_outer_size(taffy::Size {
width: known_dimensions.width.unwrap_or(layout.width),
height: known_dimensions.height.unwrap_or(layout.height),
});
}
if let Some(layout) = last_layout {
if max_width.is_none() {
return taffy::LayoutOutput::from_outer_size(taffy::Size {
width: known_dimensions.width.unwrap_or(layout.width),
height: known_dimensions.height.unwrap_or(layout.height),
});
}
}
if !has_text {
return taffy::LayoutOutput::from_outer_size(taffy::Size {
width: known_dimensions.width.unwrap_or(0.0),
height: known_dimensions.height.unwrap_or(0.0),
});
}
let size = measure_text(auto_wrap, max_width);
if has_input {
let contents = self.at_mut(id);
contents.last_layout = Some(LayoutRect::new(0.0, 0.0, size.width, size.height));
}
taffy::LayoutOutput::from_outer_size(taffy::Size {
width: known_dimensions.width.unwrap_or(size.width),
height: known_dimensions.height.unwrap_or(size.height),
})
}
}
impl TextSpansSparse {
pub(crate) fn span(&self, id: EntityId) -> &[TextSpan] {
self.find(id).map_or(&[][..], Vec::as_slice)
}
}
#[derive(Default, Clone, derive_more::Deref, derive_more::DerefMut, derive_more::IntoIterator)]
#[into_iterator(owned, ref, ref_mut)]
pub struct ExternalTextureSparse(pub SparseSecondaryMap<EntityId, Arc<dyn ExternalTexture>>);
impl MichiuSoA for ExternalTextureSparse {
type Item = Arc<dyn ExternalTexture>;
#[inline]
fn find(&self, id: EntityId) -> Option<&Self::Item> {
self.0.get(id)
}
#[inline]
fn find_mut(&mut self, id: EntityId) -> Option<&mut Self::Item> {
self.0.get_mut(id)
}
}
impl std::fmt::Debug for ExternalTextureSparse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut map = f.debug_map();
for (id, tex) in &self.0 {
map.entry(
&id,
&format_args!(
"ExternalTexture {{ ptr: {:p}, meta: {:?} }}",
Arc::as_ptr(tex),
tex.metadata()
),
);
}
map.finish()
}
}
#[derive(Default, Clone, derive_more::Deref, derive_more::DerefMut, derive_more::IntoIterator)]
#[into_iterator(owned, ref, ref_mut)]
pub struct ExternalVisualSparse(pub SparseSecondaryMap<EntityId, Arc<dyn ExternalVisual>>);
impl MichiuSoA for ExternalVisualSparse {
type Item = Arc<dyn ExternalVisual>;
#[inline]
fn find(&self, id: EntityId) -> Option<&Self::Item> {
self.0.get(id)
}
#[inline]
fn find_mut(&mut self, id: EntityId) -> Option<&mut Self::Item> {
self.0.get_mut(id)
}
}
impl std::fmt::Debug for ExternalVisualSparse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut map = f.debug_map();
for (id, tex) in &self.0 {
map.entry(
&id,
&format_args!(
"ExternalVisual {{ ptr: {:p}, meta: {:?} }}",
Arc::as_ptr(tex),
tex.metadata()
),
);
}
map.finish()
}
}
pub struct ContentStore {
pub(crate) cont_text_contents: TextContentsSparse,
pub(crate) cont_text_spans: TextSpansSparse,
pub(crate) cont_input_contents: InputContentsSparse,
pub(crate) cont_external_textures: ExternalTextureSparse,
pub(crate) cont_external_visual: ExternalVisualSparse,
pub(crate) cont_cut_text: Option<MichiuString>,
}
impl Default for ContentStore {
fn default() -> Self {
Self::new()
}
}
impl ContentStore {
#[inline]
#[must_use]
pub(crate) fn new() -> Self {
Self {
cont_text_contents: TextContentsSparse(SparseSecondaryMap::new()),
cont_text_spans: TextSpansSparse(SparseSecondaryMap::new()),
cont_input_contents: InputContentsSparse(SparseSecondaryMap::new()),
cont_external_textures: ExternalTextureSparse(SparseSecondaryMap::new()),
cont_external_visual: ExternalVisualSparse(SparseSecondaryMap::new()),
cont_cut_text: None,
}
}
#[inline]
#[must_use]
pub(crate) fn with_capacity(c: &CapacityConfig) -> Self {
Self {
cont_text_contents: TextContentsSparse(SparseSecondaryMap::with_capacity(
c.edit_selections,
)),
cont_text_spans: TextSpansSparse(SparseSecondaryMap::with_capacity(c.cont_text_spans)),
cont_input_contents: InputContentsSparse(SparseSecondaryMap::with_capacity(
c.cont_input_contents,
)),
cont_external_textures: ExternalTextureSparse(SparseSecondaryMap::with_capacity(
c.cont_external_textures,
)),
cont_external_visual: ExternalVisualSparse(SparseSecondaryMap::with_capacity(
c.cont_external_visual,
)),
..Default::default()
}
}
#[inline]
pub(crate) fn clear(&mut self) {
self.cont_text_contents.clear();
self.cont_text_spans.clear();
self.cont_input_contents.clear();
self.cont_external_textures.clear();
self.cont_external_visual.clear();
self.cont_cut_text = None;
}
#[inline]
pub(crate) fn despawn(&mut self, id: EntityId) {
self.cont_text_contents.remove(id);
self.cont_text_spans.remove(id);
self.cont_input_contents.remove(id);
self.cont_external_textures.remove(id);
self.cont_external_visual.remove(id);
self.cont_cut_text = None;
}
#[inline]
pub fn cut_text_mut(&mut self) -> &mut Option<MichiuString> {
&mut self.cont_cut_text
}
#[inline]
pub fn external_textures_mut(&mut self) -> &mut ExternalTextureSparse {
&mut self.cont_external_textures
}
#[inline]
pub fn input_contents_mut(&mut self) -> &mut InputContentsSparse {
&mut self.cont_input_contents
}
#[inline]
pub fn cont_text_contents_mut(&mut self) -> &mut TextContentsSparse {
&mut self.cont_text_contents
}
#[inline]
pub fn text_spans_mut(&mut self) -> &mut TextSpansSparse {
&mut self.cont_text_spans
}
#[inline]
pub fn webview_contents_mut(&mut self) -> &mut ExternalVisualSparse {
&mut self.cont_external_visual
}
}
#[cfg(test)]
mod tests;