20 KiB
Модуль стилей: src/interpreter/style.rs
Система CSS-подобных стилей для Glint: парсинг селекторов, построение индекса, каскадное разрешение свойств и кеширование вычисленных стилей.
Перечисления-помощники
SizeValue
pub enum SizeValue {
Px(f32),
Percent(f32),
}
Абсолютное (Px) или относительное (Percent) значение размера.
impl SizeValue {
pub fn resolve(self, relative_to: Option<f32>) -> f32
}
Percent разрешается относительно relative_to; Px возвращается как есть. При Percent и relative_to = None возвращается процент как число.
Overflow
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Overflow {
#[default] Visible,
Hidden,
Scroll,
Auto,
}
Используется для overflow-x, overflow-y.
Position
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Position {
#[default] Static,
Relative,
Absolute,
Sticky,
Fixed,
}
Определяет схему позиционирования элемента.
Display
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Display {
#[default] Block,
Flex,
Grid,
Inline,
None,
}
LayoutDirection
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayoutDirection {
Column,
Row,
Grid,
}
ContentAlign
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContentAlign {
Start,
Center,
End,
}
TextAlign
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextAlign {
Left,
Center,
Right,
}
impl From<TextAlign> for iced::alignment::Horizontal
Парсинг селекторов
AttributeSelector
pub enum AttributeSelector {
Exists(String),
Equals(String, String),
}
Селектор атрибута: [attr] или [attr=value].
Combinator
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Combinator {
Descendant, // пробел
Child, // >
NextSibling, // +
Subsequent, // ~
}
split_selectors(input: &str) -> Vec<String>
Разделяет группу селекторов по запятой с учётом вложенности скобок. Например, "Button, Label:hover" → ["Button", "Label:hover"].
CompoundSelector
#[derive(Debug, Clone)]
pub struct CompoundSelector {
pub tag: Option<String>,
pub id: Option<String>,
pub classes: Vec<String>,
pub pseudo_classes: Vec<String>,
pub attributes: Vec<AttributeSelector>,
}
Методы:
-
CompoundSelector::parse(input: &str) -> Self— парсит простой селектор видаButton#id.primary:hover[named=val]. Разбирает посимвольно, группируя части по первому символу (#,.,:,[). -
fn specificity(&self) -> (u32, u32, u32)— возвращает специфичность по правилу CSS: (id, class+attr+pseudo, tag). -
pub fn matches_element(type_name, el_id, el_classes, active_pseudo, structural, el_attributes) -> bool— проверяет, соответствует ли элемент данному простому селектору. Учитывает:- Совпадение
tag(или*) - Совпадение
id - Наличие всех
classes - Наличие всех
attributes(Exists / Equals) - Псевдоклассы:
first-child,last-child,first-of-type,empty,root,nth-child(...); остальные сравниваются сactive_pseudo.
- Совпадение
ComplexSelector
#[derive(Debug, Clone)]
pub struct ComplexSelector {
pub compounds: Vec<CompoundSelector>,
pub combinators: Vec<Combinator>,
}
Методы:
-
ComplexSelector::parse(input: &str) -> Self— парсит сложный селектор (напримерPanel > Button.primary). Разбивает на части по комбинаторам (>,+,~, пробел), парсит каждую какCompoundSelector. -
fn check_compound_against(&self, i, info: &AncestorInfo) -> bool— проверяет, соответствует лиi-й compound переданной информации о предке. -
pub fn matches(type_name, el_id, el_classes, active_pseudo, structural, ancestors, preceding_siblings, el_attributes) -> bool— полная проверка сложного селектора: последний compound — целевой элемент, остальные — предки/соседи в соответствии с комбинаторами. -
pub fn specificity(&self) -> (u32, u32, u32)— сумма специфичностей всех compounds. -
pub fn as_simple(&self) -> Option<&CompoundSelector>— если compounds содержит ровно один элемент, возвращает его; иначеNone. -
pub fn has_pseudo_class(&self, pc: &str) -> bool— есть ли среди compounds указанный псевдокласс.
Вспомогательные структуры
AncestorInfo
#[derive(Debug, Clone)]
pub struct AncestorInfo {
pub type_name: String,
pub id: Option<String>,
pub classes: Vec<String>,
}
Методы:
AncestorInfo::new(type_name, classes) -> SelfAncestorInfo::new_with_id(type_name, id, classes) -> Self
Используется при проверке сложных селекторов — описывает предка или соседний элемент.
StructuralContext
#[derive(Debug, Clone, Default)]
pub struct StructuralContext {
pub sibling_index: usize, // 0-based
pub sibling_total: usize,
pub type_index: usize, // среди элементов того же типа
pub type_total: usize,
pub has_children: bool,
pub is_root: bool,
}
Используется для разрешения структурных псевдоклассов (first-child, nth-child, empty, root).
StyleRule
#[derive(Debug, Clone)]
pub struct StyleRule {
pub selector: ComplexSelector,
pub properties: HashMap<String, String>,
}
impl StyleRule {
pub fn build(selector_str: String, properties: HashMap<String, String>) -> Self
}
StyleIndex
pub type RuleId = usize;
#[derive(Debug, Clone)]
pub struct StyleIndex {
pub by_tag: HashMap<String, Vec<RuleId>>,
pub by_class: HashMap<String, Vec<RuleId>>,
pub by_id: HashMap<String, Vec<RuleId>>,
pub by_tag_class: HashMap<(String, String), Vec<RuleId>>,
pub by_tag_id: HashMap<(String, String), Vec<RuleId>>,
pub complex_rules: Vec<(RuleId, RuleId)>,
pub universal_rules: Vec<RuleId>,
pub rule_specificities: Vec<(u32, u32, u32)>,
pub rules: Vec<StyleRule>,
pub epoch: u64,
}
impl StyleIndex {
pub fn new() -> Self
}
Индекс для быстрого поиска правил. Строится в StyleSheet::build_index:
by_tag/by_class/by_id/by_tag_class/by_tag_id— индексы для простых селекторовcomplex_rules— правила со сложными селекторами (всегда проверяются в лоб)universal_rules— правила с*rule_specificities— кеш специфичностейepoch— монотонно возрастающий счётчик для инвалидации кеша
StyleCache
#[derive(Debug, Clone)]
pub struct StyleCache {
entries: HashMap<u64, ComputedStyle>,
max_entries: usize,
}
impl StyleCache {
pub fn new(max_entries: usize) -> Self
pub fn get_or_compute(
&mut self,
type_name: &str,
props: &[(Cow<'_, str>, Cow<'_, str>)],
epoch: u64,
matched_sheets: &[&HashMap<String, String>],
) -> ComputedStyle
pub fn clear(&mut self)
}
Кеш вычисленных стилей. Ключ — хеш от type_name, inline-свойств и epoch. При превышении max_entries кеш полностью очищается.
StyleSheet
#[derive(Debug)]
pub struct StyleSheet {
rules: Vec<StyleRule>,
index: Option<StyleIndex>,
epoch: u64,
cache: Mutex<StyleCache>,
}
Главный тип модуля. Содержит список правил, опциональный индекс и кеш. Реализует Clone (с новым пустым кешем) и Default.
Методы:
-
StyleSheet::new() -> Self— создаёт пустой лист. -
pub fn add_rule(&mut self, selector: String, properties: HashMap<String, String>)— добавляет правило. Пропускает селектор черезsplit_selectors(поддержка групп через запятую). Сбрасываетindex = None. -
pub fn build_index(&mut self)— перестраиваетStyleIndex. Увеличиваетepoch, очищает кеш. Для каждого правила:- Вычисляет специфичность
- Если селектор простой (1 compound) — индексирует по tag/class/id/атрибутам
- Если сложный — помечает как
complex_rules - Универсальные (
*) попадают вuniversal_rules
-
pub fn has_index(&self) -> bool -
pub fn query_index(type_name, el_id, el_classes, active_pseudo, structural, ancestors, preceding_siblings, el_attributes) -> Option<Vec<&HashMap<String, String>>>— использует индекс для быстрого поиска: собирает кандидатов изuniversal_rules,by_tag,by_class,by_id,complex_rules; фильтрует черезComplexSelector::matches; сортирует по специфичности. -
pub fn matching_rules(...) -> Vec<&HashMap<String, String>>— поиск подходящих правил. Пытаетсяquery_index; если индекса нет — линейный перебор всехself.rulesс сортировкой. -
pub fn matching_pseudo_rules(pseudo, ...) -> HashMap<String, String>— ищет правила, содержащие указанный псевдокласс (например:hover). Используетquery_index_for_pseudoили линейный перебор. -
pub fn compute_cached(type_name, props, matched_sheets) -> ComputedStyle— вычисляет итоговый стиль через кеш (обёртка надStyleCache::get_or_compute). ВключаетPerfScope::new("style"). -
pub fn clear_cache(&self)— очищает кеш. -
pub fn is_empty(&self) -> bool—self.rules.is_empty().
Методы с #[cfg(feature = "parallel")]:
matching_rules_batch(type_names, el_ids, el_classes_list, ...) -> Vec<Vec<&HashMap<String, String>>>— параллельный batch-поиск черезrayon::par_iter.
ComputedStyle
#[derive(Debug, Clone, Default)]
pub struct ComputedStyle {
pub font_size: Option<SizeValue>,
pub color: Option<iced::Color>,
pub padding: Option<SizeValue>,
pub padding_top: Option<SizeValue>,
pub padding_right: Option<SizeValue>,
pub padding_bottom: Option<SizeValue>,
pub padding_left: Option<SizeValue>,
pub margin: Option<SizeValue>,
pub margin_top: Option<SizeValue>,
pub margin_right: Option<SizeValue>,
pub margin_bottom: Option<SizeValue>,
pub margin_left: Option<SizeValue>,
pub background: Option<iced::Color>,
pub spacing: Option<SizeValue>,
pub border_radius: Option<SizeValue>,
pub border_width: Option<SizeValue>,
pub border_color: Option<iced::Color>,
pub width: Option<iced::Length>,
pub height: Option<iced::Length>,
pub min_width: Option<SizeValue>,
pub max_width: Option<SizeValue>,
pub min_height: Option<SizeValue>,
pub max_height: Option<SizeValue>,
pub direction: Option<LayoutDirection>,
pub align_items: Option<iced::Alignment>,
pub content_align: Option<ContentAlign>,
pub flex_grow: Option<u16>,
pub position: Option<Position>,
pub top: Option<SizeValue>,
pub right: Option<SizeValue>,
pub bottom: Option<SizeValue>,
pub left: Option<SizeValue>,
pub overflow_x: Option<Overflow>,
pub overflow_y: Option<Overflow>,
pub display: Option<Display>,
pub opacity: Option<f32>,
pub font_weight: Option<u16>,
pub line_height: Option<SizeValue>,
pub text_align: Option<TextAlign>,
}
Итоговый вычисленный стиль элемента. Все поля — Option; отсутствующее свойство означает «не задано / наследуется от родителя».
ComputedStyle::compute()
pub fn compute(
inline: &[(Cow<'_, str>, Cow<'_, str>)],
matched_sheets: &[&HashMap<String, String>],
) -> Self
Собирает стиль через lookup(): для каждого поля вызывается lookup(key, inline, matched_sheets), затем парсится соответствующей функцией. Особенности:
padding/margin— сначала ищутся индивидуальные (-top,-right, и т.д.), потом общие.spacing— альтернативное имяgap.background— сначалаbackground, затемbackground-color.overflow-x/overflow-y— если индивидуальный не найден, применяется общийoverflow.flex_grow— парсится какf32, кастуется вu16.
ComputedStyle::compute_batch()
#[cfg(feature = "parallel")]
pub fn compute_batch<'a>(
pairs: &[(&[(Cow<'_, str>, Cow<'_, str>)], &[&'a HashMap<String, String>])],
) -> Vec<ComputedStyle>
Параллельный batch-вариант через rayon::par_iter.
ComputedStyle::apply_overrides()
pub fn apply_overrides(&mut self, sheet: &HashMap<String, String>)
Применяет (перезаписывает) заданный набор свойств поверх существующего стиля. Используется для динамических изменений (например :hover-правила, inline-переопределения).
Как работает lookup()
fn lookup<'a>(
key: &str,
inline: &'a [(Cow<'_, str>, Cow<'_, str>)],
matched_sheets: &[&'a HashMap<String, String>],
) -> Option<&'a str>
Порядок разрешения свойства:
- Inline-свойства — перебор пар
(key, value). Поддерживает префиксstyle:(т.е.style:colorэквивалентенcolor). - matched_sheets — список словарей от подходящих CSS-правил, отсортированный по специфичности. Перебирается с конца (последний — самый специфичный).
- Возвращается первое найденное значение.
Функции парсинга
| Функция | Сигнатура | Описание |
|---|---|---|
parse_size |
(s: &str) -> Option<SizeValue> |
Парсит размер: "10" → Px(10), "50%" → Percent(50). auto, fill, stretch → None |
parse_color |
(s: &str) -> Option<iced::Color> |
Парсит цвет: #rgb, #rrggbb, #rrggbbaa, имена (white, black, transparent) |
parse_length |
(s: &str) -> Option<iced::Length> |
Парсит длину Iced: "fill"/"100%", "shrink"/"auto", "50" → Fixed(50) |
parse_overflow |
(s: &str) -> Option<Overflow> |
visible, hidden, scroll, auto |
parse_position |
(s: &str) -> Option<Position> |
static, relative, absolute, sticky, fixed |
parse_display |
(s: &str) -> Option<Display> |
none, block, flex, grid, inline |
parse_direction |
(s: &str) -> Option<LayoutDirection> |
row/horizontal, column/vertical, grid |
parse_alignment |
(s: &str) -> Option<iced::Alignment> |
start, center, end |
parse_content_align |
(s: &str) -> Option<ContentAlign> |
start/left/top, center, end/right/bottom |
parse_opacity |
(s: &str) -> Option<f32> |
Число 0.0–1.0, clamp |
parse_font_weight |
(s: &str) -> Option<u16> |
Имена: normal→400, bold→700, lighter→300, bolder→900; числовые значения |
parse_text_align |
(s: &str) -> Option<TextAlign> |
left, center, right |
resolve_size
pub fn resolve_size(v: Option<SizeValue>, relative_to: Option<f32>) -> Option<f32>
Удобная обёртка над SizeValue::resolve, возвращает Option<f32>.
Использование в renderer.rs
Поля ComputedStyle активно используются в /home/faynot/software/glint-runtime/src/renderer.rs:
| Поле | Где используется |
|---|---|
.color |
Цвет текста в кнопках, текстовых полях, Label |
.background |
Фон контейнеров, кнопок, текстовых полей |
.padding* |
iced::Padding для кнопок, полей ввода, контейнеров |
.margin* |
Отступы вокруг элементов |
.border_radius, .border_width, .border_color |
Рамки кнопок, полей ввода, контейнеров |
.width, .height |
Размеры Scrollable, Column, Row, Image |
.min_width, .max_width, .min_height, .max_height |
Ограничения размеров |
.direction |
Направление флекса (Row/Column) |
.align_items |
Выравнивание дочерних элементов |
.content_align |
Выравнивание контента |
.flex_grow |
Flex-grow с FillPortion |
.spacing |
iced::container::Style spacing, gap в Row/Column |
.position |
Static / Fixed / Absolute / Sticky |
.top, .right, .bottom, .left |
Позиционирование |
.overflow_x, .overflow_y |
Скроллинг (Scrollable) |
.display |
Display::None — скрытие элемента |
.opacity |
Прозрачность |
.font_weight |
Вес шрифта в Text |
.line_height |
Межстрочный интервал |
.text_align |
Горизонтальное выравнивание текста |
.font_size |
Размер шрифта (передаётся от родителя) |
nth_matches(expr: &str, n: usize) -> bool
Внутренняя функция для разрешения :nth-child(an+b), :nth-child(odd), :nth-child(even) и :nth-child(<число>). Поддерживает отрицательные a и b.
Связи с другими модулями
types.rs— каждыйDomNode(как элемент, так и текстовый узел) содержитcomputed_style: ComputedStyle.renderer.rs— импортирует{ComputedStyle, ContentAlign, Display, LayoutDirection, Position, Overflow, StructuralContext, StyleSheet, TextAlign, resolve_size}.mod.rs— используетAncestorInfo,ComputedStyle,StructuralContextпри обходе DOM.