aboutsummaryrefslogtreecommitdiff
path: root/kscurses/ui/element.jai
diff options
context:
space:
mode:
authordam <dam@gudinoff>2023-08-17 20:28:47 +0100
committerdam <dam@gudinoff>2023-08-17 20:28:47 +0100
commit709879ee56d31fe543a0ad882713bd4e3d17d2d2 (patch)
tree12a35282bdd0f1f8a2159ade147944c89254db24 /kscurses/ui/element.jai
parentfa1b8ea54646f1a0f3eadef33e3a660b875cc1ff (diff)
downloadtask-time-tracker-709879ee56d31fe543a0ad882713bd4e3d17d2d2.tar.zst
task-time-tracker-709879ee56d31fe543a0ad882713bd4e3d17d2d2.zip
Added kscurses and testing program.
Diffstat (limited to 'kscurses/ui/element.jai')
-rw-r--r--kscurses/ui/element.jai156
1 files changed, 156 insertions, 0 deletions
diff --git a/kscurses/ui/element.jai b/kscurses/ui/element.jai
new file mode 100644
index 0000000..3db8fcb
--- /dev/null
+++ b/kscurses/ui/element.jai
@@ -0,0 +1,156 @@
+UI_Elem :: struct {
+ type : enum u8 {
+ NONE :: 0;
+ BUTTON :: 1;
+ TEXT_BUF :: 2;
+ GROUP :: 3;
+ SELECT_LIST :: 4;
+ SCENE_MANAGER :: 5;
+ POPUP_MANAGER :: 6;
+ LINE_INPUT :: 7;
+ SCALABLE_GROUP :: 8;
+ TABLE :: 9;
+ PROGRESS_BAR :: 10;
+ } = .NONE;
+
+ using visual_data : struct {
+ cursor_state : enum u8 { OUTSIDE :: 0; ON :: 1; IN :: 2; } = .OUTSIDE;
+ box_type : enum u8 { NONE :: 0; BORDER :: 1; NO_BORDER :: 2; } = .BORDER;
+ description_pos : enum u8 { TOP_LEFT :: 0; TOP_CENTER :: 1; TOP_RIGHT :: 2; BOTTOM_LEFT :: 4; BOTTOM_CENTER :: 5; BOTTOM_RIGHT :: 6; } = .TOP_LEFT;
+ description := "";
+ }
+
+ using links : struct {
+ left, right, bottom, top, inner, outer : *UI_Elem;
+ parent : *UI_Parent;
+ }
+
+ extra_handler : struct {
+ proc : (key : Key, data : *void) -> bool = null;
+ data : *void;
+ }
+}
+
+
+vtable_c_draw : []#type (*Canvas, *UI_Elem, Ibox2, *UI_Style) -> (bool) = .[
+ c_draw_default,
+ c_draw_button,
+ c_draw_textbuf,
+ c_draw_group,
+ c_draw_select_list,
+ c_draw_scene_manager,
+ c_draw_popup_manager,
+ c_draw_line_input,
+ c_draw_scalable_group,
+ c_draw_table,
+ c_draw_progress_bar
+];
+c_draw :: (canvas : *Canvas, using ui_elem : *UI_Elem, zone : Ibox2, style : *UI_Style) -> bool {
+ ok := box_type == .NONE || c_box(canvas, zone, ifx cursor_state == .ON && __ui_master.blink_stage != 1 then style.box.cursor else style.box.default, box_type == .BORDER);
+ if !ok return false;
+ ok = c_draw_description(canvas, ui_elem, zone, style);
+ if !ok return false;
+
+ content_zone := ifx box_type == .BORDER then cut_border(zone, 1) else zone;
+
+ c_draw_proc := vtable_c_draw[xx ui_elem.type];
+ assert(xx c_draw_proc);
+ return c_draw_proc(canvas, ui_elem, content_zone, style);
+}
+c_draw_default :: (canvas : *Canvas, using ui_elem : *UI_Elem, zone : Ibox2, style : *UI_Style) -> bool { return true; };
+
+intersection_line :: (box : Ibox2, _line : string, _start : ivec2) -> line:string, start:ivec2 {
+ line, start := _line, _start;
+ if start.x < box.left {
+ line.count += start.x - box.left;
+ start.x = box.left;
+ }
+ if start.x + line.count > box.left + box.width {
+ line.count = box.left + box.width - start.x;
+ }
+ return line, start;
+}
+
+c_draw_line_ascii_raw :: (canvas : *Canvas, line : string, start : ivec2, mode : Graphics_Mode) {
+ for x : 0..line.count-1 c_putchar(canvas, .{code = xx line[x], mode = mode}, start + ivec2.{xx x, 0});
+}
+
+c_draw_line_ascii :: (canvas : *Canvas, _line : string, _start : ivec2, mode : Graphics_Mode) {
+ line, start := intersection_line(canvas.zone, _line, _start);
+ c_draw_line_ascii_raw(canvas, line, start, mode);
+}
+c_draw_line_ascii :: (canvas : *Canvas, _line : string, bounds : Ibox2, offset : ivec2, mode : Graphics_Mode) {
+ assert(inside(bounds, canvas.zone));
+ line, start := intersection_line(bounds, _line, bounds.corner + offset);
+ c_draw_line_ascii_raw(canvas, line, start, mode);
+}
+c_draw_description :: (canvas : *Canvas, using ui_elem : *UI_Elem, zone : Ibox2, style : *UI_Style) -> bool {
+ if !description return true;
+ if description.count > zone.width - 2 return false;
+
+ top_or_bottom := !(description_pos & 4);
+ y := ifx top_or_bottom then zone.top else zone.top + zone.height - 1;
+
+ mode := style.text.default;
+ if description_pos & 3 == {
+ case 0; c_draw_line_ascii(canvas, description, .{zone.left + 1, y}, mode);
+ case 1; c_draw_line_ascii(canvas, description, .{xx (zone.left + (zone.width - description.count) / 2), y}, mode);
+ case 2; c_draw_line_ascii(canvas, description, .{xx (zone.left + zone.width - description.count - 1), y}, mode);
+ case; assert(false);
+ }
+ return true;
+}
+
+vtable_handle_key : []#type (*UI_Elem, Key) -> (bool) = .[
+ handle_key_none,
+ handle_key_button,
+ handle_key_none,
+ handle_key_group,
+ handle_key_select_list,
+ handle_key_scene_manager,
+ handle_key_popup_manager,
+ handle_key_line_input,
+ handle_key_scalable_group,
+ handle_key_table,
+ handle_key_none
+];
+handle_key :: (using ui_elem : *UI_Elem, key : Key) -> handled:bool {
+ handle_proc := vtable_handle_key[ui_elem.type];
+ assert(xx handle_proc);
+
+ handled := handle_proc(ui_elem, key);
+ // assert(cursor_state == .ON || cursor_state == .IN);
+ if !handled && cursor_state == .ON {
+ handled = handle_key_move(ui_elem, key);
+ }
+
+ if !handled && extra_handler.proc {
+ handled = extra_handler.proc(key, extra_handler.data);
+ }
+ return handled;
+}
+handle_key_none :: (using ui_elem : *UI_Elem, key : Key) -> handled:bool { return false; }
+handle_key_move :: (using ui_elem : *UI_Elem, key : Key) -> handled:bool {
+ __ui_master.blink_stage = 0;
+ restart_clock_cycle();
+
+ active_elem := ui_elem;
+ assert(active_elem.cursor_state == .ON);
+ try_move :: (new_elem : *UI_Elem) #expand {
+ if new_elem {
+ unset_active_recursive(`active_elem);
+ set_active_recursive(new_elem);
+ `active_elem = new_elem;
+ }
+ }
+ if key == {
+ case .LEFT; try_move(links.left);
+ case .RIGHT; try_move(links.right);
+ case .UP; try_move(links.top);
+ case .DOWN; try_move(links.bottom);
+ case .ESCAPE; try_move(links.outer);
+ case .ENTER; try_move(links.inner);;
+ }
+ assert(active_elem.cursor_state == .ON);
+ return ui_elem != active_elem;
+} \ No newline at end of file