Skip to main content

atmos/kernel/
fn_bar.rs

1// fn_bar.rs - 画面最下部の常時表示ファンクションキー・ガイドバー
2//
3// 画面下端から約 BAR_HEIGHT px の帯を、左端から右端まで使って ESC / F1..F12 の
4// 機能を常時表示する。レイアウトは ESC||F1|F2|F3|F4||F5|F6|F7|F8||F9|F10|F11|F12 で、
5// 13 個の等幅セル(| は細セパレータ、|| はグループ間の太セパレータ)に分割する。
6// 押下中の Modifier に応じて各セルの案内テキストを切り替える。
7#![allow(dead_code)]
8
9use crate::kernel::draw::{Color, Screen};
10
11/// バーの高さ(px)
12pub const BAR_HEIGHT: u32 = 20;
13
14/// 現在押されている修飾キーの状態
15#[derive(Clone, Copy, PartialEq, Eq)]
16pub struct ModState {
17    pub ctrl: bool,
18    pub alt: bool,
19    pub shift: bool,
20}
21
22impl ModState {
23    pub const fn none() -> Self {
24        Self {
25            ctrl: false,
26            alt: false,
27            shift: false,
28        }
29    }
30}
31
32/// 1 セル分のキー定義
33struct KeyCell {
34    /// 囲み文字に出すキー名("ESC", "F1" ...)
35    label: &'static str,
36    /// グループ末尾(このセルの右に太セパレータを引く)
37    group_end: bool,
38}
39
40const CELLS: [KeyCell; 13] = [
41    KeyCell {
42        label: "ESC",
43        group_end: true,
44    },
45    KeyCell {
46        label: "F1",
47        group_end: false,
48    },
49    KeyCell {
50        label: "F2",
51        group_end: false,
52    },
53    KeyCell {
54        label: "F3",
55        group_end: false,
56    },
57    KeyCell {
58        label: "F4",
59        group_end: true,
60    },
61    KeyCell {
62        label: "F5",
63        group_end: false,
64    },
65    KeyCell {
66        label: "F6",
67        group_end: false,
68    },
69    KeyCell {
70        label: "F7",
71        group_end: false,
72    },
73    KeyCell {
74        label: "F8",
75        group_end: true,
76    },
77    KeyCell {
78        label: "F9",
79        group_end: false,
80    },
81    KeyCell {
82        label: "F10",
83        group_end: false,
84    },
85    KeyCell {
86        label: "F11",
87        group_end: false,
88    },
89    KeyCell {
90        label: "F12",
91        group_end: false,
92    },
93];
94
95/// 各セルの案内テキストを Modifier 状態に応じて返す。
96/// index 0 = ESC, 1..=12 = F1..F12
97fn action_text(index: usize, m: ModState) -> &'static str {
98    if m.alt {
99        // Alt 押下中: ワークスペース/ウィンドウ管理
100        match index {
101            0 => "Cancel",
102            1 => "Workspace 1",
103            2 => "Workspace 2",
104            3 => "Workspace 3",
105            4 => "Workspace 4",
106            5 => "Workspace 5",
107            6 => "Workspace 6",
108            7 => "Workspace 7",
109            8 => "Workspace 8",
110            9 => "Minimize",
111            10 => "Restore",
112            11 => "Maximize",
113            12 => "Fullscreen",
114            _ => "",
115        }
116    } else if m.ctrl {
117        // Ctrl 押下中(エディタ系 Emacs 互換などは将来拡張)
118        match index {
119            0 => "Cancel",
120            _ => "-",
121        }
122    } else {
123        // 修飾なし
124        match index {
125            0 => "Process List",
126            1 => "Terminal",
127            2 => "-",
128            3 => "-",
129            4 => "-",
130            5 => "-",
131            6 => "-",
132            7 => "-",
133            8 => "-",
134            9 => "-",
135            10 => "-",
136            11 => "-",
137            12 => "-",
138            _ => "",
139        }
140    }
141}
142
143/// バーを画面最下部に描画する。
144/// screen 全幅を使い、y = screen.height - BAR_HEIGHT から下に帯を引く。
145pub fn draw(screen: &Screen, m: ModState) {
146    let w = screen.width;
147    let h = screen.height;
148    if h < BAR_HEIGHT + 1 {
149        return;
150    }
151
152    let bar_y = h - BAR_HEIGHT;
153    let theme = crate::kernel::config::get_config().theme;
154
155    // 背景
156    let bg = Color(theme.fn_bar_bg);
157    let cell_bg = Color(theme.fn_bar_cell_bg);
158    let thin_sep = Color(theme.fn_bar_border);
159    let thick_sep = Color(theme.fn_bar_border);
160    let key_box = Color(theme.fn_bar_key_bg);
161    let key_fg = Color(theme.fn_bar_key_fg);
162    let desc_fg = Color(theme.fn_bar_text_fg);
163    let dim_fg = Color(theme.fn_bar_dim_fg);
164
165    screen.boxfill(0, bar_y, w, h, bg);
166    // 上端の境界線
167    screen.boxfill(0, bar_y, w, bar_y + 1, Color(theme.fn_bar_border));
168
169    // 13 等幅セル。端数は最後のセルで吸収する。
170    let n = CELLS.len() as u32; // 13
171    let cell_w = w / n;
172
173    for (i, cell) in CELLS.iter().enumerate() {
174        let cx = i as u32 * cell_w;
175        let cx_end = if i == CELLS.len() - 1 { w } else { cx + cell_w };
176        let inner_y = bar_y + 2;
177
178        // セル背景
179        screen.boxfill(cx + 1, inner_y, cx_end, h - 1, cell_bg);
180
181        // キー囲みラベル(左側)
182        let label = cell.label;
183        let label_w = crate::kernel::vector_font::get_vector_string_width(label, 11);
184        let box_w = label_w + 8;
185        let box_x0 = cx + 3;
186        let box_y0 = bar_y + 3;
187        let box_y1 = h - 3;
188        screen.boxfill(box_x0, box_y0, box_x0 + box_w, box_y1, key_box);
189        screen.draw_string_vector(box_x0 + 4, box_y0 + 1, label, key_fg, 11);
190
191        // 案内テキスト(キー囲みの右)
192        let desc = action_text(i, m);
193        let desc_x = box_x0 + box_w + 4;
194        if desc_x < cx_end {
195            let color = if desc == "-" { dim_fg } else { desc_fg };
196            screen.draw_string_vector(desc_x, box_y0 + 1, desc, color, 11);
197        }
198
199        // セパレータ(セル右端)
200        let sep_color = if cell.group_end { thick_sep } else { thin_sep };
201        let sep_w = if cell.group_end { 2 } else { 1 };
202        screen.boxfill(
203            cx_end.saturating_sub(sep_w),
204            inner_y,
205            cx_end,
206            h - 1,
207            sep_color,
208        );
209    }
210}