getting towards terminal integration

This commit is contained in:
Yehowshua Immanuel 2024-12-23 15:22:17 -05:00
parent 4c00a633af
commit 24710414bd
10 changed files with 417 additions and 1 deletions

View file

@ -1,6 +1,7 @@
use crate::{platform, theme::*, Filename, Layout, Mode};
use std::sync::Arc;
use zoon::*;
use crate::term::TERM_OPEN;
pub struct HeaderPanel {
hierarchy: Mutable<Option<Arc<wellen::Hierarchy>>>,
@ -37,6 +38,7 @@ impl HeaderPanel {
.item(self.load_button())
.item(self.layout_switcher())
.item(self.mode_switcher())
.item(self.open_terminal())
.item(self.open_konata_file()),
)
}
@ -212,4 +214,26 @@ impl HeaderPanel {
.on_hovered_change(move |is_hovered| hovered.set_neq(is_hovered))
.on_press(move || Task::start(platform::open_konata_file()))
}
fn open_terminal(&self) -> impl Element {
let (hovered, hovered_signal) = Mutable::new_and_signal(false);
Button::new()
.s(Padding::new().x(20).y(10))
.s(Background::new().color_signal(
hovered_signal.map_bool(|| COLOR_MEDIUM_SLATE_BLUE, || COLOR_SLATE_BLUE),
))
.s(Align::new().left())
.s(RoundedCorners::all(15))
.label(
El::new()
.s(Font::new().no_wrap())
.child("Open Terminal"),
)
.on_hovered_change(move |is_hovered| hovered.set_neq(is_hovered))
.on_press(move || {
let term_open = TERM_OPEN.get();
TERM_OPEN.set(!term_open);
})
}
}

View file

@ -1,4 +1,5 @@
use shared::DiagramConnectorMessage;
use term::TERM_OPEN;
use std::{mem, sync::Arc};
use zoon::*;
@ -23,6 +24,8 @@ use command_panel::CommandPanel;
pub mod theme;
use theme::*;
pub mod term;
#[derive(Clone, Copy, Default)]
enum Layout {
Tree,
@ -181,4 +184,15 @@ fn root() -> impl Element {
}
})))
.item(CommandPanel::new())
.item_signal(
TERM_OPEN.signal_cloned().map(
|term_open| {
match term_open {
true => {El::new().child("Terminal")}
false => {El::new().child("")}
}
}
)
// El::new()
)
}

148
frontend/src/term.rs Normal file
View file

@ -0,0 +1,148 @@
use std::ops::Index;
use chrono::format;
use zoon::*;
use zoon::{println, eprintln, *};
use shared::term::{TerminalDownMsg, TerminalScreen, TerminalUpMsg};
// use tokio::time::timeout;
pub static TERM_OPEN: Lazy<Mutable<bool>> = Lazy::new(|| {false.into()});
static TERMINAL_STATE: Lazy<Mutable<TerminalDownMsg>> =
Lazy::new(|| {
Mutable::new(TerminalDownMsg::TermNotStarted)
});
// static CONNECTION: Lazy<Connection<UpMsg, DownMsg>> = Lazy::new(|| {
// Connection::new(
// |down_msg, _| {
// match down_msg {
// DownMsg::TerminalDownMsg(terminal_msg) => {
// TERMINAL_STATE.set(terminal_msg);
// }
// }
// }
// )
// });
pub fn root() -> impl Element {
term_request();
let terminal =
El::new()
.s(Width::fill())
.s(Height::fill())
.s(Font::new().family([
FontFamily::new("Lucida Console"),
FontFamily::new("Courier"),
FontFamily::new("monospace")
]))
.update_raw_el(|raw_el| {
raw_el.global_event_handler(|event: events::KeyDown| {
println!("Pressed key: {}", &event.key());
send_char(
(&event).key().as_str(),
(&event).ctrl_key(),
);
})
})
.child_signal(TERMINAL_STATE.signal_cloned().map(
|down_msg| {
match down_msg {
TerminalDownMsg::FullTermUpdate(term) => {
make_grid_with_newlines(&term)
},
TerminalDownMsg::TermNotStarted => {
"Term not yet started!".to_string()
},
TerminalDownMsg::BackendTermStartFailure(msg) => {
format!("Error: BackendTermStartFailure: {}", msg)
}
}
}
)
)
;
let root = Column::new()
.s(Width::fill())
.s(Height::fill())
.s(Align::new().top())
.item(terminal);
root
}
// TODO : fill this out
fn term_request() {
}
fn send_char(
s : &str,
has_control : bool,
) {
match process_str(s, has_control) {
// TODO : fill this out
Some(c) => {
eprintln!("Sending char: {}", c);
}
None => {eprintln!("Not processing: {}", s)}
}
}
fn make_grid_with_newlines(term : &TerminalScreen) -> String {
let mut formatted = String::new();
for (i, c) in term.content.chars().enumerate() {
formatted.push(c);
if (i + 1) % term.cols == 0 {
formatted.push('\n');
}
}
formatted
}
fn process_str(s: &str, has_ctrl : bool) -> Option<char> {
match s {
"Enter" => {return Some('\n');}
"Escape" => {return Some('\x1B');}
"Backspace" => {return Some('\x08');}
"ArrowUp" => {return Some('\x10');}
"ArrowDown" => {return Some('\x0E');}
"ArrowLeft" => {return Some('\x02');}
"ArrowRight" => {return Some('\x06');}
_ => {}
}
// Check if the string has exactly one character
if s.chars().count() == 1 {
// Safe unwrap because we know the length is 1
let c = s.chars().next().unwrap();
let c = process_for_ctrl_char(c, has_ctrl);
return Some(c);
}
None
}
fn is_lowercase_alpha(c: char) -> bool {
char_is_between_inclusive(c, 'a', 'z')
}
fn process_for_ctrl_char(c: char, has_ctrl : bool) -> char {
let mut final_ctrl_char = c;
if has_ctrl {
if is_lowercase_alpha(c) {
let c_u8 = (c as u8);
let ctrl_char_u8 = c_u8 - 96;
final_ctrl_char = (ctrl_char_u8 as char);
} else if char_is_between_inclusive(c, '[', '_') {
let c_u8 = (c as u8);
let ctrl_char_u8 = c_u8 - 90;
final_ctrl_char = (ctrl_char_u8 as char);
}
}
return final_ctrl_char
}
fn char_is_between_inclusive(c : char, lo_char : char, hi_char : char) -> bool {
c >= lo_char && c <= hi_char
}