mirror of
https://github.com/coastalwhite/lemurs
synced 2024-11-10 13:14:27 +00:00
Start on UI, basic WM selector
This commit is contained in:
parent
4eccce6d12
commit
25b421f8bb
4 changed files with 1031 additions and 5 deletions
457
Cargo.lock
generated
Normal file
457
Cargo.lock
generated
Normal file
|
@ -0,0 +1,457 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "argh"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb41d85d92dfab96cb95ab023c265c5e4261bb956c0fb49ca06d90c570f1958"
|
||||
dependencies = [
|
||||
"argh_derive",
|
||||
"argh_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argh_derive"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be69f70ef5497dd6ab331a50bd95c6ac6b8f7f17a7967838332743fbd58dc3b5"
|
||||
dependencies = [
|
||||
"argh_shared",
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argh_shared"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6f8c380fa28aa1b36107cd97f0196474bb7241bb95a453c5c01a15ac74b2eac"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cassowary"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi 0.8.0",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c85525306c4291d1b73ce93c8acf9c339f9b213aef6c1d85c3830cbf1c16325c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi 0.9.0",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a6966607622438301997d3dac0d2f6e9a90c68bb6bc1785ea98456ab93c0507"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lemors"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"argh",
|
||||
"bitflags",
|
||||
"cassowary",
|
||||
"crossterm 0.22.1",
|
||||
"rand",
|
||||
"serde",
|
||||
"tui",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"miow",
|
||||
"ntapi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
|
||||
dependencies = [
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"numtoa",
|
||||
"redox_syscall",
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tui"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39c8ce4e27049eed97cfa363a5048b09d995e209994634a0efc26a14ab6c0c23"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cassowary",
|
||||
"crossterm 0.20.0",
|
||||
"termion",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
13
Cargo.toml
13
Cargo.toml
|
@ -1,8 +1,15 @@
|
|||
[package]
|
||||
name = "lemors"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tui = { version = "0.16.0", features = [ "crossterm" ] }
|
||||
bitflags = "1.3"
|
||||
cassowary = "0.3"
|
||||
unicode-segmentation = "1.2"
|
||||
unicode-width = "0.1"
|
||||
crossterm = { version = "0.22" }
|
||||
serde = { version = "1", features = ["derive"]}
|
||||
rand = "0.8"
|
||||
argh = "0.1"
|
||||
|
|
201
src/main.rs
201
src/main.rs
|
@ -1,3 +1,200 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use crossterm::{
|
||||
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use std::{error::Error, io};
|
||||
use tui::{
|
||||
backend::{Backend, CrosstermBackend},
|
||||
layout::{Constraint, Direction, Layout},
|
||||
style::{Color, Style},
|
||||
widgets::{Block, Borders, Paragraph},
|
||||
Frame, Terminal,
|
||||
};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
mod window_manager_selector;
|
||||
use window_manager_selector::{WindowManager, WindowManagerSelectorWidget};
|
||||
|
||||
enum InputMode {
|
||||
WindowManager,
|
||||
Username,
|
||||
Password,
|
||||
Normal,
|
||||
}
|
||||
|
||||
/// App holds the state of the application
|
||||
struct App {
|
||||
/// The widget used for selection of the window manager
|
||||
window_manager_widget: WindowManagerSelectorWidget,
|
||||
|
||||
/// Current value of the Username
|
||||
username: String,
|
||||
|
||||
/// Current value of the Password
|
||||
password: String,
|
||||
|
||||
/// Current input mode
|
||||
input_mode: InputMode,
|
||||
}
|
||||
|
||||
impl Default for App {
|
||||
fn default() -> App {
|
||||
App {
|
||||
window_manager_widget: WindowManagerSelectorWidget::new(vec![
|
||||
WindowManager::new("bspwm", "sxhkd & ; exec bspwm"),
|
||||
WindowManager::new("i3", "/usr/bin/i3"),
|
||||
WindowManager::new("awesome", "/usr/bin/awesome"),
|
||||
]),
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
input_mode: InputMode::Normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// setup terminal
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
// create app and run it
|
||||
let app = App::default();
|
||||
let res = run_app(&mut terminal, app);
|
||||
|
||||
// restore terminal
|
||||
disable_raw_mode()?;
|
||||
execute!(
|
||||
terminal.backend_mut(),
|
||||
LeaveAlternateScreen,
|
||||
DisableMouseCapture
|
||||
)?;
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<()> {
|
||||
loop {
|
||||
terminal.draw(|f| ui(f, &app))?;
|
||||
|
||||
if let Event::Key(key) = event::read()? {
|
||||
match app.input_mode {
|
||||
InputMode::Normal => match key.code {
|
||||
KeyCode::Enter => {
|
||||
app.input_mode = InputMode::WindowManager;
|
||||
}
|
||||
KeyCode::Char(x) if x == 'q' => {
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
InputMode::WindowManager => match key.code {
|
||||
KeyCode::Enter | KeyCode::Tab => {
|
||||
app.input_mode = InputMode::Username;
|
||||
}
|
||||
KeyCode::Esc => {
|
||||
app.input_mode = InputMode::Normal;
|
||||
}
|
||||
KeyCode::Left => {
|
||||
app.window_manager_widget.left();
|
||||
}
|
||||
KeyCode::Right => {
|
||||
app.window_manager_widget.right();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
InputMode::Username => match key.code {
|
||||
KeyCode::Enter | KeyCode::Tab => {
|
||||
app.input_mode = InputMode::Password;
|
||||
}
|
||||
KeyCode::Esc => {
|
||||
app.input_mode = InputMode::Normal;
|
||||
}
|
||||
KeyCode::Char(c) => {
|
||||
app.username.push(c);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
InputMode::Password => match key.code {
|
||||
KeyCode::Enter | KeyCode::Tab => {
|
||||
todo!()
|
||||
}
|
||||
KeyCode::Esc => {
|
||||
app.input_mode = InputMode::Normal;
|
||||
}
|
||||
KeyCode::Char(c) => {
|
||||
app.password.push(c);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ui<B: Backend>(f: &mut Frame<B>, app: &App) {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.horizontal_margin(2)
|
||||
.vertical_margin(1)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(2),
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(2),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(2),
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(0),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(f.size());
|
||||
|
||||
app.window_manager_widget.render(
|
||||
f,
|
||||
chunks[2],
|
||||
matches!(app.input_mode, InputMode::WindowManager),
|
||||
);
|
||||
|
||||
let username = Paragraph::new(app.username.as_ref())
|
||||
.style(match app.input_mode {
|
||||
InputMode::Normal => Style::default(),
|
||||
InputMode::WindowManager => Style::default(),
|
||||
InputMode::Username => Style::default().fg(Color::Yellow),
|
||||
InputMode::Password => Style::default(),
|
||||
})
|
||||
.block(Block::default().borders(Borders::ALL).title("Input"));
|
||||
f.render_widget(username, chunks[4]);
|
||||
|
||||
let password = Paragraph::new(app.password.as_ref())
|
||||
.style(match app.input_mode {
|
||||
InputMode::Normal => Style::default(),
|
||||
InputMode::WindowManager => Style::default(),
|
||||
InputMode::Username => Style::default(),
|
||||
InputMode::Password => Style::default().fg(Color::Yellow),
|
||||
})
|
||||
.block(Block::default().borders(Borders::ALL).title("Password"));
|
||||
f.render_widget(password, chunks[6]);
|
||||
|
||||
match app.input_mode {
|
||||
InputMode::Normal | InputMode::WindowManager => {}
|
||||
InputMode::Username => f.set_cursor(
|
||||
chunks[4].x + app.username.width() as u16 + 1,
|
||||
chunks[4].y + 1,
|
||||
),
|
||||
InputMode::Password => f.set_cursor(
|
||||
chunks[6].x + app.password.width() as u16 + 1,
|
||||
chunks[6].y + 1,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
365
src/window_manager_selector.rs
Normal file
365
src/window_manager_selector.rs
Normal file
|
@ -0,0 +1,365 @@
|
|||
use tui::{
|
||||
layout::{Alignment, Rect},
|
||||
style::{Color, Style},
|
||||
terminal::Frame,
|
||||
text::{Span, Spans, Text},
|
||||
widgets::{Block, Paragraph},
|
||||
};
|
||||
|
||||
const NO_WINDOW_MANAGERS_STRING: &str = "No Window Managers Specified";
|
||||
const NO_WINDOW_MANAGERS_STRING_COLOR: [Color; 2] = [Color::LightRed, Color::Red];
|
||||
const WINDOW_MANAGER_CUTOFF_WIDTH: usize = 16;
|
||||
const PREV_NEXT_ARROWS: [&str; 2] = ["<", ">"];
|
||||
const ARROWS_COLOR: [Color; 2] = [Color::DarkGray, Color::Yellow];
|
||||
const PREV_NEXT_PADDING: usize = 1;
|
||||
const PREV_NEXT_COLOR: [Color; 2] = [Color::DarkGray, Color::DarkGray];
|
||||
const WM_PADDING: usize = 2;
|
||||
const CURRENT_COLOR: [Color; 2] = [Color::Gray, Color::White];
|
||||
|
||||
const MIN_WIDTH: usize = WINDOW_MANAGER_CUTOFF_WIDTH
|
||||
+ PREV_NEXT_ARROWS[0].len()
|
||||
+ PREV_NEXT_ARROWS[1].len()
|
||||
+ PREV_NEXT_PADDING * 2
|
||||
+ WM_PADDING * 2;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub struct WindowManager {
|
||||
title: String,
|
||||
cmd: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WindowManagerSelector {
|
||||
selected: Option<usize>,
|
||||
window_managers: Vec<WindowManager>,
|
||||
}
|
||||
|
||||
pub struct WindowManagerSelectorWidget(WindowManagerSelector);
|
||||
|
||||
impl WindowManager {
|
||||
pub fn new(title: impl ToString, cmd: impl ToString) -> WindowManager {
|
||||
let title = title.to_string();
|
||||
let cmd = cmd.to_string();
|
||||
|
||||
Self { title, cmd }
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowManagerSelector {
|
||||
pub fn new(window_managers: Vec<WindowManager>) -> Self {
|
||||
Self {
|
||||
selected: if window_managers.len() == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(0)
|
||||
},
|
||||
window_managers,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.window_managers.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn can_move(&self) -> bool {
|
||||
self.selected.map_or(false, |s| s == 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn next_index(&self, index: usize) -> usize {
|
||||
(index + 1) % self.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn prev_index(&self, index: usize) -> usize {
|
||||
if index == 0 {
|
||||
self.len() - 1
|
||||
} else {
|
||||
index - 1
|
||||
}
|
||||
}
|
||||
|
||||
fn go_next(&mut self) {
|
||||
self.selected = self.selected.map(|index| self.next_index(index));
|
||||
}
|
||||
|
||||
fn go_prev(&mut self) {
|
||||
self.selected = self.selected.map(|index| self.prev_index(index));
|
||||
}
|
||||
|
||||
fn next(&self) -> Option<&WindowManager> {
|
||||
self.selected.map_or(None, |index| {
|
||||
debug_assert!(self.len() > 0);
|
||||
self.window_managers.get(self.next_index(index))
|
||||
})
|
||||
}
|
||||
|
||||
fn prev(&self) -> Option<&WindowManager> {
|
||||
self.selected.map_or(None, |index| {
|
||||
debug_assert!(self.len() > 0);
|
||||
self.window_managers.get(self.prev_index(index))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn current(&self) -> Option<&WindowManager> {
|
||||
self.selected.map_or(None, |index| {
|
||||
debug_assert!(self.len() > 0);
|
||||
self.window_managers.get(index)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowManagerSelectorWidget {
|
||||
pub fn new(window_managers: Vec<WindowManager>) -> Self {
|
||||
Self(WindowManagerSelector::new(window_managers))
|
||||
}
|
||||
|
||||
fn wm_width(window_manager: &WindowManager) -> usize {
|
||||
// TODO: Take into account not Monospace fonts
|
||||
window_manager.title.len()
|
||||
}
|
||||
|
||||
fn find_width(prev_width: usize, current_width: usize, next_width: usize) -> usize {
|
||||
prev_width
|
||||
+ current_width
|
||||
+ next_width
|
||||
+ PREV_NEXT_ARROWS[0].len()
|
||||
+ PREV_NEXT_ARROWS[1].len()
|
||||
+ PREV_NEXT_PADDING * 2
|
||||
+ WM_PADDING * 2
|
||||
}
|
||||
|
||||
fn do_show_prev(
|
||||
area_width: usize,
|
||||
prev_width: usize,
|
||||
current_width: usize,
|
||||
next_width: Option<usize>,
|
||||
) -> bool {
|
||||
debug_assert!(area_width >= Self::find_width(0, current_width, 0));
|
||||
|
||||
Self::find_width(prev_width, current_width, next_width.unwrap_or(0)) <= area_width
|
||||
}
|
||||
|
||||
fn do_show_next(
|
||||
area_width: usize,
|
||||
left_width: Option<usize>,
|
||||
middle_width: usize,
|
||||
right_width: usize,
|
||||
) -> bool {
|
||||
debug_assert!(area_width >= Self::find_width(0, middle_width, 0));
|
||||
|
||||
Self::find_width(left_width.unwrap_or(0), middle_width, right_width) <= area_width
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&self,
|
||||
frame: &mut Frame<impl tui::backend::Backend>,
|
||||
area: Rect,
|
||||
is_focused: bool,
|
||||
) {
|
||||
let Self(selector) = self;
|
||||
|
||||
// TODO: Optimize these calls
|
||||
let current = selector.current();
|
||||
let prev = selector.prev();
|
||||
let next = selector.next();
|
||||
|
||||
let is_focused = if is_focused { 1 } else { 0 };
|
||||
|
||||
let mut msg = Vec::with_capacity(
|
||||
// Left + Right +
|
||||
// LeftPad + RightPad +
|
||||
// LeftWM + RightWM +
|
||||
// LeftWMPad + RightWMPad +
|
||||
// MiddleWM = 9
|
||||
9,
|
||||
);
|
||||
if let Some(current) = current {
|
||||
msg.push(Span::styled(
|
||||
PREV_NEXT_ARROWS[0],
|
||||
Style::default().fg(ARROWS_COLOR[is_focused]),
|
||||
)); // Left Arrow
|
||||
msg.push(Span::raw(" ".repeat(PREV_NEXT_PADDING))); // LeftPad
|
||||
if let Some(prev) = prev {
|
||||
if Self::do_show_prev(
|
||||
area.width.into(),
|
||||
Self::wm_width(prev),
|
||||
Self::wm_width(current),
|
||||
next.map(|next| Self::wm_width(next)),
|
||||
) {
|
||||
msg.push(Span::styled(
|
||||
&prev.title,
|
||||
Style::default().fg(PREV_NEXT_COLOR[is_focused]),
|
||||
)); // LeftWM
|
||||
msg.push(Span::raw(" ".repeat(WM_PADDING))); // LeftWMPad
|
||||
}
|
||||
}
|
||||
|
||||
msg.push(Span::styled(
|
||||
¤t.title,
|
||||
Style::default().fg(CURRENT_COLOR[is_focused]),
|
||||
)); // CurrentWM
|
||||
|
||||
if let Some(next) = next {
|
||||
if Self::do_show_next(
|
||||
area.width.into(),
|
||||
prev.map(|prev| Self::wm_width(prev)),
|
||||
Self::wm_width(current),
|
||||
Self::wm_width(next),
|
||||
) {
|
||||
msg.push(Span::raw(" ".repeat(WM_PADDING))); // RightWMPad
|
||||
msg.push(Span::styled(
|
||||
&next.title,
|
||||
Style::default().fg(PREV_NEXT_COLOR[is_focused]),
|
||||
)); // RightWM
|
||||
}
|
||||
}
|
||||
msg.push(Span::raw(" ".repeat(PREV_NEXT_PADDING))); // RightPad
|
||||
msg.push(Span::styled(PREV_NEXT_ARROWS[1], Style::default().fg(ARROWS_COLOR[is_focused]))); // Right Arrow
|
||||
} else {
|
||||
msg.push(Span::styled(
|
||||
NO_WINDOW_MANAGERS_STRING,
|
||||
Style::default().fg(NO_WINDOW_MANAGERS_STRING_COLOR[is_focused]),
|
||||
));
|
||||
}
|
||||
let text = Text::from(Spans::from(msg));
|
||||
let widget = Paragraph::new(text)
|
||||
.block(Block::default())
|
||||
.alignment(Alignment::Center);
|
||||
|
||||
frame.render_widget(widget, area);
|
||||
}
|
||||
|
||||
pub fn left(&mut self) {
|
||||
let Self(ref mut selector) = self;
|
||||
selector.go_prev();
|
||||
}
|
||||
|
||||
pub fn right(&mut self) {
|
||||
let Self(ref mut selector) = self;
|
||||
selector.go_next();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
mod window_manager_selector {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn empty_creation() {
|
||||
let mut selector = WindowManagerSelector::new(vec![]);
|
||||
assert_eq!(selector.current(), None);
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), None);
|
||||
selector.go_prev();
|
||||
assert_eq!(selector.current(), None);
|
||||
|
||||
let mut selector = WindowManagerSelector::new(vec![]);
|
||||
assert_eq!(selector.current(), None);
|
||||
selector.go_prev();
|
||||
assert_eq!(selector.current(), None);
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_creation() {
|
||||
let wm = WindowManager::new("abc", "/abc");
|
||||
|
||||
let mut selector = WindowManagerSelector::new(vec![wm.clone()]);
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
selector.go_prev();
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
|
||||
let mut selector = WindowManagerSelector::new(vec![wm.clone()]);
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
selector.go_prev();
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
|
||||
let mut selector = WindowManagerSelector::new(vec![wm.clone()]);
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
|
||||
let mut selector = WindowManagerSelector::new(vec![wm.clone()]);
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
selector.go_prev();
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
selector.go_prev();
|
||||
assert_eq!(selector.current(), Some(&wm));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_creation() {
|
||||
let wm1 = WindowManager::new("abc", "/abc");
|
||||
let wm2 = WindowManager::new("def", "/def");
|
||||
|
||||
let mut selector = WindowManagerSelector::new(vec![wm1.clone(), wm2.clone()]);
|
||||
assert_eq!(selector.current(), Some(&wm1));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm2));
|
||||
selector.go_prev();
|
||||
assert_eq!(selector.current(), Some(&wm1));
|
||||
|
||||
let mut selector = WindowManagerSelector::new(vec![wm1.clone(), wm2.clone()]);
|
||||
assert_eq!(selector.current(), Some(&wm1));
|
||||
selector.go_prev();
|
||||
assert_eq!(selector.current(), Some(&wm2));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm1));
|
||||
|
||||
let mut selector = WindowManagerSelector::new(vec![wm1.clone(), wm2.clone()]);
|
||||
assert_eq!(selector.current(), Some(&wm1));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm2));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm1));
|
||||
|
||||
let mut selector = WindowManagerSelector::new(vec![wm1.clone(), wm2.clone()]);
|
||||
assert_eq!(selector.current(), Some(&wm1));
|
||||
selector.go_prev();
|
||||
assert_eq!(selector.current(), Some(&wm2));
|
||||
selector.go_prev();
|
||||
assert_eq!(selector.current(), Some(&wm1));
|
||||
|
||||
let wm3 = WindowManager::new("ghi", "/ghi");
|
||||
let wm4 = WindowManager::new("jkl", "/jkl");
|
||||
|
||||
let mut selector = WindowManagerSelector::new(vec![
|
||||
wm1.clone(),
|
||||
wm2.clone(),
|
||||
wm3.clone(),
|
||||
wm4.clone(),
|
||||
]);
|
||||
assert_eq!(selector.current(), Some(&wm1));
|
||||
selector.go_prev();
|
||||
assert_eq!(selector.current(), Some(&wm4));
|
||||
|
||||
let mut selector = WindowManagerSelector::new(vec![
|
||||
wm1.clone(),
|
||||
wm2.clone(),
|
||||
wm3.clone(),
|
||||
wm4.clone(),
|
||||
]);
|
||||
assert_eq!(selector.current(), Some(&wm1));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm2));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm3));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm4));
|
||||
selector.go_next();
|
||||
assert_eq!(selector.current(), Some(&wm1));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue