Skip to main content

atmos/os_lib/web_engine/
paint_update.rs

1//! DOM インラインスタイル変更の軽量再ペイント(PaintOnly)判定。
2//!
3//! スクロールイベントやタイマー等で JavaScript が `element.style.transform = ...` や
4//! `element.style.color = ...` などの見た目のみを変えるプロパティを頻繁に更新する場合、
5//! 毎回 HTML を再パースして全ルールを再カスケードする全再レイアウト(約 350ms)を行うと
6//! 画面がカクつき、スクロールの追従性が著しく劣化する。
7//!
8//! 本モジュールは、蓄積された保留スタイル更新(pending updates)と構造変更フラグから、
9//! 全再レイアウトを省略して RenderElement への直接上書き(再ペイントのみ)で済ませられるか
10//! を決定論的に判定する純粋ロジックを提供する。
11
12extern crate alloc;
13
14use alloc::string::String;
15use alloc::vec::Vec;
16use super::repaint_scope::is_paint_only;
17
18/// 保留中のスタイル更新 1 件。
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct PendingStyleUpdate {
21    pub node_idx: usize,
22    pub prop: String,
23    pub val: String,
24}
25
26/// 必要な更新処理の範囲。
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum UpdateScope {
29    /// 変更なし。
30    Nothing,
31    /// 軽量再ペイントのみ。各要素の (element_id, prop, val) のリストを返す。
32    PaintOnly(Vec<(String, String, String)>),
33    /// 全再レイアウトが必要。
34    Relayout,
35}
36
37/// 保留中のスタイル更新群と構造ダーティフラグから、処理範囲を判定する。
38///
39/// `resolve_id` は `node_idx` からその要素の RenderElement 照合用 `element_id` を
40/// 解決するクロージャ。要素に id が無い場合は None を返し、安全に Relayout へフォールバックする。
41pub fn classify_updates<F>(
42    structural_dirty: bool,
43    updates: &[PendingStyleUpdate],
44    resolve_id: F,
45) -> UpdateScope
46where
47    F: Fn(usize) -> Option<String>,
48{
49    if structural_dirty {
50        return UpdateScope::Relayout;
51    }
52    if updates.is_empty() {
53        return UpdateScope::Nothing;
54    }
55
56    let mut paint_values = Vec::with_capacity(updates.len());
57    for u in updates {
58        // プロパティが PaintOnly か判定
59        match is_paint_only(&u.prop) {
60            Ok(true) => {}
61            _ => return UpdateScope::Relayout,
62        }
63        // 要素の element_id を解決(見つからなければ安全に Relayout へ)
64        let Some(eid) = resolve_id(u.node_idx) else {
65            return UpdateScope::Relayout;
66        };
67        if eid.is_empty() {
68            return UpdateScope::Relayout;
69        }
70        paint_values.push((eid, u.prop.clone(), u.val.clone()));
71    }
72
73    UpdateScope::PaintOnly(paint_values)
74}