use alloc::{rc::Rc, string::String, vec::Vec};
use core::{hash::Hasher, num::NonZeroUsize};
use crate::length_num::LengthNum;
use crate::property::{
NodeProperties, NodePropertiesOrder, Property, PropertyMeta, PropertyValueWithGlobal,
};
use crate::sheet::Rule;
use crate::sheet::{RuleWeight, Theme};
use crate::typing::{Length, LengthType};
#[derive(Debug, Clone, PartialEq)]
pub struct MediaQueryStatus<L: LengthNum> {
pub is_screen: bool,
pub width: L,
pub height: L,
pub pixel_ratio: f32,
pub base_font_size: L,
pub theme: Theme,
pub env: EnvValues<L>,
}
#[derive(Debug, Clone, PartialEq)]
#[allow(missing_docs)]
pub struct EnvValues<L: LengthNum> {
pub safe_area_inset_left: L,
pub safe_area_inset_top: L,
pub safe_area_inset_right: L,
pub safe_area_inset_bottom: L,
}
impl<L: LengthNum> Default for EnvValues<L> {
fn default() -> Self {
Self {
safe_area_inset_left: L::zero(),
safe_area_inset_top: L::zero(),
safe_area_inset_right: L::zero(),
safe_area_inset_bottom: L::zero(),
}
}
}
impl<L: LengthNum> Default for MediaQueryStatus<L> {
fn default() -> Self {
Self::default_screen()
}
}
impl<L: LengthNum> MediaQueryStatus<L> {
pub fn default_screen() -> Self {
Self {
is_screen: true,
width: L::from_i32(800),
height: L::from_i32(600),
pixel_ratio: 1.,
base_font_size: L::from_i32(16),
theme: Theme::Light,
env: Default::default(),
}
}
pub fn default_screen_with_size(width: L, height: L) -> Self {
Self {
is_screen: true,
width,
height,
pixel_ratio: 1.,
base_font_size: L::from_i32(16),
theme: Theme::Light,
env: Default::default(),
}
}
}
#[derive(Clone, Debug)]
pub struct StyleQuery<'a> {
pub(super) style_scope: Option<NonZeroUsize>,
pub(super) extra_style_scope: Option<NonZeroUsize>,
pub(super) host_style_scope: Option<NonZeroUsize>,
pub(super) tag_name: &'a str,
pub(super) id: &'a str,
pub(super) classes: &'a [(String, Option<NonZeroUsize>)],
#[allow(unused)]
pub(super) attributes: &'a [String], }
impl<'a> StyleQuery<'a> {
pub fn single(
style_scope: Option<NonZeroUsize>,
extra_style_scope: Option<NonZeroUsize>,
host_style_scope: Option<NonZeroUsize>,
tag_name: &'a str,
id: &'a str,
classes: &'a [(String, Option<NonZeroUsize>)],
attributes: &'a [String],
) -> Self {
Self {
style_scope,
extra_style_scope,
host_style_scope,
tag_name,
id,
classes,
attributes,
}
}
pub(crate) fn contain_scope(&self, scope: Option<NonZeroUsize>) -> bool {
scope.is_none()
|| self.style_scope == scope
|| self.extra_style_scope == scope
|| self.host_style_scope == scope
}
}
#[derive(Debug, Clone)]
pub struct MatchedRuleRef<'a> {
pub rule: &'a Rc<Rule>,
pub weight: RuleWeight,
}
#[derive(Debug, Clone)]
pub struct MatchedRule {
pub rule: Rc<Rule>,
pub weight: RuleWeight,
pub style_scope: Option<NonZeroUsize>,
}
impl PartialEq for MatchedRule {
fn eq(&self, other: &Self) -> bool {
self.weight.normal() == other.weight.normal()
}
}
impl PartialOrd for MatchedRule {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.weight.normal().cmp(&other.weight.normal()))
}
}
impl Ord for MatchedRule {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.weight.normal().cmp(&other.weight.normal())
}
}
impl Eq for MatchedRule {}
#[derive(Debug, Clone)]
pub struct MatchedRuleList {
pub rules: Vec<MatchedRule>,
}
impl MatchedRuleList {
pub fn new_empty() -> Self {
Self {
rules: Vec::with_capacity(0),
}
}
pub fn get_current_font_size<L: LengthNum>(
&self,
parent_font_size: f32,
parent_node_properties: Option<&NodeProperties>,
extra_styles: &[PropertyMeta],
media_query_status: &MediaQueryStatus<L>,
) -> f32 {
let mut font_size_p = None;
let mut font_size_w: u64 = 0;
fn handle_property_meta<'a>(
font_size_p: &mut Option<&'a LengthType>,
font_size_w: &mut u64,
pm: &'a PropertyMeta,
rw: RuleWeight,
) {
match pm {
PropertyMeta::Normal { property: p } => {
if let Property::FontSize(x) = p {
let w = rw.normal();
if w >= *font_size_w {
*font_size_w = w;
*font_size_p = Some(x);
}
}
}
PropertyMeta::Important { property: p } => {
if let Property::FontSize(x) = p {
let w = rw.important();
if w >= *font_size_w {
*font_size_w = w;
*font_size_p = Some(x);
}
}
}
PropertyMeta::DebugGroup {
properties,
important,
disabled,
..
} => {
if !disabled {
let w = if *important {
rw.important()
} else {
rw.normal()
};
if w >= *font_size_w {
for p in &**properties {
if let Property::FontSize(x) = p {
*font_size_w = w;
*font_size_p = Some(x);
}
}
}
}
}
}
}
for pm in extra_styles.iter() {
handle_property_meta(&mut font_size_p, &mut font_size_w, pm, RuleWeight::inline());
}
for matched_rule in self.rules.iter() {
let rw = matched_rule.weight;
if !matched_rule.rule.has_font_size {
continue;
};
for pm in matched_rule.rule.properties.iter() {
handle_property_meta(&mut font_size_p, &mut font_size_w, pm, rw);
}
}
let default_font_size = media_query_status.base_font_size.to_f32();
let parent_font_size_p = parent_node_properties.map(|x| x.font_size_ref());
let parent_font_size = parent_font_size.to_f32();
let current_font_size = if let Some(p) = font_size_p {
p.to_inner(parent_font_size_p, Length::Px(default_font_size), true)
.and_then(|x| x.resolve_to_f32(media_query_status, parent_font_size, true))
.unwrap_or(parent_font_size)
} else {
parent_font_size
};
current_font_size
}
pub fn merge_node_properties(
&self,
node_properties: &mut NodeProperties,
parent_node_properties: Option<&NodeProperties>,
current_font_size: f32,
extra_styles: &[PropertyMeta],
) {
let mut order = NodePropertiesOrder::new();
let mut merge_property_meta = |pm: &PropertyMeta, rw: RuleWeight| match pm {
PropertyMeta::Normal { property: p } => {
if order.compare_property(p, rw.normal()) {
node_properties.merge_property(p, parent_node_properties, current_font_size)
}
}
PropertyMeta::Important { property: p } => {
if order.compare_property(p, rw.important()) {
node_properties.merge_property(p, parent_node_properties, current_font_size)
}
}
PropertyMeta::DebugGroup {
properties,
important,
disabled,
..
} => {
if !disabled {
let w = if *important {
rw.important()
} else {
rw.normal()
};
for p in &**properties {
if order.compare_property(p, w) {
node_properties.merge_property(
p,
parent_node_properties,
current_font_size,
)
}
}
}
}
};
for pm in extra_styles.iter() {
merge_property_meta(pm, RuleWeight::inline());
}
for matched_rule in self.rules.iter() {
for pm in matched_rule.rule.properties.iter() {
merge_property_meta(pm, matched_rule.weight);
}
}
}
pub fn animation_name_style_scope(&self) -> Option<NonZeroUsize> {
let mut w = u64::MIN;
let mut ret = None;
let mut check_property_meta = |pm: &PropertyMeta, rw: RuleWeight, scope| {
for p in pm.iter() {
if let Property::AnimationName(..) = p {
let self_w = if pm.is_important() {
rw.important()
} else {
rw.normal()
};
if self_w >= w {
w = self_w;
ret = scope;
}
}
}
};
for matched_rule in self.rules.iter() {
for pm in matched_rule.rule.properties.iter() {
check_property_meta(pm, matched_rule.weight, matched_rule.style_scope);
}
}
ret
}
pub fn fast_hash_value(&self) -> u64 {
let mut hasher = ahash::AHasher::default();
for matched_rule in self.rules.iter() {
let rule: &Rule = &matched_rule.rule;
hasher.write_usize(rule as *const Rule as usize);
hasher.write_u64(matched_rule.weight.normal());
}
hasher.finish()
}
}