Skip to main content

atmos/os_lib/web_engine/
text_align.rs

1//! 折り返した段落の行揃え(`text-align`)の計算(純粋ロジック)。
2//!
3//! 【2026-10-04 修正】sugi-lab.net/smarthome.htm の機能カード
4//! (`.feature-card{text-align:center}`)で、1 行の見出しは中央に来るのに、
5//! 複数行に折り返す本文の段落が左揃えで描かれた。
6//! 1 行の文字はレイアウト木(`layout/tree.rs` の text-align 後処理)が行内の箱ごと
7//! ずらすので揃うが、折り返した文字の箱は全幅なのでずらす量が 0 になる。
8//! 描画用の行を作る `web_engine/layout.rs` は各行を箱の左端から置いており、
9//! 行揃えを一切見ていなかった。ここで行ごとの開始位置を決める。
10//!
11//! 1 行の文字は箱の幅 = 行の幅なので、この計算の結果は 0(字下げのみ)になり、
12//! レイアウト木のずらしと二重には掛からない。
13//!
14//! 試験: `tests/src/test_text_align.rs`
15
16/// 行の先頭の x を、行ボックスの左端からのオフセットで返す。
17///
18/// - `align`: 計算済みの `text-align`(継承済みの値。未指定は空文字)
19/// - `rtl`: `direction: rtl` か
20/// - `avail_w`: 行ボックスの幅(折り返しに使った幅)
21/// - `indent`: この行の字下げ(1 行目の `text-indent`。それ以外は 0)
22/// - `line_w`: この行の文字の幅
23///
24/// 揃えた結果の余りが負(行がはみ出す)なら先頭揃え(CSS Text §6.3)。
25/// 両端揃え(`justify`)は単語間を広げる処理が無いので先頭揃えとして扱う。
26pub fn line_start_offset(align: &str, rtl: bool, avail_w: i32, indent: i32, line_w: i32) -> i32 {
27    let a = align.trim().to_ascii_lowercase();
28    #[derive(PartialEq)]
29    enum Side {
30        Left,
31        Center,
32        Right,
33    }
34    let start = if rtl { Side::Right } else { Side::Left };
35    let end = if rtl { Side::Left } else { Side::Right };
36    let side = match a.as_str() {
37        "left" => Side::Left,
38        "right" => Side::Right,
39        "center" | "-webkit-center" | "-moz-center" => Side::Center,
40        "end" => end,
41        // "" / "start" / "justify" / "match-parent" / 不明な値
42        _ => start,
43    };
44    let space = avail_w - indent - line_w.max(0);
45    if space <= 0 {
46        return indent;
47    }
48    match side {
49        Side::Left => indent,
50        Side::Center => indent + space / 2,
51        Side::Right => indent + space,
52    }
53}