A simple CPU rendered GUI IDE experience.
-rw-r--r--Cargo.toml2
-rw-r--r--src/bar.rs63
-rw-r--r--src/main.rs186
-rw-r--r--src/text.rs65
4 files changed, 198 insertions, 118 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 76ee558..9645cf2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,7 +22,7 @@ lower = "0.2.0"
amap = "0.1.2"
run_times = "0.1.0"
array_chunks = "1.0.0"
-rust-fsm = { version = "0.8.0", features = ["diagram"] }
+rust-fsm = { version = "0.8.0", path = "../rust-fsm/rust-fsm", features = ["diagram"] }
[build-dependencies]
cc = "*"
diff --git a/src/bar.rs b/src/bar.rs
index 39167b5..d2aacf9 100644
--- a/src/bar.rs
+++ b/src/bar.rs
@@ -2,30 +2,13 @@ use std::iter::{once, repeat};
use dsb::Cell;
use dsb::cell::Style;
+use winit::keyboard::{Key, ModifiersState, NamedKey};
pub struct Bar {
- pub state: state::StateMachine,
pub text: crate::text::TextArea,
pub last_action: String,
}
-rust_fsm::state_machine! {
- #[derive(Debug, PartialEq, Eq, Copy, Clone)]
- pub(crate) state(Inactive)
-
- Inactive => {
- Control => Control,
- },
- Control => {
- Saved => Inactive,
- WaitingForFname => InputFname,
- Released => Inactive,
- },
- InputFname => {
- Enter => Inactive,
- }
-}
-
impl Bar {
pub fn write_to(
&self,
@@ -34,6 +17,7 @@ impl Bar {
(into, (w, _)): (&mut [Cell], (usize, usize)),
oy: usize,
fname: &str,
+ state: &super::State,
) {
let row = &mut into[oy * w..oy * w + w];
row.fill(Cell {
@@ -43,26 +27,14 @@ impl Bar {
fn s(s: &str) -> impl Iterator<Item = (char, u8)> {
s.chars().zip(repeat(0))
}
- match self.state.state() {
- state::State::Inactive => {
- row[1.."gracilaria".len() + 1]
- .iter_mut()
- .zip("gracilaria".chars())
- .for_each(|(x, y)| x.letter = Some(y));
- row[w / 2 - fname.len() / 2
- ..w / 2 - fname.len() / 2 + fname.len()]
- .iter_mut()
- .zip(fname.chars())
- .for_each(|(x, y)| x.letter = Some(y));
- row.iter_mut()
- .rev()
- .zip(self.last_action.chars().rev())
- .for_each(|(x, y)| x.letter = Some(y));
- }
- state::State::Control => {
+ use super::State;
+ match state {
+ State::Default if super::ctrl() => {
let x = s("C + { ")
.chain(once(('S', Style::BOLD)))
.chain(s("ave, "))
+ .chain(once(('Q', Style::BOLD)))
+ .chain(s("uit, "))
.chain(once(('C', Style::BOLD)))
.chain(s("opy }"));
@@ -74,11 +46,26 @@ impl Bar {
}
});
}
- state::State::InputFname => {
+ State::Default => {
+ row[1.."gracilaria".len() + 1]
+ .iter_mut()
+ .zip("gracilaria".chars())
+ .for_each(|(x, y)| x.letter = Some(y));
+ row[w / 2 - fname.len() / 2
+ ..w / 2 - fname.len() / 2 + fname.len()]
+ .iter_mut()
+ .zip(fname.chars())
+ .for_each(|(x, y)| x.letter = Some(y));
+ row.iter_mut()
+ .rev()
+ .zip(self.last_action.chars().rev())
+ .for_each(|(x, y)| x.letter = Some(y));
+ }
+ State::InputFname(x) => {
"write to file: "
.chars()
.zip(repeat(Style::BOLD | Style::ITALIC))
- .chain(s(&self.text.rope.to_string()))
+ .chain(s(&x.rope.to_string()))
.zip(row)
.for_each(|((x, z), y)| {
*y = Cell {
@@ -88,6 +75,8 @@ impl Bar {
}
});
}
+ State::Save => unreachable!(),
+ _ => {}
}
}
}
diff --git a/src/main.rs b/src/main.rs
index 5935a9f..6899a92 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,8 @@
+// this looks pretty good though
#![feature(tuple_trait, unboxed_closures, fn_traits)]
#![feature(
+ import_trait_associated_functions,
+ guard_patterns,
if_let_guard,
deref_patterns,
generic_const_exprs,
@@ -17,6 +20,7 @@ use std::simd::prelude::*;
use std::sync::LazyLock;
use std::time::Instant;
+use Default::default;
use array_chunks::*;
use atools::prelude::*;
use dsb::cell::Style;
@@ -26,10 +30,9 @@ use rust_fsm::StateMachineImpl;
use swash::{FontRef, Instance};
use winit::event::{ElementState, Event, MouseScrollDelta, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
-use winit::keyboard::{Key, NamedKey};
+use winit::keyboard::{Key, ModifiersState, NamedKey};
use crate::bar::Bar;
-use crate::bar::state::{Input, State};
use crate::text::TextArea;
mod bar;
mod text;
@@ -38,6 +41,8 @@ fn main() {
entry(EventLoop::new().unwrap())
}
+static mut MODIFIERS: ModifiersState = ModifiersState::empty();
+
const BG: [u8; 3] = [31, 36, 48];
const FG: [u8; 3] = [204, 202, 194];
#[implicit_fn::implicit_fn]
@@ -48,16 +53,14 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
let mut text = TextArea::default();
let mut origin = std::env::args().nth(1);
let mut fonts = dsb::Fonts::new(
- *FONT,
F::instance(*FONT, *BFONT),
+ *FONT,
*IFONT,
F::instance(*IFONT, *BIFONT),
);
- let mut bar = Bar {
- text: TextArea::default(),
- state: bar::state::StateMachine::new(),
- last_action: String::default(),
- };
+ let mut state = State::Default;
+ let mut bar =
+ Bar { text: TextArea::default(), last_action: String::default() };
let mut i = Image::build(1, 1).fill(BG);
let mut cells = vec![];
std::env::args().nth(1).map(|x| {
@@ -101,7 +104,6 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
window.inner_size().height as _,
),
);
- dbg!(&bar.state);
match event {
Event::WindowEvent {
window_id,
@@ -167,12 +169,15 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
(&mut cells, (c, r)),
(t_ox, 0),
);
+ text.c = c - t_ox;
+ text.r = r - 1;
bar.write_to(
BG,
FG,
(&mut cells, (c, r)),
r - 1,
&origin.as_deref().unwrap_or("new buffer"),
+ &state,
);
println!("cell=");
@@ -264,89 +269,56 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
}
}
Event::WindowEvent {
- event: WindowEvent::KeyboardInput { event, .. },
+ event: WindowEvent::ModifiersChanged(modifiers),
..
- } if event.state == ElementState::Released
- && *bar.state.state() == State::Control
- && event.logical_key == NamedKey::Control =>
- {
- bar.state.consume(&Input::Released).unwrap();
+ } => {
+ unsafe { MODIFIERS = modifiers.state() };
window.request_redraw();
}
Event::WindowEvent {
event: WindowEvent::KeyboardInput { event, .. },
..
- } if *bar.state.state() == State::Control
- && event.state == ElementState::Pressed =>
- {
- use Key::*;
- match event.logical_key {
- Character(x) if x == "s" => match &origin {
+ } if event.state == ElementState::Pressed => {
+ let o = state
+ .consume(Action::K(event.logical_key.clone()))
+ .unwrap();
+ match o {
+ Some(Do::Save) => match &origin {
Some(_) => {
- bar.state.consume(&Input::Saved).unwrap();
+ state.consume(Action::Saved).unwrap();
save!();
}
None => {
- bar.state
- .consume(&Input::WaitingForFname)
+ state
+ .consume(Action::RequireFilename)
.unwrap();
}
},
- // Character(x)
- // if x == "s"
- // && =>
- // {
- // bar.state.consume(State::Save);
- // text.rope
- // .write_to(File::open(origin).unwrap())
- // .unwrap();
- // }
- // Character(x) if x == "s" => {}
- _ => panic!(),
- }
- window.request_redraw();
- }
-
- Event::WindowEvent {
- event: WindowEvent::KeyboardInput { event, .. },
- ..
- } if event.state == ElementState::Pressed => {
- use Key::*;
- use NamedKey::*;
- let text = if bar.state.state() == &State::InputFname {
- &mut bar.text
- } else {
- &mut text
- };
- match event.logical_key {
- Named(Space) => text.insert(" "),
- Named(Backspace) => text.backspace(),
- Named(ArrowLeft) => text.left(),
- Named(Home) => text.home(),
- Named(End) => text.end(),
- Named(ArrowRight) => text.right(),
- Named(ArrowUp) => text.up(),
- Named(ArrowDown) => text.down(r),
- Named(Enter)
- if bar.state.state() == &State::InputFname =>
- {
- bar.state.consume(&Input::Enter).unwrap();
- origin = Some(
- std::mem::take(&mut bar.text)
- .rope
- .to_string(),
- );
+ Some(Do::SaveTo(x)) => {
+ origin = Some(x);
save!();
}
- Named(Enter) => text.enter(),
- Named(Control) => {
- bar.state.consume(&Input::Control).unwrap();
+ Some(Do::Edit) => {
+ handle2(event.logical_key, &mut text);
}
- Character(x) => {
- text.insert(&*x);
+ Some(Do::Quit) => elwt.exit(),
+ Some(Do::StartSelection) => {
+ let State::Selection(x) = &mut state else {
+ panic!()
+ };
+ *x = text.cursor..text.cursor
}
- _ => {}
- };
+ Some(Do::UpdateSelection) => {
+ let State::Selection(x) = &mut state else {
+ panic!()
+ };
+ let Key::Named(y) = event.logical_key else {
+ panic!()
+ };
+ *x = text.extend_selection(y, x.clone());
+ }
+ None => {}
+ }
window.request_redraw();
}
_ => {}
@@ -356,6 +328,31 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
winit_app::run_app(event_loop, app);
}
+
+fn handle2(key: Key, text: &mut TextArea) {
+ use Key::*;
+ use NamedKey::*;
+
+ match key {
+ Named(Space) => text.insert(" "),
+ Named(Backspace) => text.backspace(),
+ Named(ArrowLeft) => text.left(),
+ Named(Home) => text.home(),
+ Named(End) => text.end(),
+ Named(ArrowRight) => text.right(),
+ Named(ArrowUp) => text.up(),
+ Named(ArrowDown) => text.down(),
+ Named(Enter) => text.enter(),
+ Character(x) => {
+ text.insert(&*x);
+ }
+ _ => {}
+ };
+}
+fn handle(key: Key, mut text: TextArea) -> TextArea {
+ handle2(key, &mut text);
+ text
+}
pub static FONT: LazyLock<FontRef<'static>> = LazyLock::new(|| {
FontRef::from_index(
&include_bytes!("/home/os/CascadiaCodeNF.ttf")[..],
@@ -377,3 +374,42 @@ pub static BIFONT: LazyLock<Instance<'static>> = LazyLock::new(|| {
pub static BFONT: LazyLock<Instance<'static>> =
LazyLock::new(|| FONT.instances().find_by_name("Bold").unwrap());
+fn shift() -> bool {
+ unsafe { MODIFIERS }.shift_key()
+}
+fn ctrl() -> bool {
+ unsafe { MODIFIERS }.control_key()
+}
+fn arrow(k: &Key) -> bool {
+ matches!(
+ k,
+ Key::Named(
+ NamedKey::ArrowLeft
+ | NamedKey::ArrowRight
+ | NamedKey::ArrowDown
+ | NamedKey::ArrowUp
+ )
+ )
+}
+
+// use NamedKey::Arrow
+use std::ops::Range;
+rust_fsm::state_machine! {
+ #[derive(Clone, Debug)]
+ pub(crate) State => Action => Do
+
+ Dead => K(Key => _) => Dead,
+ Default => {
+ K(Key => Key::Character(x) if x == "s" && ctrl()) => Save [Save],
+ K(Key => Key::Character(x) if x == "q" && ctrl()) => Dead [Quit],
+ K(Key => x if shift() && arrow(&x)) => Selection(Range<usize> => 0..0) [StartSelection],
+ K(Key => _) => Default [Edit],
+ },
+ Selection(Range<usize> => x) => K(Key => y if arrow(&y) && shift()) => Selection(Range<usize> => x) [UpdateSelection],
+ Save => {
+ RequireFilename => InputFname(TextArea => default()),
+ Saved => Default,
+ },
+ InputFname(TextArea => t) => K(Key => Key::Named(NamedKey::Enter)) => Default [SaveTo(String => t.rope.to_string())],
+ InputFname(TextArea => t) => K(Key => k) => InputFname(TextArea => handle(k, t)),
+}
diff --git a/src/text.rs b/src/text.rs
index 1f92481..bfb6d1c 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -1,3 +1,4 @@
+use std::fmt::Debug;
use std::sync::LazyLock;
use atools::Chunked;
@@ -7,7 +8,7 @@ use ropey::Rope;
use tree_sitter_highlight::{
HighlightConfiguration, HighlightEvent, Highlighter,
};
-use winit::keyboard::SmolStr;
+use winit::keyboard::{NamedKey, SmolStr};
#[rustfmt::skip]
const NAMES: [&str; 13] = ["attribute", "comment", "constant", "function", "keyword", "number", "operator", "punctuation",
"string", "tag", "type", "variable", "variable.parameter"];
@@ -40,6 +41,35 @@ pub struct TextArea {
highlighter: Highlighter,
column: usize,
pub vo: usize,
+
+ pub r: usize,
+ pub c: usize,
+}
+impl Debug for TextArea {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("TextArea")
+ .field("rope", &self.rope)
+ .field("cursor", &self.cursor)
+ .field("column", &self.column)
+ .field("vo", &self.vo)
+ .field("r", &self.r)
+ .field("c", &self.c)
+ .finish()
+ }
+}
+
+impl Clone for TextArea {
+ fn clone(&self) -> Self {
+ Self {
+ rope: self.rope.clone(),
+ cursor: self.cursor,
+ highlighter: Highlighter::default(),
+ column: self.column,
+ vo: self.vo,
+ r: self.r,
+ c: self.c,
+ }
+ }
}
impl TextArea {
@@ -120,7 +150,7 @@ impl TextArea {
(|| self.insert(" ")).run(n);
}
- pub fn down(&mut self, r: usize) {
+ pub fn down(&mut self) {
let l = self.rope.try_char_to_line(self.cursor).unwrap_or(0);
// next line size
@@ -145,10 +175,10 @@ impl TextArea {
.unwrap_or(0)
};
if self.rope.char_to_line(self.cursor)
- >= (self.vo + r).saturating_sub(5)
+ >= (self.vo + self.r).saturating_sub(5)
{
self.vo += 1;
- self.vo = self.vo.min(self.l() - r);
+ self.vo = self.vo.min(self.l() - self.r);
}
}
@@ -170,7 +200,9 @@ impl TextArea {
}
pub fn backspace(&mut self) {
- _ = self.rope.try_remove(self.cursor - 1..self.cursor);
+ _ = self
+ .rope
+ .try_remove(self.cursor.saturating_sub(1)..self.cursor);
self.cursor = self.cursor.saturating_sub(1);
}
#[implicit_fn::implicit_fn]
@@ -312,4 +344,27 @@ impl TextArea {
need
}
+
+ pub fn extend_selection(
+ &self,
+ key: NamedKey,
+ r: std::ops::Range<usize>,
+ ) -> std::ops::Range<usize> {
+ match key {
+ NamedKey::ArrowLeft => r.start.saturating_sub(1)..r.end,
+ NamedKey::ArrowRight => r.start..r.end + 1,
+ NamedKey::ArrowUp => {
+ let l = self.rope.char_to_line(r.start);
+ self.rope.line_to_char(l - 1) + r.start
+ - self.rope.line_to_char(l)..r.end
+ }
+ NamedKey::ArrowDown => {
+ let l = self.rope.char_to_line(r.end);
+ r.start
+ ..self.rope.line_to_char(l + 1) + r.end
+ - self.rope.line_to_char(l)
+ }
+ _ => unreachable!(),
+ }
+ }
}