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
205 lines
8.1 KiB
Rust
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)
|
|
}
|
|
}
|
|
|