Files
ming-wm/src/essential/start_menu.rs
stjet 1a5eba7191 major usability improvements
Terminal now modal, supports line buffering, stdin, because it uses pty (and threading). Also supports paste, again (?). Audio player queueing no longer blocks, supports appending. Now can change window size. Halfscreen window shortcut improved. Eliminate dirs dep, deconstruct audiotags dep, better feature flags. Remove .alpha files because they are built. Use /home/jondough for default dirs if possible. Themes config. Fix for rotated touchscreen. Write more docs
2025-02-28 03:03:09 +00:00

205 lines
8.1 KiB
Rust

use std::vec;
use std::vec::Vec;
use std::boxed::Box;
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo;
use crate::components::Component;
use crate::components::highlight_button::HighlightButton;
static CATEGORIES: [&'static str; 9] = ["About", "Utils", "Games", "Editing", "Files", "Internet", "Misc", "Help", "Logout"];
#[derive(Clone)]
enum StartMenuMessage {
CategoryClick(&'static str),
WindowClick(&'static str),
Back,
ChangeAcknowledge,
}
#[derive(Default)]
pub struct StartMenu {
dimensions: Dimensions,
components: Vec<Box<dyn Component<StartMenuMessage> + Send>>,
current_focus: String,
y_each: usize,
}
impl WindowLike for StartMenu {
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
match message {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
self.y_each = (self.dimensions[1] - 1) / CATEGORIES.len();
self.add_category_components();
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
//up and down
if key_press.key == 'k' || key_press.key == 'j' {
let old_focus_index = self.get_focus_index().unwrap();
self.components[old_focus_index].handle_message(WindowMessage::Unfocus);
let current_focus_index = if key_press.key == 'j' {
if old_focus_index + 1 == self.components.len() {
0
} else {
old_focus_index + 1
}
} else {
if old_focus_index == 0 {
self.components.len() - 1
} else {
old_focus_index - 1
}
};
self.current_focus = self.components[current_focus_index].name().to_string();
self.components[current_focus_index].handle_message(WindowMessage::Focus);
WindowMessageResponse::JustRedraw
} else if key_press.key == '𐘂' { //the enter key
let focus_index = self.get_focus_index();
if let Some(focus_index) = focus_index {
let r = self.components[focus_index].handle_message(WindowMessage::FocusClick);
self.handle_start_menu_message(r)
} else {
WindowMessageResponse::DoNothing
}
} else {
let current_focus_index = self.get_focus_index().unwrap();
if key_press.key.is_lowercase() {
//look forwards to see category/window that starts with that char
if let Some(n_index) = self.components[current_focus_index..].iter().position(|c| c.name().chars().next().unwrap_or('𐘂').to_lowercase().next().unwrap() == key_press.key) {
//now old focus, not current focus
self.components[current_focus_index].handle_message(WindowMessage::Unfocus);
self.current_focus = self.components[current_focus_index + n_index].name().to_string();
self.components[current_focus_index + n_index].handle_message(WindowMessage::Focus);
WindowMessageResponse::JustRedraw
} else {
WindowMessageResponse::DoNothing
}
} else {
//look backwards to see category/window that starts with that char
if let Some(n_index) = self.components[..current_focus_index].iter().rev().position(|c| c.name().chars().next().unwrap_or('𐘂').to_uppercase().next().unwrap() == key_press.key) {
//now old focus, not current focus
self.components[current_focus_index].handle_message(WindowMessage::Unfocus);
self.current_focus = self.components[current_focus_index - n_index - 1].name().to_string();
self.components[current_focus_index - n_index - 1].handle_message(WindowMessage::Focus);
WindowMessageResponse::JustRedraw
} else {
WindowMessageResponse::DoNothing
}
}
}
},
_ => WindowMessageResponse::DoNothing,
}
}
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
let mut instructions = vec![
//top thin border
DrawInstructions::Rect([0, 0], [self.dimensions[0], 1], theme_info.border_left_top),
//right thin border
DrawInstructions::Rect([self.dimensions[0] - 1, 0], [1, self.dimensions[1]], theme_info.border_right_bottom),
//background
DrawInstructions::Rect([0, 1], [self.dimensions[0] - 1, self.dimensions[1] - 1], theme_info.background),
//mingde logo
DrawInstructions::Bmp([2, 2], "./bmps/mingde.bmp".to_string(), false),
//I truly don't know why, it should be - 44 but - 30 seems to work better :shrug:
DrawInstructions::Gradient([2, 42], [40, self.dimensions[1] - 30], [255, 201, 14], [225, 219, 77], 15),
];
for component in &self.components {
instructions.extend(component.draw(theme_info));
}
instructions
}
//properties
fn subtype(&self) -> WindowLikeType {
WindowLikeType::StartMenu
}
fn ideal_dimensions(&self, _dimensions: Dimensions) -> Dimensions {
[175, 250]
}
}
impl StartMenu {
pub fn new() -> Self {
Default::default()
}
fn handle_start_menu_message(&mut self, message: Option<StartMenuMessage>) -> WindowMessageResponse {
if let Some(message) = message {
match message {
StartMenuMessage::CategoryClick(name) => {
if name == "Logout" {
WindowMessageResponse::Request(WindowManagerRequest::Lock)
} else if name == "About" || name == "Help" {
//todo above: also do the same for Help
WindowMessageResponse::Request(WindowManagerRequest::OpenWindow(name.to_string()))
} else {
self.current_focus = "Back".to_string();
self.components = vec![
Box::new(HighlightButton::new(
"Back".to_string(), [42, 1], [self.dimensions[0] - 42 - 1, self.y_each], "Back".to_string(), StartMenuMessage::Back, StartMenuMessage::ChangeAcknowledge, true
))
];
//add window buttons
let mut to_add: Vec<&str> = Vec::new();
if name == "Games" {
to_add.extend(["Minesweeper", "Reversi"]);
} else if name == "Editing" {
to_add.push("Malvim");
} else if name == "Utils" {
to_add.push("Terminal");
} else if name == "Files" {
to_add.extend(["File Explorer", "Audio Player"]);
}
//
for a in 0..to_add.len() {
let w_name = to_add[a];
self.components.push(Box::new(HighlightButton::new(
w_name.to_string(), [42, (a + 1) * self.y_each], [self.dimensions[0] - 42 - 1, self.y_each], w_name.to_string(), StartMenuMessage::WindowClick(w_name), StartMenuMessage::ChangeAcknowledge, false
)));
}
WindowMessageResponse::JustRedraw
}
},
StartMenuMessage::WindowClick(name) => {
//open the selected window
WindowMessageResponse::Request(WindowManagerRequest::OpenWindow(name.to_string()))
},
StartMenuMessage::Back => {
self.add_category_components();
WindowMessageResponse::JustRedraw
},
StartMenuMessage::ChangeAcknowledge => {
//
WindowMessageResponse::JustRedraw
},
}
} else {
//maybe should be JustRedraw?
WindowMessageResponse::DoNothing
}
}
pub fn add_category_components(&mut self) {
self.current_focus = "About".to_string();
self.components = Vec::new();
for c in 0..CATEGORIES.len() {
let name = CATEGORIES[c];
self.components.push(Box::new(HighlightButton::new(
name.to_string(), [42, self.y_each * c + 1], [self.dimensions[0] - 42 - 1, self.y_each], name.to_string(), StartMenuMessage::CategoryClick(name), StartMenuMessage::ChangeAcknowledge, c == 0
)));
}
}
pub fn get_focus_index(&self) -> Option<usize> {
self.components.iter().filter(|c| c.focusable()).position(|c| c.name() == &self.current_focus)
}
}