v1.0.3: mostly malvim improvements

malvim: search, d$, %. fix w and m in mono font. apply some clippy lint suggestions
This commit is contained in:
stjet
2025-04-17 00:53:31 +00:00
parent 11af21ee6d
commit d32b82a2bb
19 changed files with 233 additions and 93 deletions

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "ming-wm" name = "ming-wm"
version = "1.0.2" version = "1.0.3"
repository = "https://github.com/stjet/ming-wm" repository = "https://github.com/stjet/ming-wm"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
edition = "2021" edition = "2021"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 470 B

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 374 B

View File

@@ -15,8 +15,7 @@ fn font_chars_to_alphas(dir: &str) {
if file_name.len() < 2 { if file_name.len() < 2 {
continue; continue;
} }
if file_name[1] == "bmp" { if file_name[1] == "bmp" && !path.is_dir() {
if !path.is_dir() {
let mut ch: Vec<Vec<String>> = Vec::new(); let mut ch: Vec<Vec<String>> = Vec::new();
let b = BMP::new_from_file(&path.clone().into_os_string().into_string().unwrap()).unwrap(); let b = BMP::new_from_file(&path.clone().into_os_string().into_string().unwrap()).unwrap();
let dib_header = b.get_dib_header().unwrap(); let dib_header = b.get_dib_header().unwrap();
@@ -45,7 +44,6 @@ fn font_chars_to_alphas(dir: &str) {
} }
} }
} }
}
fn main() { fn main() {
//hash + "salt" password and add to build //hash + "salt" password and add to build

View File

@@ -16,5 +16,9 @@
- Alt+J: Move window to bottom edge - Alt+J: Move window to bottom edge
- Alt+K: Move window to top edge - Alt+K: Move window to top edge
- Alt+L: Move window to right edge - Alt+L: Move window to right edge
- Alt+n: Expand window width
- Alt+m: Expand window height
- Alt+N: Shrink window width
- Alt+M: Shrink window height
- Alt+1, Alt+2, ..., Alt+[n], ..., Alt+9: Switch to workspace [n] - Alt+1, Alt+2, ..., Alt+[n], ..., Alt+9: Switch to workspace [n]
- Alt+shift+1, Alt+shift+2, ..., Alt+shift+[n], ..., Alt+shift+9: Move window to workspace [n] - Alt+shift+1, Alt+shift+2, ..., Alt+shift+[n], ..., Alt+shift+9: Move window to workspace [n]

View File

@@ -12,6 +12,7 @@ It is probably best to read a Vim tutorial for the basics. All supportd keystrok
- `t[abe] <file>`, `[tab]n`, `[tab]p` - `t[abe] <file>`, `[tab]n`, `[tab]p`
- `q[uit]` - `q[uit]`
- `w[rite]` - `w[rite]`
- `/<query>`
Tab completion is supported for the `<file>` argument. Tab completion is supported for the `<file>` argument.
@@ -23,6 +24,7 @@ Tab completion is supported for the `<file>` argument.
- `r` - `r`
- `dd` - `dd`
- `dw` - `dw`
- `d$`
- `G` - `G`
- `gg` - `gg`
- `<number>gg` - `<number>gg`
@@ -32,6 +34,7 @@ Tab completion is supported for the `<file>` argument.
- `h` (or left arrow), `j` (or down arrow), `k` (or up arrow), `l` (or right arrow) - `h` (or left arrow), `j` (or down arrow), `k` (or up arrow), `l` (or right arrow)
- `<num>h`, `<num>j` (or down arrow), `<num>k` (or up arrow), `<num>l` - `<num>h`, `<num>j` (or down arrow), `<num>k` (or up arrow), `<num>l`
- `0`, `^`, `$` - `0`, `^`, `$`
- `%`
### Malvim Specific ### Malvim Specific

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "ming-wm-lib" name = "ming-wm-lib"
version = "0.1.5" version = "0.1.6"
repository = "https://github.com/stjet/ming-wm" repository = "https://github.com/stjet/ming-wm"
description = "library for building windows for ming-wm in rust" description = "library for building windows for ming-wm in rust"
readme = "README.md" readme = "README.md"

View File

@@ -58,22 +58,22 @@ pub fn listen(mut window_like: impl WindowLike) {
let arg = &parts.collect::<Vec<&str>>().join(" "); let arg = &parts.collect::<Vec<&str>>().join(" ");
let output = match method { let output = match method {
"handle_message" => { "handle_message" => {
format!("{}", &window_like.handle_message(WindowMessage::deserialize(arg).unwrap()).serialize()) window_like.handle_message(WindowMessage::deserialize(arg).unwrap()).serialize().to_string()
}, },
"draw" => { "draw" => {
format!("{}", &window_like.draw(&ThemeInfo::deserialize(arg).unwrap()).serialize().replace("\n", "")) window_like.draw(&ThemeInfo::deserialize(arg).unwrap()).serialize().replace("\n", "").to_string()
}, },
"title" => { "title" => {
format!("{}", window_like.title()) window_like.title().to_string()
}, },
"resizable" => { "resizable" => {
format!("{}", window_like.resizable()) window_like.resizable().to_string()
}, },
"subtype" => { "subtype" => {
format!("{}", &window_like.subtype().serialize()) window_like.subtype().serialize().to_string()
}, },
"ideal_dimensions" => { "ideal_dimensions" => {
format!("{}", &window_like.ideal_dimensions(Dimensions::deserialize(arg).unwrap()).serialize()) window_like.ideal_dimensions(Dimensions::deserialize(arg).unwrap()).serialize().to_string()
}, },
_ => String::new(), _ => String::new(),
}; };

View File

@@ -39,10 +39,10 @@ fn option_to_string<T: Display>(option: &Option<T>) -> String {
fn get_color(serialized: &str) -> Result<[u8; 3], ()> { fn get_color(serialized: &str) -> Result<[u8; 3], ()> {
let rgb = serialized.split("\x1F"); let rgb = serialized.split("\x1F");
let mut color = [0; 3]; let mut color = [0; 3];
let mut c_i = 0;
//won't return error if rgb is 0, 1, or 2 elements. //won't return error if rgb is 0, 1, or 2 elements.
//I guess that's okay, since it doesn't panic either //I guess that's okay, since it doesn't panic either
for c in rgb { //c_i is the loop counter. enumerate(), you are awesome
for (c_i, c) in rgb.enumerate() {
if c_i == 3 { if c_i == 3 {
return Err(()); return Err(());
} }
@@ -51,7 +51,6 @@ fn get_color(serialized: &str) -> Result<[u8; 3], ()> {
} else { } else {
return Err(()); return Err(());
} }
c_i += 1;
} }
Ok(color) Ok(color)
} }
@@ -68,7 +67,7 @@ fn get_two_array(serialized: &str) -> Result<[usize; 2], ()> {
} }
return Err(()); return Err(());
} }
return Ok(a); Ok(a)
} }
pub trait Serializable { pub trait Serializable {
@@ -87,9 +86,8 @@ impl Serializable for ThemeInfo {
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized }; let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
let mut theme_info: ThemeInfo = Default::default(); let mut theme_info: ThemeInfo = Default::default();
let arrays = serialized.split(":"); let arrays = serialized.split(":");
let mut a_i = 0;
//won't error or panic if less than 9... rest will just be black by default I guess //won't error or panic if less than 9... rest will just be black by default I guess
for a in arrays { for (a_i, a) in arrays.enumerate() {
if a_i == 9 { if a_i == 9 {
return Err(()); return Err(());
} }
@@ -127,7 +125,6 @@ impl Serializable for ThemeInfo {
if a_i == 8 { if a_i == 8 {
return Ok(theme_info); return Ok(theme_info);
} }
a_i += 1;
} }
Err(()) Err(())
} }
@@ -220,7 +217,7 @@ impl Serializable for DrawInstructions {
match self { match self {
//use \x1E (record separator) because it won't be in strings. it better fucking not be at least //use \x1E (record separator) because it won't be in strings. it better fucking not be at least
DrawInstructions::Rect(p, d, c) => format!("Rect/{}\x1E{}\x1E{}", array_to_string(p), array_to_string(d), array_to_string(c)), DrawInstructions::Rect(p, d, c) => format!("Rect/{}\x1E{}\x1E{}", array_to_string(p), array_to_string(d), array_to_string(c)),
DrawInstructions::Text(p, vs, s, c1, c2, ou1, ou2) => format!("Text/{}\x1E{}\x1E{}\x1E{}\x1E{}\x1E{}\x1E{}", array_to_string(p), array_to_string(&vs), s, array_to_string(c1), array_to_string(c2), option_to_string(ou1), option_to_string(ou2)), DrawInstructions::Text(p, vs, s, c1, c2, ou1, ou2) => format!("Text/{}\x1E{}\x1E{}\x1E{}\x1E{}\x1E{}\x1E{}", array_to_string(p), array_to_string(vs), s, array_to_string(c1), array_to_string(c2), option_to_string(ou1), option_to_string(ou2)),
DrawInstructions::Gradient(p, d, c1, c2, u) => format!("Gradient/{}\x1E{}\x1E{}\x1E{}\x1E{}", array_to_string(p), array_to_string(d), array_to_string(c1), array_to_string(c2), u), DrawInstructions::Gradient(p, d, c1, c2, u) => format!("Gradient/{}\x1E{}\x1E{}\x1E{}\x1E{}", array_to_string(p), array_to_string(d), array_to_string(c1), array_to_string(c2), u),
DrawInstructions::Bmp(p, s, b) => format!("Bmp/{}\x1E{}\x1E{}", array_to_string(p), s, b), DrawInstructions::Bmp(p, s, b) => format!("Bmp/{}\x1E{}\x1E{}", array_to_string(p), s, b),
DrawInstructions::Circle(p, u, c) => format!("Circle/{}\x1E{}\x1E{}", array_to_string(p), u, array_to_string(c)), DrawInstructions::Circle(p, u, c) => format!("Circle/{}\x1E{}\x1E{}", array_to_string(p), u, array_to_string(c)),
@@ -674,8 +671,7 @@ impl Serializable for WindowMessage {
} }
let mut w_tuple: (usize, String) = Default::default(); let mut w_tuple: (usize, String) = Default::default();
let mut w_vec = Vec::new(); let mut w_vec = Vec::new();
let mut i = 0; for (i, a) in arg2.unwrap().split("\x1F").enumerate() {
for a in arg2.unwrap().split("\x1F") {
if i % 2 == 0 { if i % 2 == 0 {
if let Ok(n) = a.parse() { if let Ok(n) = a.parse() {
w_tuple.0 = n; w_tuple.0 = n;
@@ -684,7 +680,6 @@ impl Serializable for WindowMessage {
w_tuple.1 = a.to_string(); w_tuple.1 = a.to_string();
w_vec.push(w_tuple.clone()); w_vec.push(w_tuple.clone());
} }
i += 1;
} }
let arg2 = parts2.next(); let arg2 = parts2.next();
if arg2.is_none() { if arg2.is_none() {

View File

@@ -104,6 +104,6 @@ pub fn get_theme_info(theme: &Themes) -> Option<ThemeInfo> {
return Some(pair.1); return Some(pair.1);
} }
} }
return None; None
} }

View File

@@ -11,6 +11,7 @@ pub trait Substring {
fn substring(&self, start: usize, end: usize) -> &str; fn substring(&self, start: usize, end: usize) -> &str;
fn remove(&self, index: usize, len: usize) -> String; fn remove(&self, index: usize, len: usize) -> String;
fn remove_last(&self) -> String; fn remove_last(&self) -> String;
fn find_substring(&self, substr: &str) -> Option<usize>;
} }
impl Substring for String { impl Substring for String {
@@ -29,6 +30,14 @@ impl Substring for String {
byte_end += char_length; byte_end += char_length;
} }
&self[byte_start..byte_end] &self[byte_start..byte_end]
/*
let mut result = String::new();
let mut chars = self.chars().skip(start);
for _i in 0..(end - start) {
result += &chars.next().unwrap().to_string();
}
result
*/
} }
fn remove(&self, index: usize, len: usize) -> String { fn remove(&self, index: usize, len: usize) -> String {
@@ -38,13 +47,26 @@ impl Substring for String {
fn remove_last(&self) -> String { fn remove_last(&self) -> String {
self.substring(0, self.chars().count() - 1).to_string() self.substring(0, self.chars().count() - 1).to_string()
} }
fn find_substring(&self, substr: &str) -> Option<usize> {
//slightly inefficient
let substr_len = substr.chars().count();
let self_len = self.chars().count();
if substr_len <= self_len {
for start in 0..=(self_len - substr_len) {
if self.substring(start, start + substr_len) == substr {
return Some(start);
}
}
}
None
}
} }
//the tuple is first, line #, actual line //the tuple is first, line #, actual line
pub fn calc_actual_lines<'a>(lines: impl Iterator<Item = &'a String>, max_chars_per_line: usize, one_extra: bool) -> Vec<(bool, usize, String)> { pub fn calc_actual_lines<'a>(lines: impl Iterator<Item = &'a String>, max_chars_per_line: usize, one_extra: bool) -> Vec<(bool, usize, String)> {
let mut actual_lines = Vec::new(); let mut actual_lines = Vec::new();
let mut line_num = 0; for (line_num, real_line) in lines.enumerate() {
for real_line in lines {
let mut line = real_line.to_string() + if one_extra { " " } else { "" }; let mut line = real_line.to_string() + if one_extra { " " } else { "" };
let mut first = true; let mut first = true;
loop { loop {
@@ -62,7 +84,6 @@ pub fn calc_actual_lines<'a>(lines: impl Iterator<Item = &'a String>, max_chars_
} }
first = false; first = false;
} }
line_num += 1;
} }
actual_lines actual_lines
} }
@@ -132,7 +153,7 @@ pub fn hex_to_u8(c1: char, c2: char) -> u8 {
} }
pub fn is_hex(c: char) -> bool { pub fn is_hex(c: char) -> bool {
HEX_CHARS.iter().position(|hc| hc == &c).is_some() HEX_CHARS.iter().any(|hc| hc == &c)
} }
pub fn point_inside(point: Point, top_left: Point, size: Dimensions) -> bool { pub fn point_inside(point: Point, top_left: Point, size: Dimensions) -> bool {
@@ -193,4 +214,3 @@ pub fn path_autocomplete(current_path: &str, partial_path: &str) -> Option<Strin
None None
} }
} }

View File

@@ -61,8 +61,7 @@ impl WindowLike for FileExplorer {
WindowMessageResponse::JustRedraw WindowMessageResponse::JustRedraw
}, },
WindowMessage::KeyPress(key_press) => { WindowMessage::KeyPress(key_press) => {
self.state = State::List; if key_press.is_enter() && self.state == State::List {
if key_press.is_enter() {
if self.current_dir_contents.len() > 0 { if self.current_dir_contents.len() > 0 {
let selected_entry = &self.current_dir_contents[self.position]; let selected_entry = &self.current_dir_contents[self.position];
if !selected_entry.is_file { if !selected_entry.is_file {
@@ -75,6 +74,7 @@ impl WindowLike for FileExplorer {
} }
WindowMessageResponse::DoNothing WindowMessageResponse::DoNothing
} else if key_press.key == 'j' || key_press.is_down_arrow() || key_press.key == 'k' || key_press.is_up_arrow() { } else if key_press.key == 'j' || key_press.is_down_arrow() || key_press.key == 'k' || key_press.is_up_arrow() {
self.state = State::List;
if key_press.key == 'j' || key_press.is_down_arrow() { if key_press.key == 'j' || key_press.is_down_arrow() {
//down //down
if self.position == self.current_dir_contents.len() - 1 { if self.position == self.current_dir_contents.len() - 1 {
@@ -103,13 +103,22 @@ impl WindowLike for FileExplorer {
}; };
WindowMessageResponse::JustRedraw WindowMessageResponse::JustRedraw
} else if key_press.key == 'i' { } else if key_press.key == 'i' {
if self.state == State::Info {
self.state = State::List;
} else {
self.state = State::Info; self.state = State::Info;
let selected_entry = &self.current_dir_contents[self.position]; let selected_entry = &self.current_dir_contents[self.position];
self.metadata = Some(metadata(&selected_entry.path).unwrap()); self.metadata = Some(metadata(&selected_entry.path).unwrap());
}
WindowMessageResponse::JustRedraw
} else {
if self.state == State::Info {
self.state = State::List;
WindowMessageResponse::JustRedraw WindowMessageResponse::JustRedraw
} else { } else {
WindowMessageResponse::DoNothing WindowMessageResponse::DoNothing
} }
}
}, },
_ => WindowMessageResponse::DoNothing, _ => WindowMessageResponse::DoNothing,
} }

View File

@@ -116,10 +116,10 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
let mut y: Option<usize> = None; let mut y: Option<usize> = None;
for line in reader.lines() { for line in reader.lines() {
let line = line.unwrap(); let line = line.unwrap();
if line.contains(&"ABS_X), value ") || line.contains(&"ABS_Y), value ") { if line.contains("ABS_X), value ") || line.contains("ABS_Y), value ") {
let value: Vec<_> = line.split("), value ").collect(); let value: Vec<_> = line.split("), value ").collect();
let value = value[value.len() - 1].parse::<usize>().unwrap(); let value = value[value.len() - 1].parse::<usize>().unwrap();
if line.contains(&"ABS_X") { if line.contains("ABS_X") {
x = Some(value); x = Some(value);
} else { } else {
y = Some(value); y = Some(value);

View File

@@ -2,6 +2,7 @@ use std::vec::Vec;
use std::vec; use std::vec;
use std::fmt; use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use std::collections::HashMap;
use std::fs::{ read_to_string, write }; use std::fs::{ read_to_string, write };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType }; use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType };
@@ -10,7 +11,7 @@ use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType }; use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::utils::{ calc_actual_lines, calc_new_cursor_pos, Substring }; use ming_wm_lib::utils::{ calc_actual_lines, calc_new_cursor_pos, Substring };
use ming_wm_lib::dirs::home; use ming_wm_lib::dirs::home;
use ming_wm_lib::utils::path_autocomplete; use ming_wm_lib::utils::{ get_rest_of_split, path_autocomplete };
use ming_wm_lib::ipc::listen; use ming_wm_lib::ipc::listen;
const MONO_WIDTH: u8 = 10; const MONO_WIDTH: u8 = 10;
@@ -175,18 +176,23 @@ impl WindowLike for Malvim {
} }
let new_length = current_file.content[current_file.line_pos].chars().count(); let new_length = current_file.content[current_file.line_pos].chars().count();
current_file.cursor_pos = calc_new_cursor_pos(current_file.cursor_pos, new_length); current_file.cursor_pos = calc_new_cursor_pos(current_file.cursor_pos, new_length);
} else if key_press.key == 'w' { } else if key_press.key == 'w' || key_press.key == '$' {
let line = &current_file.content[current_file.line_pos]; let line = &current_file.content[current_file.line_pos];
let line_len = line.chars().count(); let line_len = line.chars().count();
if line_len > 0 && current_file.cursor_pos < line_len { if line_len > 0 && current_file.cursor_pos < line_len {
//offset until space or eol //offset until space or eol
let mut line_chars = line.chars().skip(current_file.cursor_pos).peekable(); let mut line_chars = line.chars().skip(current_file.cursor_pos).peekable();
let current_char = line_chars.peek().unwrap().clone(); //deref is Copy
let offset = line_chars.position(|c| if current_char == ' ' { let current_char = *line_chars.peek().unwrap();
let offset = if key_press.key == 'w' {
line_chars.position(|c| if current_char == ' ' {
c != ' ' c != ' '
} else { } else {
c == ' ' c == ' '
}).unwrap_or(line_len - current_file.cursor_pos); }).unwrap_or(line_len - current_file.cursor_pos)
} else {
line_chars.count()
};
current_file.content[current_file.line_pos] = line.remove(current_file.cursor_pos, offset); current_file.content[current_file.line_pos] = line.remove(current_file.cursor_pos, offset);
let new_length = current_file.content[current_file.line_pos].chars().count(); let new_length = current_file.content[current_file.line_pos].chars().count();
current_file.cursor_pos = calc_new_cursor_pos(current_file.cursor_pos, new_length); current_file.cursor_pos = calc_new_cursor_pos(current_file.cursor_pos, new_length);
@@ -240,6 +246,8 @@ impl WindowLike for Malvim {
if current_length == 1 { if current_length == 1 {
current_file.cursor_pos = 0; current_file.cursor_pos = 0;
} }
} else {
changed = false;
} }
} else if key_press.key == 'h' || key_press.is_left_arrow() { } else if key_press.key == 'h' || key_press.is_left_arrow() {
current_file.cursor_pos = current_file.cursor_pos.checked_sub(self.maybe_num.unwrap_or(1)).unwrap_or(0); current_file.cursor_pos = current_file.cursor_pos.checked_sub(self.maybe_num.unwrap_or(1)).unwrap_or(0);
@@ -295,7 +303,88 @@ impl WindowLike for Malvim {
} else if key_press.key == 'F' { } else if key_press.key == 'F' {
self.state = State::BackFind; self.state = State::BackFind;
changed = false; changed = false;
} else if key_press.key.is_digit(10) { } else if key_press.key == '%' {
let current_l = &current_file.content[current_file.line_pos];
if current_file.cursor_pos < current_l.len() {
let current_c = current_l.chars().nth(current_file.cursor_pos).unwrap();
let pairs = HashMap::from([
('(', (')', true)),
(')', ('(', false)),
('[', (']', true)),
(']', ('[', false)),
('"', ('"', true)), //could be either, really
('{', ('}', true)),
('}', ('{', false)),
('<', ('>', true)),
('>', ('<', false)),
//
]);
if let Some((corres, forwards)) = pairs.get(&current_c) {
let mut count = 0;
let content_len = current_file.content.len();
let lines: Vec<&String> = if *forwards {
current_file.content.iter().skip(current_file.line_pos).collect()
} else {
current_file.content.iter().rev().skip(content_len - current_file.line_pos - 1).collect()
};
let end = if *forwards {
content_len - current_file.line_pos
} else {
current_file.line_pos + 1
};
'outer: for i in 0..end {
let line = if i == 0 {
let l = lines[i];
let l_len = l.len();
if *forwards {
if current_file.cursor_pos + 1 < l_len {
l.substring(current_file.cursor_pos + 1, l_len)
} else {
""
}
} else {
&l.substring(0, current_file.cursor_pos).chars().rev().collect::<String>()
}
} else {
if *forwards {
lines[i]
} else {
&lines[i].chars().rev().collect::<String>()
}
};
for (c_i, c) in line.chars().enumerate() {
if c == current_c {
count += 1;
} else if &c == corres {
if count == 0 {
if *forwards {
current_file.line_pos += i;
} else {
current_file.line_pos -= i;
};
current_file.cursor_pos = if i == 0 {
if *forwards {
current_file.cursor_pos + c_i + 1
} else {
current_file.cursor_pos - c_i - 1
}
} else {
if *forwards {
c_i
} else {
line.chars().count() - c_i - 1
}
};
break 'outer;
}
count -= 1;
}
}
}
}
}
changed = false;
} else if key_press.key.is_ascii_digit() {
self.maybe_num = Some(self.maybe_num.unwrap_or(0) * 10 + key_press.key.to_digit(10).unwrap() as usize); self.maybe_num = Some(self.maybe_num.unwrap_or(0) * 10 + key_press.key.to_digit(10).unwrap() as usize);
numbered = true; numbered = true;
changed = false; changed = false;
@@ -401,7 +490,7 @@ impl WindowLike for Malvim {
let mut used_width = 0; let mut used_width = 0;
for file_index in 0..self.files.len() { for file_index in 0..self.files.len() {
let file_info = &self.files[file_index]; let file_info = &self.files[file_index];
let future_used_width = used_width + 4 + (file_info.name.len() + if file_info.changed { 2 } else { 0 }) * MONO_WIDTH as usize; let future_used_width = used_width + 4 + (file_info.name.len() + if file_info.changed { 2 } else { 0 }) * MONO_WIDTH as usize + 15;
//just cut off when too many file tabs open to fit //just cut off when too many file tabs open to fit
if future_used_width > self.dimensions[0] { if future_used_width > self.dimensions[0] {
break; break;
@@ -455,12 +544,11 @@ impl WindowLike for Malvim {
//bottom blue band stuff //bottom blue band stuff
//write mode //write mode
instructions.push(DrawInstructions::Text([0, self.dimensions[1] - BAND_HEIGHT * 2 + 2], vec!["nimbus-romono".to_string()], self.mode.to_string(), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH))); instructions.push(DrawInstructions::Text([0, self.dimensions[1] - BAND_HEIGHT * 2 + 2], vec!["nimbus-romono".to_string()], self.mode.to_string(), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH)));
let file_status; let file_status = if self.files.len() > 0 {
if self.files.len() > 0 { self.files[self.current_file_index].name.clone()
file_status = self.files[self.current_file_index].name.clone();
} else { } else {
file_status = "No file open".to_string(); "No file open".to_string()
} };
instructions.push(DrawInstructions::Text([self.dimensions[0] - file_status.len() * (MONO_WIDTH as usize), self.dimensions[1] - BAND_HEIGHT * 2 + 2], vec!["nimbus-romono".to_string()], file_status, theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH))); instructions.push(DrawInstructions::Text([self.dimensions[0] - file_status.len() * (MONO_WIDTH as usize), self.dimensions[1] - BAND_HEIGHT * 2 + 2], vec!["nimbus-romono".to_string()], file_status, theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH)));
//write command or bottom message //write command or bottom message
if self.mode == Mode::Command { if self.mode == Mode::Command {
@@ -593,12 +681,40 @@ impl Malvim {
} else if first.starts_with("/") { } else if first.starts_with("/") {
let current_file = &mut self.files[self.current_file_index]; let current_file = &mut self.files[self.current_file_index];
if current_file.content.len() > 0 { if current_file.content.len() > 0 {
let found_line_no = current_file.content.iter().skip(current_file.line_pos + 1).position(|line| { let p1 = if arg == "" {
line.contains(&first[1..]) String::new()
}); } else {
if let Some(found_line_no) = found_line_no { " ".to_string() + arg
current_file.line_pos = found_line_no + current_file.line_pos + 1; };
current_file.cursor_pos = 0; let rest = get_rest_of_split(&mut parts, Some(" "));
let rest = if rest == "" {
String::new()
} else {
" ".to_string() + &rest
};
let query = first[1..].to_string() + &p1 + &rest;
let mut lines = current_file.content.iter().skip(current_file.line_pos);
for i in 0..(current_file.content.len() - current_file.line_pos) {
let line = if i == 0 {
let l = lines.next().unwrap();
let l_len = l.len();
if (current_file.cursor_pos + 1) < l_len {
l.substring(current_file.cursor_pos + 1, l_len)
} else {
""
}
} else {
lines.next().unwrap()
};
if let Some(found_index) = line.to_string().find_substring(&query) {
current_file.line_pos += i;
current_file.cursor_pos = if i == 0 {
current_file.cursor_pos + found_index + 1
} else {
found_index
};
break;
}
} }
} }
} else if first == "x" || first == "w" || first == "write" || first == "q" || first == "quit" { } else if first == "x" || first == "w" || first == "write" || first == "q" || first == "quit" {

View File

@@ -65,15 +65,15 @@ impl WindowLike for Minesweeper {
self.first_char = '\0'; self.first_char = '\0';
WindowMessageResponse::DoNothing WindowMessageResponse::DoNothing
} else if self.first_char == '\0' { } else if self.first_char == '\0' {
if HEX_CHARS.iter().find(|c| c == &&key_press.key).is_some() { if HEX_CHARS.iter().any(|c| c == &key_press.key) {
self.first_char = key_press.key; self.first_char = key_press.key;
} }
WindowMessageResponse::DoNothing WindowMessageResponse::DoNothing
} else if HEX_CHARS.iter().find(|c| c == &&key_press.key).is_some() { } else if HEX_CHARS.iter().any(|c| c == &key_press.key) {
let u = hex_to_u8(self.first_char, key_press.key) as usize; let u = hex_to_u8(self.first_char, key_press.key) as usize;
let y = u / 16; let y = u / 16;
let x = u % 16; let x = u % 16;
if HEX_CHARS.iter().find(|c| c == &&key_press.key).is_some() { if HEX_CHARS.iter().any(|c| c == &key_press.key) {
if self.state == State::BeforePlaying { if self.state == State::BeforePlaying {
loop { loop {
self.new_tiles(); self.new_tiles();

View File

@@ -131,7 +131,7 @@ impl WindowLike for Terminal {
self.prev(); self.prev();
} else if key_press.is_down_arrow() { } else if key_press.is_down_arrow() {
self.next(); self.next();
} else { } else if key_press.is_regular() {
self.current_input += &key_press.key.to_string(); self.current_input += &key_press.key.to_string();
} }
self.calc_actual_lines(); self.calc_actual_lines();

View File

@@ -115,13 +115,11 @@ impl WindowLike for StartMenu {
]; ];
let max_per_page = CATEGORIES.len(); let max_per_page = CATEGORIES.len();
let current_focus = self.get_focus_index().unwrap(); let current_focus = self.get_focus_index().unwrap();
let mut index = 0; for (index, component) in self.components.iter().enumerate() {
for component in &self.components {
//supports multiple pages of window options per category //supports multiple pages of window options per category
if (index >= max_per_page && current_focus >= max_per_page) || (index < max_per_page && current_focus < max_per_page) { if (index >= max_per_page && current_focus >= max_per_page) || (index < max_per_page && current_focus < max_per_page) {
instructions.extend(component.draw(theme_info)); instructions.extend(component.draw(theme_info));
} }
index += 1;
} }
instructions instructions
} }
@@ -194,8 +192,7 @@ impl StartMenu {
pub fn add_category_components(&mut self) { pub fn add_category_components(&mut self) {
self.current_focus = "About".to_string(); self.current_focus = "About".to_string();
self.components = Vec::new(); self.components = Vec::new();
for c in 0..CATEGORIES.len() { for (c, name) in CATEGORIES.iter().enumerate() {
let name = CATEGORIES[c];
self.components.push(Box::new(HighlightButton::new( 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 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
))); )));

View File

@@ -89,7 +89,7 @@ impl FramebufferWriter {
} }
} }
self.rotate_buffer = Some(output_array); self.rotate_buffer = Some(output_array);
&self.rotate_buffer.as_ref().unwrap() self.rotate_buffer.as_ref().unwrap()
} }
pub fn save_buffer(&mut self) { pub fn save_buffer(&mut self) {

View File

@@ -95,7 +95,7 @@ impl WindowManager {
pub fn add_window_like(&mut self, mut window_like: Box<dyn WindowLike>, top_left: Point, dimensions: Option<Dimensions>) { pub fn add_window_like(&mut self, mut window_like: Box<dyn WindowLike>, top_left: Point, dimensions: Option<Dimensions>) {
let subtype = window_like.subtype(); let subtype = window_like.subtype();
let dimensions = dimensions.unwrap_or(window_like.ideal_dimensions(self.dimensions)); let dimensions = dimensions.unwrap_or(window_like.ideal_dimensions(self.dimensions));
self.id_count = self.id_count + 1; self.id_count += 1;
let id = self.id_count; let id = self.id_count;
window_like.handle_message(WindowMessage::Init(dimensions)); window_like.handle_message(WindowMessage::Init(dimensions));
let dimensions = if subtype == WindowLikeType::Window { [dimensions[0], dimensions[1] + WINDOW_TOP_HEIGHT] } else { dimensions }; let dimensions = if subtype == WindowLikeType::Window { [dimensions[0], dimensions[1] + WINDOW_TOP_HEIGHT] } else { dimensions };
@@ -154,7 +154,7 @@ impl WindowManager {
file.read_to_string(&mut contents).unwrap(); file.read_to_string(&mut contents).unwrap();
let lines: Vec<&str> = contents.split("\n").collect(); let lines: Vec<&str> = contents.split("\n").collect();
if lines.len() > self.current_workspace.into() { if lines.len() > self.current_workspace.into() {
self.theme = Themes::from_str(lines[self.current_workspace as usize]).unwrap_or(Default::default()); self.theme = Themes::from_str(lines[self.current_workspace as usize]).unwrap_or_default();
} }
} }
} }
@@ -667,8 +667,7 @@ impl WindowManager {
}); });
//these are needed to decide when to snapshot //these are needed to decide when to snapshot
let max_index = if redraw_ids.len() > 0 { redraw_ids.len() } else { maybe_length } - 1; let max_index = if redraw_ids.len() > 0 { redraw_ids.len() } else { maybe_length } - 1;
let mut w_index = 0; for (w_index, window_info) in redraw_windows.enumerate() {
for window_info in redraw_windows {
let window_dimensions = if window_info.fullscreen { let window_dimensions = if window_info.fullscreen {
[self.dimensions[0], self.dimensions[1] - TASKBAR_HEIGHT - INDICATOR_HEIGHT] [self.dimensions[0], self.dimensions[1] - TASKBAR_HEIGHT - INDICATOR_HEIGHT]
} else { } else {
@@ -746,7 +745,6 @@ impl WindowManager {
} }
} }
self.writer.borrow_mut().draw_buffer(window_info.top_left, window_dimensions[1], window_dimensions[0] * bytes_per_pixel, &window_writer.get_buffer()); self.writer.borrow_mut().draw_buffer(window_info.top_left, window_dimensions[1], window_dimensions[0] * bytes_per_pixel, &window_writer.get_buffer());
w_index += 1;
} }
//could probably figure out a way to do borrow() when self.rotate is false but does it matter? //could probably figure out a way to do borrow() when self.rotate is false but does it matter?
let mut writer_borrow = self.writer.borrow_mut(); let mut writer_borrow = self.writer.borrow_mut();