From 2e31cbeb70d93bb0f5512064f181990db189d1f6 Mon Sep 17 00:00:00 2001 From: Sebastiano Tronto Date: Sun, 4 Oct 2026 20:02:15 +0200 Subject: Initial commit --- demo/hjkl/hjkl.ha | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 demo/hjkl/hjkl.ha (limited to 'demo/hjkl/hjkl.ha') diff --git a/demo/hjkl/hjkl.ha b/demo/hjkl/hjkl.ha new file mode 100644 index 0000000..487ebd3 --- /dev/null +++ b/demo/hjkl/hjkl.ha @@ -0,0 +1,190 @@ +use fmt; +use tb = termbox2; + +def bg = tb::COLOR.DEFAULT; +def fg = tb::COLOR.DEFAULT | tb::ATTRIBUTE.BOLD; +def exit_help: str = "Use :q to exit (colon, q, enter). Sad to see you go :("; + +let wpos: (int, int) = (-1, -1); +let msg: str = "Use HJKL to move the widget around"; +let colon_mode: bool = false; +let exit_attempts: size = 0; + +type cmd = enum { + UP, + DOWN, + LEFT, + RIGHT, + COLON, + ARROW, + Q, + ENTER, + EXIT_ATTEMPT, + NONE, +}; + +fn draw_small_widget_thing(w: size, h: size) void = { + if (wpos.0 < 4) wpos.0 += 1 + else if (wpos.0 > w: int - 5) wpos.0 -= 1 + else if (wpos.1 < 2) wpos.1 += 1 + else if (wpos.1 > h: int - 3) wpos.1 -= 1; + + tb::print(wpos.0, wpos.1 - 1, fg, bg, "K")!; + tb::print(wpos.0, wpos.1 + 1, fg, bg, "J")!; + tb::print(wpos.0 - 2, wpos.1, fg, bg, "H + L")!; +}; + +fn paint(x: int, y: int, c: tb::uattr) void = + tb::set_cell(x, y, ' ', tb::COLOR.DEFAULT, c)!; + +fn paint_col3(x: int, y: int, c: tb::uattr) void = { + paint(x, y-1, c); + paint(x, y, c); + paint(x, y+1, c); +}; + +fn paint_row5(x: int, y: int, c: tb::uattr) void = { + paint(x-2, y, c); + paint(x-1, y, c); + paint(x, y, c); + paint(x+1, y, c); + paint(x+2, y, c); +}; + +fn paint_cell(x: int, y: int, c: tb::uattr, s: str) void = { + paint_col3(x-2, y, c); + paint_row5(x, y-1, c); + tb::print(x, y, fg, bg, s)!; + paint_row5(x, y+1, c); + paint_col3(x+2, y, c); +}; + +fn draw_large_widget_thing(w: size, h: size) void = { + if (wpos.0 < 10) wpos.0 += 1 + else if (wpos.0 > w: int - 11) wpos.0 -= 1 + else if (wpos.1 < 7) wpos.1 += 1 + else if (wpos.1 > h: int - 8) wpos.1 -= 1; + + let c = tb::COLOR.BLUE; + paint_cell(wpos.0-6, wpos.1, c, "H"); + paint_cell(wpos.0+6, wpos.1, c, "L"); + paint_cell(wpos.0, wpos.1-4, c, "K"); + paint_cell(wpos.0, wpos.1+4, c, "J"); + paint_col3(wpos.0, wpos.1, c); + paint_row5(wpos.0, wpos.1, c); +}; + +fn draw_widget_thing(w: size, h: size) void = { + if (w < 30 || h < 15) + draw_small_widget_thing(w, h) + else + draw_large_widget_thing(w, h); +}; + +fn draw_bottom_bar(w: size, h: size) void = { + tb::print(0, h: int - 1, fg, bg, msg)!; +}; + +fn handle_events_and_exit() bool = { + let e = tb::poll_event(); + if (e is tb::err) + return false; + + return match (e as tb::event) { + case tb::resize_event => yield false; + case tb::mouse_event => yield { + msg = "A mouse? Are you serious?"; + yield false; + }; + case let ke: tb::key_event => yield handle_key_event_and_exit(ke); + }; +}; + +fn is_arrow(k: tb::key) bool = + k == tb::KEY.ARROW_UP || k == tb::KEY.ARROW_DOWN || + k == tb::KEY.ARROW_LEFT || k == tb::KEY.ARROW_RIGHT; + +fn is_exit_attempt(k: tb::key) bool = + k == tb::KEY.CTRL_Q || k == tb::KEY.CTRL_C || k == tb::KEY.CTRL_X || + k == tb::KEY.CTRL_Z || k == tb::KEY.CTRL_D || + k == tb::KEY.DELETE || k == tb::KEY.BACKSPACE || k == tb::KEY.ESC; + +fn get_cmd(ke: tb::key_event) cmd = match (ke.key_or_ch) { + case let r: rune => yield + if (r == 'h') cmd::LEFT + else if (r == 'j') cmd::DOWN + else if (r == 'k') cmd::UP + else if (r == 'l') cmd::RIGHT + else if (r == ':') cmd::COLON + else if (r == 'q') cmd::Q + else if (r == 'Q') cmd::EXIT_ATTEMPT + else cmd::NONE; + case let k: tb::key => yield + if (k == tb::KEY.ENTER) cmd::ENTER + else if (is_arrow(k)) cmd::ARROW + else if (is_exit_attempt(k)) cmd::EXIT_ATTEMPT + else cmd::NONE; + }; + +fn exit_attempt() void = { + exit_attempts += 1; + msg = if (exit_attempts < 3) "Trying to get out? :3" + else if (exit_attempts < 6) "It's just like Vim!" + else if (exit_attempts < 8) "Uhm no, that won't work" + else if (exit_attempts < 10) "Ok, I'll tell you..." + else exit_help; +}; + +fn handle_key_event_and_exit(ke: tb::key_event) bool = { + let c = get_cmd(ke); + + if (c == cmd::COLON) + colon_mode = true; + + if (colon_mode) { + if (msg == ":q" && c == cmd::ENTER) + return true; + + msg = if (msg == ":") { + yield if (c == cmd::Q) ":q" else exit_help; + } else if (c == cmd::COLON) ":" else exit_help; + + return false; + }; + + switch (c) { + case cmd::UP => wpos.1 -= 1; + case cmd::DOWN => wpos.1 += 1; + case cmd::LEFT => wpos.0 -= 1; + case cmd::RIGHT => wpos.0 += 1; + case cmd::ARROW => msg = "No arrows allowed!"; + case cmd::COLON => colon_mode = true; + case cmd::EXIT_ATTEMPT => exit_attempt(); + case cmd::Q => exit_attempt(); + case => yield; + }; + + return false; +}; + +export fn main() void = { + let e = tb::init(); + if (e is tb::err) + fmt::fatalf(tb::strerror(e as tb::err)); + defer tb::shutdown()!; + + wpos = (tb::width()!: int / 2, tb::height()!: int / 2); + for (true) { + tb::clear()!; + + let w = tb::width()!; + let h = tb::height()!; + + draw_widget_thing(w, h); + draw_bottom_bar(w, h); + tb::present()!; + + if (handle_events_and_exit()) break; + }; + return; +}; -- cgit v1.3