Files
Glint-Runtime/STYLE-SYSTEM-COMPLETION-PLAN.md
2026-07-08 23:45:37 +03:00

22 KiB
Raw Blame History

План завершения стилевой системы Glint

Цель

Достичь полной мощности HTML+CSS в языке Glint. Система разделена на 4 уровня:

  • Парсер (glt/src/style_parser.rs) — чтение .glts
  • AST + Компилятор (glt/src/ast.rs, compiler.rs) — представление и байткод
  • Интерпретатор (runtime/src/interpreter/) — VDOM + вычисление стилей
  • Рендерер (runtime/src/renderer.rs) — отрисовка через Iced

1. Селекторы (критично)

1.1 Структурированное представление селектора

Сейчас: Селектор — плоская строка "Panel.desktop", matching по точному совпадению.

Нужно: Разобрать селектор в структуру:

enum Selector {
    Simple(SimpleSelector),
    Compound(CompoundSelector),     // без комбинатора
    Complex {
        left: Box<Selector>,
        combinator: Combinator,
        right: Box<Selector>,
    },
    List(Vec<Selector>),            // запятые: `Button, .btn, #id`
}

struct SimpleSelector {
    tag: Option<String>,            // `Button` | `*`
    id: Option<String>,             // `#myid`
    classes: Vec<String>,           // `.foo.bar`
    pseudo_classes: Vec<PseudoClass>, // `:hover`
    pseudo_element: Option<PseudoElement>, // `::before`
    attribute: Vec<AttributeSelector>, // `[disabled]`, `[type="text"]`
}

enum Combinator {
    Descendant,   // пробел
    Child,        // >
    NextSibling,  // +
    Subsequent,   // ~
}

enum PseudoClass {
    Hover, Active, Focus,
    FirstChild, LastChild,
    NthChild(i32, i32),  // An+B
    FirstOfType, LastOfType,
    Disabled, Enabled, Checked,
    Root, Empty,
    Not(Box<Selector>),
    // ...
}

struct AttributeSelector {
    name: String,
    op: AttrOp,
    value: Option<String>,
}

enum AttrOp { Set, Eq, Contain, StartsWith, EndsWith, InList }

1.2 Парсинг селекторов (style_parser.rs)

  • Заменить parse_selector() на полноценный парсер селекторов.
  • Поддержка: Button.active:hover, Panel > .btn, #header, [type="text"], *, :nth-child(2n+1), запятые.
  • Валидация и сообщения об ошибках.

1.3 Байткод для селекторов

  • Новый опкод OP_SELECTOR или расширение OP_STYLE_RULE:
    • Записать дерево селектора в байткод.
    • На каждый тип узла — свой подопкод (0x300x3F).
  • Компилятор: сериализация структуры селектора.
  • Читатель (reader.rs): десериализация обратно в структуру.

1.4 Runtime matching (interpreter/mod.rs)

  • Заменить collect_matching_styles() на алгоритм обхода дерева селекторов.
  • Специфичность: inline > id > class/attr/pseudo > tag.
  • При одинаковой специфичности — порядок объявления (последний побеждает).
  • Комбинаторы: при обходе VDOM подниматься вверх по предкам/соседям.
  • Кэширование результатов matching для производительности.

2. Псевдоклассы (высокий приоритет)

2.1 Интерактивные (:hover, :active, :focus)

Сейчас: Только у Button ховер/нажатие — хардкод в renderer.

Нужно:

  • Хранить состояние интерактивности в VDOM/Element (ElementState).
  • В renderer передавать статус (hovered/pressed/focused) в collect_matching_styles.
  • :hover-стили переопределяют базовые.
  • Плавный переход между состояниями (см. transitions).

Изменения:

  • types.rs: Element может получить поле state: ElementState.
  • renderer.rs: у render_element появляется доступ к состоянию мыши.
  • style.rs: ComputedStyle::compute() принимает псевдоклассы.

2.2 Структурные (:first-child, :nth-child, :last-child, :first-of-type, :last-of-type, :empty, :root)

Сейчас: Не поддерживаются.

Нужно:

  • При matching вычислять позицию элемента среди siblings.
  • Для :nth-child(An+B) — парсить выражение и проверять (index - B) % A == 0.
  • :emptyel.children.is_empty().
  • :root — элемент верхнего уровня в VDOM.

2.3 Состояния (:disabled, :enabled, :checked)

  • :disabled/:enabled — по свойству disabled.
  • :checked — по свойству checked.

3. Медиа-запросы (@media) (средний приоритет)

3.1 Парсер

@media (max-width: 600px) {
    Panel { direction: vertical }
}
  • Парсить @media в Directive::MediaQuery { query, child_span }.
  • Поддержка: width, height, min-width, max-width, orientation, prefers-color-scheme.
  • Логические операторы: and, or, not, , (or).

3.2 Runtime

  • Собрать информацию об окне (размер, тема ОС).
  • При изменении размера окна переоценивать media queries.
  • Хранить в Document::media_state: MediaState.
  • collect_matching_styles проверяет media query перед добавлением правил.

4. Анимации (@anim) (средний приоритет)

4.1 Парсинг — уже есть

@anim name { from { ... } to { ... } } — парсится и компилируется.

4.2 Runtime — НЕ реализован

Сейчас: OP_STYLE_ANIM пропускается.

Нужно:

  1. Хранить KeyframeAnimation в StyleSheet (не только в байткоде, но и в runtime-структуре).
  2. В Element поле animation_state: HashMap<String, AnimationInstance>.
  3. Система тиков:
    • Iced subscription на каждый кадр (on_every_eventEvent::Window(RedrawRequested)).
    • При каждом тике обновлять elapsed для активных анимаций.
    • Интерполировать между keyframes.
  4. Свойства animation-name, animation-duration, animation-timing-function, animation-delay, animation-iteration-count, animation-fill-mode.
  5. Поддержка @keyframes с процентами: 0% { ... } 50% { ... } 100% { ... }.

4.3 Timing functions

  • linear, ease, ease-in, ease-out, ease-in-out.
  • cubic-bezier(p1x, p1y, p2x, p2y).
  • steps(n, direction).

5. Транзишены (transition) (средний приоритет)

5.1 Свойства

  • transition-property, transition-duration, transition-timing-function, transition-delay.
  • Шорткат transition: all 0.3s ease.

5.2 Runtime

  • При изменении ComputedStyle (например, при ховере) не применять мгновенно, а запускать интерполяцию.
  • Хранить TransitionState на элемент: HashMap<String, (start_value, end_value, elapsed, duration, easing)>.
  • Каждый кадр обновлять transitioning-свойства.

6. Новые CSS-свойства в ComputedStyle

6.1 Типографика

Свойство Тип Парсер
font-family Option<String> список шрифтов
font-weight Option<u16> normal(400), bold(700), числовое
font-style Option<FontStyle> normal, italic, oblique
line-height Option<f32> числовое или normal
letter-spacing Option<f32> px
text-align Option<TextAlign> left, center, right, justify
text-decoration Option<TextDecoration> none, underline, line-through
text-transform Option<TextTransform> none, uppercase, lowercase, capitalize
white-space Option<WhiteSpace> normal, nowrap, pre
word-break Option<WordBreak> normal, break-all, keep-all
text-overflow Option<TextOverflow> clip, ellipsis

6.2 Фон и границы

Свойство Тип Парсер
opacity Option<f32> 0.01.0
background-image Option<String> url(...)
background-repeat Option<BgRepeat> repeat, no-repeat
background-size Option<BgSize> cover, contain, px, %
background-position Option<BgPos> center, top left, px
linear-gradient(...) Option<Gradient> парсить в Value::Call и обрабатывать
box-shadow Option<Vec<Shadow>> offset-x offset-y blur spread color
text-shadow Option<Vec<Shadow>> то же
outline Option<Outline> width, style, color
border-style Option<BorderStyle> solid, dashed, dotted
border (шорткат) разворачивать в width/style/color

6.3 Flexbox (расширение)

Свойство Тип
justify-content Option<JustifyContent>
flex-wrap Option<FlexWrap>
flex-direction Option<FlexDirection>
align-self Option<Alignment>
align-content Option<AlignContent>
flex (шорткат)
flex-shrink Option<f32>
flex-basis Option<Length>

6.4 Grid Layout

Свойство Тип
grid-template-columns Option<Vec<GridTrack>>
grid-template-rows Option<Vec<GridTrack>>
grid-gap / gap уже есть как spacing
grid-column Option<(u32, u32)>
grid-row Option<(u32, u32)>
grid-auto-flow Option<GridFlow>

6.5 Позиционирование (расширение)

Свойство Тип Статус
position: absolute сейчас мэппится в Static
position: relative сейчас мэппится в Static
position: sticky сейчас мэппится в Static
z-index Option<i32>
display Option<Display> block, flex, grid, none, inline

6.6 Прочее

Свойство Тип
visibility Option<Visibility>
cursor Option<Cursor>
pointer-events Option<PointerEvents>
transform Option<Vec<Transform>>
transform-origin Option<String>
backdrop-filter Option<Vec<Filter>>
filter Option<Vec<Filter>>
list-style Option<ListStyle>
isolation Option<Isolation>
mix-blend-mode Option<BlendMode>

6.7 Итого: ~4060 новых полей в ComputedStyle

Каждое поле: тип Option<T>, значение по умолчанию None. Парсинг: новая функция parse_<property>(s: &str) -> Option<T> для каждого. Lookup в ComputedStyle::compute().

Рендеринг в Iced:

  • Iced 0.14 поддерживает Background::Gradient.
  • Box-shadow пока не поддерживается, но можно эмулировать через container style с offset-тенью.
  • Transforms и filters — Iced пока не нативно; нужен кастомный widget или пропуск.

7. Система каскада и наследования

7.1 Наследование (сейчас: только color, font-size)

Добавить наследование для:

  • font-family, font-weight, font-style, line-height, letter-spacing, text-align, white-space, word-break, text-transform, visibility, cursor, pointer-events.

Механизм: в render_element() передавать не только parent_color и parent_font_size, а &ComputedStyle родителя или отдельный InheritedStyle.

7.2 inherit / initial / unset

  • Специальные значения для любого свойства.

7.3 !important

  • Флаг важности в правиле. Переопределяет специфичность.
  • Парсить value!important.

8. CSS-функции и значения

8.1 calc()

enum CalcValue {
    Number(f32),
    Percentage(f32),
    Add(Box<CalcValue>, Box<CalcValue>),
    Sub(Box<CalcValue>, Box<CalcValue>),
    Mul(Box<CalcValue>, Box<CalcValue>),
    Div(Box<CalcValue>, Box<CalcValue>),
    Var(String),
}
  • Парсить calc(100% - 20px).
  • Вычислять в runtime с учётом контекста (размер родителя).

8.2 var() — CSS custom properties

  • --my-var: value; в правилах.
  • var(--my-var, fallback) при использовании.
  • Хранить custom properties в ComputedStyle.custom_props: HashMap<String, String>.
  • Наследуются по умолчанию.

8.3 min(), max(), clamp()

  • min(100%, 500px), max(200px, 50%), clamp(200px, 50%, 500px).

8.4 rgb(), rgba(), hsl(), hsla(), hwb(), oklch(), color-mix()

  • Сейчас только #hex и 3 named colors.
  • Парсить rgb(255, 0, 0), rgba(255, 0, 0, 0.5).
  • Расширить parse_color().

8.5 Named colors

  • Расширить словарь до 140+ CSS named colors (AliceBlue, ...).

9. @-правила

9.1 @import

@import "theme.glts"
  • В парсере: обработать @import, загрузить файл, влить переменные и миксины.
  • Может быть сложным (циклы, кэширование). Опционально на раннем этапе.

9.2 @font-face

@font-face {
    font-family: "MyFont"
    src: fs:/fonts/myfont.ttf
}
  • Регистрировать в Iced через font::Family.
  • Потребует iced::font::load().

9.3 @scope (CSS Cascading Layers)

  • @scope (.card) { ... } — ограничение области действия правил.

10. Рендеринг (renderer.rs)

10.1 Псевдоклассы в рендере

  • Для интерактивных элементов передавать button::Status.
  • :hover/:active-стили должны пересчитываться при изменении статуса.

10.2 Flexbox (полноценный)

  • justify-content: Iced Row/Column не имеют нативного justifyContent; эмулировать через наполнители (Length::Fill).
  • flex-wrap: нужен Flow widget или эмуляция строками.

10.3 Grid

  • Полноценный CSS Grid: имплементировать кастомный Iced widget или разбивать на строки/колонки вручную.
  • grid-template-columns: 1fr 1fr 1fr → разбивка children на ряды.
  • grid-column/grid-row: позиционирование ячеек.

10.4 display: none

  • Пропускать элемент при рендеринге (уже частично — None возвращается).

10.5 z-index

  • Сортировать fixed-слой по z-index внутри stack.

10.6 position: absolute / relative

  • relative: смещение через padding контейнера.
  • absolute: позиционирование относительно предка с position: relative (или окна). Эмулировать через stack слои, как fixed.

10.7 position: sticky

  • Эмулировать через Iced scrollable + перехват событий скролла.
  • Пока не поддерживается Iced нативно. Можно через subscription на scroll position + ручное позиционирование.

10.8 opacity

  • Iced: widget.opacity(f32) (iced 0.14+).

10.9 box-shadow, text-shadow

  • Эмуляция: container с подложкой (offset background) или кастомный шейдер.
  • Iced 0.14 имеет Border только с color, width, radius. Shadow — нет.

10.10 outline

  • Через border с outline-offset эмуляцией.

10.11 Трансформы (transform)

  • Iced не поддерживает transforms нативно.
  • Потребуется кастомный widget с lyon или vello для path-трансформаций.
  • На раннем этапе — документировать как unsupported.

11. Инструментарий разработчика

11.1 Вывод вычисленных стилей

  • Флаг --debug-styles в CLI: печатать ComputedStyle каждого элемента.

11.2 Инспектор элементов

  • Iced debug overlay: iced::widget::pane_grid с инспектором.
  • Показывать matched selectors, specificity, computed properties.

11.3 Hot-reload стилей

  • Перекомпиляция .glts без перезапуска приложения.
  • Обновление bytecode в runtime.

12. Порядок реализации (приоритеты)

Фаза 1: Критическое (MVP+)

  1. Структурированные селекторы + matching engine со специфичностью
  2. Комбинаторы (особенно descendant)
  3. Псевдоклассы :hover, :active, :focus
  4. Селектор по ID (#id)
  5. Множественные селекторы через запятую
  6. Псевдоклассы :first-child, :nth-child, :last-child, :empty

Фаза 2: Визуальное обогащение

  1. Новые свойства: opacity, font-weight, text-align, justify-content, line-height
  2. fill-шорткаты для flex (flex-grow/shrink/basis)
  3. position: absolute / relative / sticky
  4. z-index
  5. display: none
  6. calc(), min(), max(), clamp()
  7. rgb(), rgba(), hsl() + расширенный словарь named colors
  8. !important

Фаза 3: Продвинутое

  1. @media queries
  2. Animations (playback)
  3. Transitions
  4. box-shadow, text-shadow (эмуляция)
  5. CSS Grid
  6. flex-wrap + justify-content полноценно
  7. var() CSS custom properties
  8. @import
  9. @font-face

Фаза 4: Экспертное

  1. transform, transform-origin
  2. backdrop-filter, filter
  3. gradient (linear, radial)
  4. color-mix(), oklch()
  5. Hot-reload
  6. DevTools инспектор
  7. @scope

13. Архитектурные изменения

13.1 ComputedStyle → разбить на подс-труктуры

Вместо 40+ плоских полей:

pub struct ComputedStyle {
    pub box_model: BoxModel,
    pub typography: Typography,
    pub background: BackgroundStyle,
    pub border: BorderStyle,
    pub flex: FlexStyle,
    pub grid: GridStyle,
    pub position: Positioning,
    pub effects: Effects,
    pub overflow: OverflowStyle,
    pub animation: AnimationStyle,
    pub custom: HashMap<String, String>,
    pub display: Option<Display>,
    pub visibility: Option<Visibility>,
}

13.2 Selector engine — отдельный модуль

mod selector {
    pub struct Selector { ... }
    pub fn parse(input: &str) -> Result<Selector, String>;
    pub fn specificity(&self) -> (u32, u32, u32);
    pub fn matches(&self, el: &Element, ctx: &MatchContext) -> bool;
}

13.3 StyleSheet — расширение

pub struct StyleRule {
    pub selector: Selector,
    pub properties: HashMap<String, String>,
    pub source_order: u32,
    pub media: Option<MediaQuery>,
    pub important: HashSet<String>,
}

13.4 Система тиков для анимаций/транзишенов

  • Новый модуль animation в runtime.
  • AnimationEngine с HashMap<AnimationId, AnimationInstance>.
  • Iced subscription: time::every(Duration::from_millis(16)) (60fps).

14. Оценка сложности

Компонент Новые файлы Изменённые файлы Пример LOC
Selector parser glt/src/selector.rs style_parser.rs ~500
Selector matching runtime/src/selector.rs mod.rs ~400
ComputedStyle расширение style.rs +400
Медиа-запросы style_parser.rs, mod.rs ~300
Анимации runtime/src/animation.rs style.rs, mod.rs, app.rs ~500
Транзишены runtime/src/transition.rs ~400
Псевдоклассы mod.rs, renderer.rs, types.rs ~300
Новые парсеры свойств style.rs +600
Рендеринг renderer.rs +500
Flex/Grid runtime/src/layout.rs renderer.rs ~400
calc/var runtime/src/values.rs style.rs ~300
@import/@font-face style_parser.rs, renderer.rs ~200
Инструменты runtime/src/inspector.rs cli.rs, app.rs ~300

Итого: ~57 новых файлов, ~15 изменённых, ~50006000 строк нового кода.