Skip to main content

framework_tool_tui/tui/component/
main.rs

1use std::sync::Arc;
2
3use ratatui::{
4    crossterm::event::{Event, KeyCode, KeyEventKind},
5    layout::{Constraint, Layout, Rect},
6    Frame,
7};
8
9use crate::{
10    framework::{fingerprint::Fingerprint, info::FrameworkInfo},
11    tui::{
12        component::{
13            brightness_panel::BrightnessPanelComponent, charge_panel::ChargePanelComponent,
14            pd_ports_panel::PdPortsPanelComponent, privacy_panel::PrivacyPanelComponent,
15            smbios_panel::SmbiosPanelComponent, AdjustableComponent, Component,
16        },
17        theme::Theme,
18    },
19};
20
21pub struct MainComponent {
22    privacy_panel: PrivacyPanelComponent,
23    smbios_panel: SmbiosPanelComponent,
24    pd_ports_panel: PdPortsPanelComponent,
25    adjustable_panels: Vec<Box<dyn AdjustableComponent>>,
26    selected_panel: Option<usize>,
27}
28
29impl MainComponent {
30    pub fn new(finterprint: Arc<Fingerprint>, info: &FrameworkInfo) -> Self {
31        let mut adjustable_panels: Vec<Box<dyn AdjustableComponent>> = Vec::new();
32        let charge_panel = Box::new(ChargePanelComponent::new());
33
34        adjustable_panels.push(charge_panel);
35
36        if Self::is_brightness_supported(info) {
37            let brightness_panel = Box::new(BrightnessPanelComponent::new(finterprint));
38
39            adjustable_panels.push(brightness_panel);
40        }
41
42        Self {
43            privacy_panel: PrivacyPanelComponent,
44            smbios_panel: SmbiosPanelComponent,
45            pd_ports_panel: PdPortsPanelComponent::new(),
46            adjustable_panels,
47            selected_panel: None,
48        }
49    }
50
51    fn switch_panels(&mut self) {
52        let len = self.adjustable_panels.len();
53
54        if let Some(selected_panel) = self.selected_panel {
55            if selected_panel < len - 1 {
56                let next = selected_panel + 1;
57
58                self.adjustable_panels[selected_panel].panel().toggle();
59                self.adjustable_panels[next].panel().toggle();
60
61                self.selected_panel = Some(next);
62            } else {
63                self.adjustable_panels[selected_panel].panel().toggle();
64                self.selected_panel = None;
65            }
66        } else {
67            self.adjustable_panels[0].panel().toggle();
68            self.selected_panel = Some(0);
69        }
70    }
71
72    fn render_privacy_and_smbios_panels(
73        &mut self,
74        frame: &mut Frame,
75        area: Rect,
76        theme: &Theme,
77        info: &FrameworkInfo,
78    ) {
79        let [privacy_panel_area, smbios_panel_area] =
80            Layout::horizontal([Constraint::Min(0), Constraint::Min(0)]).areas(area);
81
82        // Privacy panel
83        self.privacy_panel
84            .render(frame, privacy_panel_area, theme, info);
85
86        // SMBIOS panel
87        self.smbios_panel
88            .render(frame, smbios_panel_area, theme, info);
89    }
90
91    fn is_brightness_supported(info: &FrameworkInfo) -> bool {
92        // NOTE: modifiying FP and KB brightness is not supported on FW 12
93        info.platform != Some(framework_lib::smbios::Platform::Framework12IntelGen13)
94    }
95}
96
97impl Component for MainComponent {
98    fn handle_input(&mut self, event: Event) -> Option<crate::app::AppEvent> {
99        if let Event::Key(key) = &event {
100            if key.kind == KeyEventKind::Press && key.code == KeyCode::Tab {
101                self.switch_panels();
102            }
103        }
104
105        self.adjustable_panels
106            .iter_mut()
107            .find_map(|panel| panel.handle_input(event.clone()))
108    }
109
110    fn render(&mut self, frame: &mut Frame, area: Rect, theme: &Theme, info: &FrameworkInfo) {
111        let [top_area, pd_ports_panel_area] =
112            Layout::vertical([Constraint::Max(15), Constraint::Min(0)]).areas(area);
113        let [charge_panel_area, top_right_area] =
114            Layout::horizontal([Constraint::Min(0), Constraint::Min(0)]).areas(top_area);
115
116        // Charge panel
117        self.adjustable_panels[0].render(frame, charge_panel_area, theme, info);
118
119        // Show brightness panel only on supported platforms
120        if Self::is_brightness_supported(info) {
121            let [brightness_panel_area, privacy_and_smbios_panels_area] =
122                Layout::vertical([Constraint::Min(7), Constraint::Min(7)]).areas(top_right_area);
123
124            // Brightness panel (top of right_area)
125            self.adjustable_panels[1].render(frame, brightness_panel_area, theme, info);
126
127            // Privacy and SMBIOS panels
128            self.render_privacy_and_smbios_panels(
129                frame,
130                privacy_and_smbios_panels_area,
131                theme,
132                info,
133            );
134        } else {
135            // Privacy and SMBIOS panels
136            self.render_privacy_and_smbios_panels(frame, top_right_area, theme, info);
137        }
138
139        // PD Ports panel (bottom of right_area)
140        self.pd_ports_panel
141            .render(frame, pd_ports_panel_area, theme, info);
142    }
143}