diff --git a/plugins/grep/src/lib.rs b/plugins/grep/src/lib.rs index 323a7ec..8ce5a34 100644 --- a/plugins/grep/src/lib.rs +++ b/plugins/grep/src/lib.rs @@ -8,7 +8,7 @@ use std::{ }; use grep::{ - regex::{RegexMatcher, RegexMatcherBuilder}, + regex::{RegexMatcherBuilder}, searcher::{BinaryDetection, SearcherBuilder, Sink, SinkError}, }; use plugin_rs_bindings::{Buffer, Closure, Hook, InputMap, Key, PaletteColor, Plugin}; diff --git a/src/main.odin b/src/main.odin index 5193d6f..08804ce 100644 --- a/src/main.odin +++ b/src/main.odin @@ -754,6 +754,8 @@ main :: proc() { } }; + ui.init(); + for !raylib.WindowShouldClose() && !state.should_close { state.screen_width = int(raylib.GetScreenWidth()); state.screen_height = int(raylib.GetScreenHeight()); @@ -778,9 +780,85 @@ main :: proc() { } core.draw_file_buffer(&state, buffer, 32, state.source_font_height, state.font); - ui.draw_menu_bar(&state, &menu_bar_state, 0, 0, i32(state.screen_width), i32(state.screen_height), state.source_font_height); - raylib.DrawRectangle(0, i32(state.screen_height - state.source_font_height), i32(state.screen_width), i32(state.source_font_height), theme.get_palette_raylib_color(.Background2)); + { + ui.push_parent(ui.push_box("main", {}, .Vertical, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.PercentOfParent, 100)})); + defer ui.pop_parent(); + + { + ui.push_parent(ui.push_box("top_nav", {.DrawBackground}, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.Exact, state.source_font_height)})); + defer ui.pop_parent(); + + if ui.button("Editor").clicked { + fmt.println("you clicked the button"); + } + + ui.push_box( + "nav spacer", + {.DrawBackground}, + semantic_size = { + ui.make_semantic_size(.Exact, 16), + ui.make_semantic_size(.Exact, state.source_font_height) + } + ); + + if ui.button("Buffers").clicked { + fmt.println("you clicked the button"); + } + + //ui.two_buttons_test("button left", "button right"); + } + { + ui.push_parent(ui.push_box("deezbuffer", {}, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.Fill, 0)})); + defer ui.pop_parent(); + + ui.spacer("left side"); + { + ui.push_parent(ui.spacer("right side")); + defer ui.pop_parent(); + + ui.button("Do you need some help?"); + } + } + { + ui.push_parent(ui.push_box("bottom stats", {.DrawBackground}, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.Exact, state.source_font_height)})); + defer ui.pop_parent(); + + label := ""; + if state.mode == .Insert { + label = "INSERT"; + } else if state.mode == .Normal { + label = "NORMAL"; + } + + if ui.button(label).clicked { + fmt.println("you clicked the button"); + } + + ui.spacer("stats inbetween"); + + line_info_text := raylib.TextFormat( + "Line: %d, Col: %d, Len: %d --- Slice Index: %d, Content Index: %d", + //"Line: %d, Col: %d", + buffer.cursor.line + 1, + buffer.cursor.col + 1, + core.file_buffer_line_length(buffer, buffer.cursor.index), + buffer.cursor.index.slice_index, + buffer.cursor.index.content_index + ); + ui.button(string(line_info_text)); + } + } + + ui.compute_layout({ state.screen_width, state.screen_height }, state.source_font_width, state.source_font_height); + + + + ui.draw(state.font, state.source_font_width, state.source_font_height); + ui.prune(); + //ui.draw_menu_bar(&state, &menu_bar_state, 0, 0, i32(state.screen_width), i32(state.screen_height), state.source_font_height); + + //raylib.DrawRectangle(0, i32(state.screen_height - state.source_font_height), i32(state.screen_width), i32(state.source_font_height), theme.get_palette_raylib_color(.Background2)); line_info_text := raylib.TextFormat( // "Line: %d, Col: %d, Len: %d --- Slice Index: %d, Content Index: %d", @@ -793,60 +871,60 @@ main :: proc() { ); line_info_width := raylib.MeasureTextEx(state.font, line_info_text, f32(state.source_font_height), 0).x; - switch state.mode { - case .Normal: - raylib.DrawRectangle( - 0, - i32(state.screen_height - state.source_font_height), - i32(8 + len("NORMAL")*state.source_font_width), - i32(state.source_font_height), - theme.get_palette_raylib_color(.Foreground4)); - raylib.DrawRectangleV( - raylib.Vector2 { f32(state.screen_width) - line_info_width - 8, f32(state.screen_height - state.source_font_height) }, - raylib.Vector2 { 8 + line_info_width, f32(state.source_font_height) }, - theme.get_palette_raylib_color(.Foreground4)); - raylib.DrawTextEx( - state.font, - "NORMAL", - raylib.Vector2 { 4, f32(state.screen_height - state.source_font_height) }, - f32(state.source_font_height), - 0, - theme.get_palette_raylib_color(.Background1)); - case .Insert: - raylib.DrawRectangle( - 0, - i32(state.screen_height - state.source_font_height), - i32(8 + len("INSERT")*state.source_font_width), - i32(state.source_font_height), - theme.get_palette_raylib_color(.Foreground2)); - raylib.DrawRectangleV( - raylib.Vector2 { f32(state.screen_width) - line_info_width - 8, f32(state.screen_height - state.source_font_height) }, - raylib.Vector2 { 8 + line_info_width, f32(state.source_font_height) }, - theme.get_palette_raylib_color(.Foreground2)); - raylib.DrawTextEx( - state.font, - "INSERT", - raylib.Vector2 { 4, f32(state.screen_height - state.source_font_height) }, - f32(state.source_font_height), - 0, - theme.get_palette_raylib_color(.Background1)); - } + // switch state.mode { + // case .Normal: + // raylib.DrawRectangle( + // 0, + // i32(state.screen_height - state.source_font_height), + // i32(8 + len("NORMAL")*state.source_font_width), + // i32(state.source_font_height), + // theme.get_palette_raylib_color(.Foreground4)); + // raylib.DrawRectangleV( + // raylib.Vector2 { f32(state.screen_width) - line_info_width - 8, f32(state.screen_height - state.source_font_height) }, + // raylib.Vector2 { 8 + line_info_width, f32(state.source_font_height) }, + // theme.get_palette_raylib_color(.Foreground4)); + // raylib.DrawTextEx( + // state.font, + // "NORMAL", + // raylib.Vector2 { 4, f32(state.screen_height - state.source_font_height) }, + // f32(state.source_font_height), + // 0, + // theme.get_palette_raylib_color(.Background1)); + // case .Insert: + // raylib.DrawRectangle( + // 0, + // i32(state.screen_height - state.source_font_height), + // i32(8 + len("INSERT")*state.source_font_width), + // i32(state.source_font_height), + // theme.get_palette_raylib_color(.Foreground2)); + // raylib.DrawRectangleV( + // raylib.Vector2 { f32(state.screen_width) - line_info_width - 8, f32(state.screen_height - state.source_font_height) }, + // raylib.Vector2 { 8 + line_info_width, f32(state.source_font_height) }, + // theme.get_palette_raylib_color(.Foreground2)); + // raylib.DrawTextEx( + // state.font, + // "INSERT", + // raylib.Vector2 { 4, f32(state.screen_height - state.source_font_height) }, + // f32(state.source_font_height), + // 0, + // theme.get_palette_raylib_color(.Background1)); + // } relative_file_path, _ := filepath.rel(state.directory, buffer.file_path) - raylib.DrawTextEx( - state.font, - raylib.TextFormat("%s", relative_file_path), - raylib.Vector2 { 8 + 4 + 6 * f32(state.source_font_width), f32(state.screen_height - state.source_font_height) }, - f32(state.source_font_height), - 0, - theme.get_palette_raylib_color(.Foreground1)); - raylib.DrawTextEx( - state.font, - line_info_text, - raylib.Vector2 { f32(state.screen_width) - line_info_width - 4, f32(state.screen_height - state.source_font_height) }, - f32(state.source_font_height), - 0, - theme.get_palette_raylib_color(.Background1)); + // raylib.DrawTextEx( + // state.font, + // raylib.TextFormat("%s", relative_file_path), + // raylib.Vector2 { 8 + 4 + 6 * f32(state.source_font_width), f32(state.screen_height - state.source_font_height) }, + // f32(state.source_font_height), + // 0, + // theme.get_palette_raylib_color(.Foreground1)); + // raylib.DrawTextEx( + // state.font, + // line_info_text, + // raylib.Vector2 { f32(state.screen_width) - line_info_width - 4, f32(state.screen_height - state.source_font_height) }, + // f32(state.source_font_height), + // 0, + // theme.get_palette_raylib_color(.Background1)); if state.window != nil && state.window.draw != nil { state.window.draw(state.plugin_vtable, state.window.user_data); diff --git a/src/ui/imm.odin b/src/ui/imm.odin new file mode 100644 index 0000000..7ea021c --- /dev/null +++ b/src/ui/imm.odin @@ -0,0 +1,569 @@ +package ui + +import "core:fmt" +import "core:strings" +import "core:math" +import "vendor:raylib" + +import "../theme" + +root: ^Box = nil; +current_parent: ^Box = nil; +persistent: map[Key]^Box = nil; +current_interaction_index: int = 0; + +clips: [dynamic]Rect = nil; + +Rect :: struct { + pos: [2]int, + size: [2]int, +} + +Key :: struct { + label: string, + value: int, +} + +Interaction :: struct { + clicked: bool, +} + +Flag :: enum { + Clickable, + Hoverable, + Scrollable, + DrawText, + DrawBorder, + DrawBackground, +} + +SemanticSizeKind :: enum { + FitText, + Exact, + ChildrenSum, + Fill, + PercentOfParent, +} + +SemanticSize :: struct { + kind: SemanticSizeKind, + value: int, +} + +Axis :: enum { + Horizontal = 0, + Vertical = 1, +} + +Box :: struct { + first: ^Box, + last: ^Box, + next: ^Box, + prev: ^Box, + parent: ^Box, + + key: Key, + last_interacted_index: int, + + flags: bit_set[Flag], + + label: string, + + axis: Axis, + semantic_size: [2]SemanticSize, + computed_size: [2]int, + + computed_pos: [2]int +} + +init :: proc() { + if persistent == nil { + persistent = make(map[Key]^Box); + } + + if clips == nil { + clips = make([dynamic]Rect); + } + + root = new(Box); + root.key = gen_key("root", 69); + current_parent = root; +} + +gen_key :: proc(label: string, value: int) -> Key { + key_label := "" + if current_parent == nil || len(current_parent.key.label) < 1 { + key_label = strings.clone(label); + } else { + key_label = fmt.aprintf("%s:%s", current_parent.key.label, label); + } + + return Key { + label = key_label, + value = value, + }; +} + +make_box :: proc(key: Key, label: string, flags: bit_set[Flag], axis: Axis, semantic_size: [2]SemanticSize) -> ^Box { + box: ^Box = nil; + + if cached_box, exists := persistent[key]; exists { + if cached_box.last_interacted_index < current_interaction_index { + old_cached_box := persistent[key]; + free(old_cached_box); + box = new(Box); + + persistent[key] = box; + } else { + box = cached_box; + } + } else { + box = new(Box); + persistent[key] = box; + } + + box.key = key; + box.label = label; + + box.first = nil; + box.last = nil; + box.next = nil; + box.prev = current_parent.last; + box.parent = current_parent; + box.flags = flags; + box.axis = axis; + box.semantic_size = semantic_size; + box.computed_pos = {}; + box.computed_size = {}; + + if current_parent.last != nil { + current_parent.last.next = box; + } + if current_parent.first == nil { + current_parent.first = box; + } + + current_parent.last = box; + + return box; +} + +make_semantic_size :: proc(kind: SemanticSizeKind, value: int = 0) -> SemanticSize { + return SemanticSize { + kind = kind, + value = value + }; +} + +FitText :[2]SemanticSize: { + SemanticSize { + kind = .FitText, + }, + SemanticSize { + kind = .FitText, + } +}; + +ChildrenSum :[2]SemanticSize: { + SemanticSize { + kind = .ChildrenSum, + }, + SemanticSize { + kind = .ChildrenSum, + } +}; + +push_box :: proc(label: string, flags: bit_set[Flag], axis: Axis = .Horizontal, semantic_size: [2]SemanticSize = FitText, value: int = 0) -> ^Box { + key := gen_key(label, value); + box := make_box(key, label, flags, axis, semantic_size); + + return box; +} + +push_parent :: proc(box: ^Box) { + current_parent = box; +} + +pop_parent :: proc() { + if current_parent.parent != nil { + current_parent = current_parent.parent; + } +} + +test_box :: proc(box: ^Box) -> Interaction { + return Interaction { + clicked = false, + }; +} + +delete_box_children :: proc(box: ^Box, keep_persistent: bool = true) { + iter := BoxIter { box.first, 0 }; + + for box in iterate_box(&iter) { + delete_box(box, keep_persistent); + } +} + +delete_box :: proc(box: ^Box, keep_persistent: bool = true) { + delete_box_children(box, keep_persistent); + + if !(box.key in persistent) || !keep_persistent { + delete(box.key.label); + free(box); + } +} + +prune :: proc() { + iter := BoxIter { root.first, 0 }; + + for box in iterate_box(&iter) { + delete_box_children(box); + + if !(box.key in persistent) { + free(box); + } + } + + root_key := root.key; + root^ = { + key = root_key, + }; + current_parent = root; +} + +ancestor_size :: proc(box: ^Box, axis: Axis) -> int { + if box == nil || box.parent == nil { + return root.computed_size[axis]; + } + + switch box.parent.semantic_size[axis].kind { + case .FitText: fallthrough + case .Exact: fallthrough + case .Fill: fallthrough + case .PercentOfParent: + return box.parent.computed_size[axis]; + + case .ChildrenSum: + return ancestor_size(box.parent, axis); + } + + return 1337; +} + +compute_layout :: proc(canvas_size: [2]int, font_width: int, font_height: int, box: ^Box = root) { + if box == nil { return; } + + axis := Axis.Horizontal; + if box.parent != nil { + axis = box.parent.axis; + box.computed_pos = box.parent.computed_pos; + } + + if box.prev != nil { + box.computed_pos[axis] = box.prev.computed_pos[axis] + box.prev.computed_size[axis]; + } + + compute_children := true; + if box == root { + box.computed_size = canvas_size; + } else { + switch box.semantic_size.x.kind { + case .FitText: { + // TODO: don't use hardcoded font size + box.computed_size.x = len(box.label) * font_width; + } + case .Exact: { + box.computed_size.x = box.semantic_size.x.value; + } + case .ChildrenSum: { + compute_children = false; + box.computed_size.x = 0; + + iter := BoxIter { box.first, 0 }; + for child in iterate_box(&iter) { + compute_layout(canvas_size, font_width, font_height, child); + + switch box.axis { + case .Horizontal: { + box.computed_size.x += child.computed_size.x; + } + case .Vertical: { + if child.computed_size.x > box.computed_size.x { + box.computed_size.x = child.computed_size.x; + } + } + } + } + } + case .Fill: { + } + case .PercentOfParent: { + box.computed_size.x = int(f32(ancestor_size(box, .Horizontal))*(f32(box.semantic_size.x.value)/100.0)); + } + } + switch box.semantic_size.y.kind { + case .FitText: { + // TODO: don't use hardcoded font size + box.computed_size.y = font_height; + } + case .Exact: { + box.computed_size.y = box.semantic_size.y.value; + } + case .ChildrenSum: { + compute_children = false; + should_post_compute := false; + number_of_fills := 0; + box.computed_size.y = 0; + parent_size := ancestor_size(box, .Vertical); + + iter := BoxIter { box.first, 0 }; + for child in iterate_box(&iter) { + compute_layout(canvas_size, font_width, font_height, child); + + if child.semantic_size.y.kind == .Fill { + number_of_fills += 1; + should_post_compute := true; + } + + switch box.axis { + case .Horizontal: { + if child.computed_size.y > box.computed_size.y { + box.computed_size.y = child.computed_size.y; + } + } + case .Vertical: { + box.computed_size.y += child.computed_size.y; + } + } + } + + // if should_post_compute { + // iter := BoxIter { box.first, 0 }; + // for child in iterate_box(&iter) { + // if compute_layout(canvas_size, font_width, font_height, child) { + // child.computed_size.y = (parent_size - box.computed_size.y) / number_of_fills; + // } + // } + // } + } + case .Fill: { + } + case .PercentOfParent: { + box.computed_size.y = int(f32(ancestor_size(box, .Vertical))*(f32(box.semantic_size.y.value)/100.0)); + } + } + } + + if compute_children { + iter := BoxIter { box.first, 0 }; + should_post_compute := false; + child_size: [2]int = {0,0}; + + // NOTE: the number of fills for the opposite axis of this box needs to be 1 + // because it will never get incremented in the loop below and cause a divide by zero + // and the number of fills for the axis of the box needs to start at zero or else it will + // be n+1 causing incorrect sizes + number_of_fills: [2]int = {1,1}; + number_of_fills[box.axis] = 0; + + our_size := box.computed_size; + + for child in iterate_box(&iter) { + compute_layout(canvas_size, font_width, font_height, child); + if child.semantic_size[box.axis].kind == .Fill { + number_of_fills[box.axis] += 1; + should_post_compute = true; + } else { + child_size[box.axis] += child.computed_size[box.axis]; + } + } + + if should_post_compute { + iter := BoxIter { box.first, 0 }; + for child in iterate_box(&iter) { + for axis in 0..<2 { + if child.semantic_size[axis].kind == .Fill { + if child_size[axis] >= our_size[axis] { + child.computed_size[axis] = our_size[axis] / number_of_fills[axis]; + } else { + child.computed_size[axis] = (our_size[axis] - child_size[axis]) / number_of_fills[axis]; + } + } + } + + compute_layout(canvas_size, font_width, font_height, child); + } + } + } +} + +push_clip :: proc(pos: [2]int, size: [2]int) { + rect := Rect { pos, size }; + + if len(clips) > 0 { + parent_rect := clips[len(clips)-1]; + + if rect.pos.x >= parent_rect.pos.x && + rect.pos.y >= parent_rect.pos.y && + rect.pos.x < parent_rect.pos.x + parent_rect.size.x && + rect.pos.y < parent_rect.pos.y + parent_rect.size.y + { + //rect.pos.x = math.max(rect.pos.x, parent_rect.pos.x); + //rect.pos.y = math.max(rect.pos.y, parent_rect.pos.y); + + rect.size.x = math.min(rect.pos.x + rect.size.x, parent_rect.pos.x + parent_rect.size.x); + rect.size.y = math.min(rect.pos.y + rect.size.y, parent_rect.pos.y + parent_rect.size.y); + + rect.size.x -= rect.pos.x; + rect.size.y -= rect.pos.y; + } else { + rect = parent_rect; + } + } + + raylib.BeginScissorMode( + i32(rect.pos.x), + i32(rect.pos.y), + i32(rect.size.x), + i32(rect.size.y) + ); + + append(&clips, rect); +} + +pop_clip :: proc() { + raylib.EndScissorMode(); + + if len(clips) > 0 { + rect := pop(&clips); + + raylib.BeginScissorMode( + i32(rect.pos.x), + i32(rect.pos.y), + i32(rect.size.x), + i32(rect.size.y) + ); + } +} + +draw :: proc(font: raylib.Font, font_width: int, font_height: int, box: ^Box = root) { + if box == nil { return; } + + // NOTE: for some reason if you place this right before the + // for loop, the clipping only works for the first child. Compiler bug? + push_clip(box.computed_pos, box.computed_size); + defer pop_clip(); + + if .DrawBorder in box.flags { + raylib.DrawRectangleLines( + i32(box.computed_pos.x), + i32(box.computed_pos.y), + i32(box.computed_size.x), + i32(box.computed_size.y), + theme.get_palette_raylib_color(.Background4) + ); + } + if .DrawBackground in box.flags { + raylib.DrawRectangle( + i32(box.computed_pos.x), + i32(box.computed_pos.y), + i32(box.computed_size.x), + i32(box.computed_size.y), + theme.get_palette_raylib_color(.Background1) + ); + } + if .DrawText in box.flags { + for codepoint, index in box.label { + raylib.DrawTextCodepoint( + font, + rune(codepoint), + raylib.Vector2 { f32(box.computed_pos.x + index * font_width), f32(box.computed_pos.y) }, + f32(font_height), + theme.get_palette_raylib_color(.Foreground1) + ); + } + } + + iter := BoxIter { box.first, 0 }; + for child in iterate_box(&iter) { + draw(font, font_width, font_height, child); + } +} + +BoxIter :: struct { + box: ^Box, + index: int, +} + +iterate_box :: proc(iter: ^BoxIter, print: bool = false) -> (box: ^Box, idx: int, cond: bool) { + if iter.box == nil { + return nil, iter.index, false; + } + + box = iter.box; + idx = iter.index; + + iter.box = iter.box.next; + iter.index += 1; + + return box, iter.index, true; +} + +debug_print :: proc(box: ^Box, depth: int = 0) { + iter := BoxIter { box.first, 0 }; + + for box, idx in iterate_box(&iter, true) { + for _ in 0..<(depth*6) { + fmt.print("-"); + } + if depth > 0 { + fmt.print(">"); + } + fmt.println(idx, "Box", box.label, "#", box.key.label, "first", transmute(rawptr)box.first, "parent", transmute(rawptr)box.parent, box.computed_size); + debug_print(box, depth+1); + } + + if depth == 0 { + fmt.println("persistent"); + for p in persistent { + fmt.println(p); + } + } +} + +spacer :: proc(label: string) -> ^Box { + return push_box(label, {}, semantic_size = {make_semantic_size(.Fill, 0), make_semantic_size(.Fill, 0)}); +} + +button :: proc(label: string) -> Interaction { + box := push_box(label, {.Clickable, .Hoverable, .DrawText, .DrawBorder, .DrawBackground}); + + return test_box(box); +} + +two_buttons_test :: proc(label1: string, label2: string) { + push_parent(push_box("two_button_container", {.DrawBorder}, .Vertical, semantic_size = ChildrenSum)); + + button("1"); + button("2"); + button(label1); + button(label2); + button("5"); + button("6"); + + push_parent(push_box("two_button_container_inner", {.DrawBorder}, semantic_size = ChildrenSum)); + button("second first button"); + { + push_parent(push_box("two_button_container_inner", {.DrawBorder}, .Vertical, semantic_size = {make_semantic_size(.PercentOfParent, 50), { .Exact, 256}})); + defer pop_parent(); + + button("first inner most button"); + button("inner_button2"); + button("inner_button3"); + } + button("inner_button3"); + + pop_parent(); + button("Help me I'm falling"); + pop_parent(); +}