Skip to main content

atmos/kernel/
shortcut_manager.rs

1#![allow(dead_code)]
2
3/// システム内で発生するアクションの列挙
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum InputAction {
6    Ignore,
7    TerminalInputChar(char),
8    TerminalHistoryUp,
9    TerminalHistoryDown,
10    SwitchTabRelative(isize),
11    NewTab,
12    CloseTab,
13    OpenFileDialog, // Alt+O: アプリのファイルオープンダイアログを呼び出す
14    SaveFile,       // Alt+S: アプリのファイル保存を呼び出す
15    TogglePowerMenu,
16    ToggleFocusSysmonTerminal,
17    BrowserScroll(i32),
18    DispatchToApp(u8, bool), // keycode, pressed
19    Custom(u32),
20    AppCommand(u32), // アプリケーション内専用の汎用コマンド
21}
22
23/// ショートカットが登録されるレイヤー。評価時はこの逆順(OS > App > User)も考慮するが、
24/// 基本はキー数優先とする。同数の場合は優先度の高いレイヤーが勝つ。
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
26pub enum ShortcutLayer {
27    // Ord を derive しているため、下に定義するほど大きな値になる。
28    // つまり OS < App < User の順で優先度を持たせるため、Userを最大値にする。
29    OS = 1,
30    App = 2,
31    User = 3,
32}
33
34/// 修飾キーの状態
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
36pub struct Modifiers {
37    pub ctrl: bool,
38    pub alt: bool,
39    pub shift: bool,
40}
41
42impl Modifiers {
43    pub fn new(ctrl: bool, alt: bool, shift: bool) -> Self {
44        Self { ctrl, alt, shift }
45    }
46}
47
48/// ひとつのショートカット定義
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct Shortcut {
51    pub keycode: u8,
52    pub modifiers: Modifiers,
53    pub layer: ShortcutLayer,
54    pub action: InputAction,
55    pub weight: usize, // 同時押しキー数 (メインキー1 + 修飾キーn)
56}
57
58impl Shortcut {
59    pub fn new(
60        keycode: u8,
61        modifiers: Modifiers,
62        layer: ShortcutLayer,
63        action: InputAction,
64    ) -> Self {
65        let mut mods = 0;
66        if modifiers.ctrl {
67            mods += 1;
68        }
69        if modifiers.alt {
70            mods += 1;
71        }
72        if modifiers.shift {
73            mods += 1;
74        }
75
76        Self {
77            keycode,
78            modifiers,
79            layer,
80            action,
81            weight: 1 + mods,
82        }
83    }
84}
85
86extern crate alloc;
87use alloc::vec::Vec;
88
89pub struct ShortcutManager {
90    shortcuts: Vec<Shortcut>,
91}
92
93impl Default for ShortcutManager {
94    fn default() -> Self {
95        Self::new()
96    }
97}
98
99impl ShortcutManager {
100    pub fn new() -> Self {
101        Self {
102            shortcuts: Vec::new(),
103        }
104    }
105
106    /// ショートカットを登録します
107    pub fn register(&mut self, shortcut: Shortcut) {
108        self.shortcuts.push(shortcut);
109    }
110
111    pub fn clear_app_shortcuts(&mut self) {
112        self.shortcuts.retain(|s| s.layer != ShortcutLayer::App);
113    }
114
115    pub fn clear_user_shortcuts(&mut self) {
116        self.shortcuts.retain(|s| s.layer != ShortcutLayer::User);
117    }
118
119    /// 現在のキーボード状態(押されたキーと修飾キー)を受け取り、
120    /// 重み(weight)順 -> レイヤー(layer)順に評価してアクションを返します
121    pub fn evaluate(&self, keycode: u8, modifiers: Modifiers) -> InputAction {
122        // 条件にマッチするものをフィルタリング
123        let mut matched = Vec::new();
124        for s in &self.shortcuts {
125            if s.keycode == keycode && s.modifiers == modifiers {
126                matched.push(s);
127            }
128        }
129
130        // マッチしたものがない場合は Ignore を返すか、未登録のデフォルトの動作にフォールバックさせる
131        // ここでは純粋にショートカットとして登録されているかのみを判定する。
132        if matched.is_empty() {
133            return InputAction::Ignore;
134        }
135
136        // weight (降順), layer (降順) の順でソート
137        matched.sort_by(|a, b| b.weight.cmp(&a.weight).then_with(|| b.layer.cmp(&a.layer)));
138
139        matched[0].action.clone()
140    }
141}
142