Implement complete keyboard navigation and UI enhancement

Phase 1 - Panel Navigation:
- Add PanelType enum and panel focus state management
- Implement Shift+Tab cycling between panels (System → Services → Backup → Network)
- Add visual focus indicators with blue borders for focused panels
- Preserve existing Tab behavior for host switching

Phase 2 - Dynamic Statusbar:
- Add bottom statusbar with context-aware shortcuts
- Display different shortcuts based on focused panel
- Global shortcuts: Tab, Shift+Tab, Up/Down arrows, Q
- Panel-specific shortcuts: R (Rebuild), Space/R (Services), B (Backup), N (Network)

Phase 3 - Scrolling Support:
- Add scroll state management per host and panel type
- Implement Up/Down arrow key scrolling within focused panels
- Smart scrolling that activates only when content exceeds panel height
- Scroll bounds checking to prevent over-scrolling

Complete keyboard navigation experience with visual feedback and contextual help.
This commit is contained in:
2025-10-23 20:34:45 +02:00
parent 51375e8020
commit 8cb5650fbb
5 changed files with 278 additions and 23 deletions

View File

@@ -399,6 +399,13 @@ impl Widget for SystemWidget {
}
fn render(&mut self, frame: &mut Frame, area: Rect) {
self.render_with_scroll(frame, area, 0);
}
}
impl SystemWidget {
/// Render with scroll offset support
pub fn render_with_scroll(&mut self, frame: &mut Frame, area: Rect, scroll_offset: usize) {
let mut lines = Vec::new();
// NixOS section
@@ -509,7 +516,28 @@ impl Widget for SystemWidget {
}
}
let paragraph = Paragraph::new(Text::from(lines));
frame.render_widget(paragraph, area);
// Apply scroll offset
let total_lines = lines.len();
let available_height = area.height as usize;
if total_lines > available_height {
// Content is larger than area, apply scrolling
let max_scroll = total_lines.saturating_sub(available_height);
let effective_scroll = scroll_offset.min(max_scroll);
// Take only the visible portion after scrolling
let visible_lines: Vec<Line> = lines
.into_iter()
.skip(effective_scroll)
.take(available_height)
.collect();
let paragraph = Paragraph::new(Text::from(visible_lines));
frame.render_widget(paragraph, area);
} else {
// All content fits, render normally
let paragraph = Paragraph::new(Text::from(lines));
frame.render_widget(paragraph, area);
}
}
}