From 99dea9e863785cf8243e79e699dd94c8f485a27f Mon Sep 17 00:00:00 2001 From: Patrick Cleavelin Date: Fri, 19 Jan 2024 22:01:16 -0600 Subject: [PATCH 1/6] fix background being draw on top of border --- src/main.odin | 71 ++++++++++++++++++++++++++----------------------- src/ui/imm.odin | 29 +++++++++++++------- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/src/main.odin b/src/main.odin index 08804ce..82a0ddc 100644 --- a/src/main.odin +++ b/src/main.odin @@ -212,6 +212,13 @@ load_plugin :: proc(info: os.File_Info, in_err: os.Errno, state: rawptr) -> (err return in_err, skip_dir; } +ui_font_width :: proc() -> i32 { + return i32(state.source_font_width); +} +ui_font_height :: proc() -> i32 { + return i32(state.source_font_height); +} + main :: proc() { state = State { ctx = context, @@ -224,6 +231,12 @@ main :: proc() { highlighters = make(map[string]plugin.OnColorBufferProc), hooks = make(map[plugin.Hook][dynamic]plugin.OnHookProc), }; + + ui_ctx := ui.Context { + text_width = ui_font_width, + text_height = ui_font_height, + } + state.plugin_vtable = plugin.Plugin { state = cast(rawptr)&state, register_hook = proc "c" (hook: plugin.Hook, on_hook: plugin.OnHookProc) { @@ -737,9 +750,9 @@ main :: proc() { } } - raylib.InitWindow(640, 480, "odin_editor - [back to basics]"); + raylib.InitWindow(640, 480, "odin_editor - [now with more ui]"); raylib.SetWindowState({ .WINDOW_RESIZABLE, .VSYNC_HINT }); - raylib.SetTargetFPS(60); + raylib.SetTargetFPS(144); raylib.SetExitKey(.KEY_NULL); // TODO: don't just hard code a MacOS font path @@ -772,13 +785,6 @@ main :: proc() { raylib.ClearBackground(theme.get_palette_raylib_color(.Background)); - // TODO: be more granular in /what/ is being draw by the plugin - for plugin in state.plugins { - if plugin.on_initialize != nil { - //plugin.on_draw(plugin.plugin); - } - } - core.draw_file_buffer(&state, buffer, 32, state.source_font_height, state.font); { @@ -789,7 +795,7 @@ main :: proc() { 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 { + if ui.label("Editor").clicked { fmt.println("you clicked the button"); } @@ -802,23 +808,12 @@ main :: proc() { } ); - if ui.button("Buffers").clicked { + if ui.label("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_box("deezbuffer", {}, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.Fill, 0)}); } { 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)})); @@ -837,16 +832,26 @@ main :: proc() { 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.push_parent(ui.push_box("center info", {}, semantic_size = ui.ChildrenSum)); + defer ui.pop_parent(); + + line_info_text := raylib.TextFormat( + //"Line: %d, Col: %d, Len: %d --- Slice Index: %d, Content Index: %d --- Frame Time: %fms", + "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.label(string(line_info_text)); + } + + ui.spacer("frame time spacer"); + frame_time := (60.0/f32(raylib.GetFPS())) * 10; + frame_time_text := raylib.TextFormat("frame time: %fms", frame_time); + ui.label(string(frame_time_text)); } } diff --git a/src/ui/imm.odin b/src/ui/imm.odin index 7ea021c..aa8bf14 100644 --- a/src/ui/imm.odin +++ b/src/ui/imm.odin @@ -7,6 +7,11 @@ import "vendor:raylib" import "../theme" +Context :: struct { + text_width: proc() -> i32, + text_height: proc() -> i32, +} + root: ^Box = nil; current_parent: ^Box = nil; persistent: map[Key]^Box = nil; @@ -454,15 +459,6 @@ draw :: proc(font: raylib.Font, font_width: int, font_height: int, box: ^Box = r 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), @@ -472,6 +468,15 @@ draw :: proc(font: raylib.Font, font_width: int, font_height: int, box: ^Box = r theme.get_palette_raylib_color(.Background1) ); } + 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 .DrawText in box.flags { for codepoint, index in box.label { raylib.DrawTextCodepoint( @@ -535,6 +540,12 @@ spacer :: proc(label: string) -> ^Box { return push_box(label, {}, semantic_size = {make_semantic_size(.Fill, 0), make_semantic_size(.Fill, 0)}); } +label :: proc(label: string) -> Interaction { + box := push_box(label, {.DrawText}); + + return test_box(box); +} + button :: proc(label: string) -> Interaction { box := push_box(label, {.Clickable, .Hoverable, .DrawText, .DrawBorder, .DrawBackground}); From 96338e29248ebc3ae0ae3dd6043c04d80e3fa971 Mon Sep 17 00:00:00 2001 From: Patrick Cleavelin Date: Thu, 25 Jan 2024 16:59:08 -0600 Subject: [PATCH 2/6] HUGE refactor to SDL + SDL_TTF rendering --- flake.nix | 2 + plugin-rs-bindings/src/lib.rs | 353 ++++++++++----- plugins/buffer_search/plugin.odin | 25 +- plugins/grep/src/lib.rs | 20 +- src/core/core.odin | 23 +- src/core/file_buffer.odin | 18 +- src/core/gfx.odin | 155 +++++++ src/main.odin | 731 ++++++++++++++++++------------ src/plugin/plugin.odin | 355 ++++++++++----- src/theme/theme.odin | 13 +- src/ui/imm.odin | 479 ++++++++++++-------- src/ui/ui.odin | 289 ++++++------ 12 files changed, 1577 insertions(+), 886 deletions(-) create mode 100644 src/core/gfx.odin diff --git a/flake.nix b/flake.nix index 5c7ff05..2dbf713 100755 --- a/flake.nix +++ b/flake.nix @@ -59,6 +59,8 @@ fixed-odin local-rust rust-analyzer + SDL2 + SDL2_ttf darwin.apple_sdk.frameworks.CoreData darwin.apple_sdk.frameworks.Kernel darwin.apple_sdk.frameworks.CoreVideo diff --git a/plugin-rs-bindings/src/lib.rs b/plugin-rs-bindings/src/lib.rs index 4018c0f..7e4a78a 100644 --- a/plugin-rs-bindings/src/lib.rs +++ b/plugin-rs-bindings/src/lib.rs @@ -361,119 +361,246 @@ pub enum Hook { #[repr(i32)] pub enum Key { - KeyNull = 0, // Key: NULL, used for no key pressed - // Alphanumeric keys - Apostrophe = 39, // key: ' - Comma = 44, // Key: , - Minus = 45, // Key: - - Period = 46, // Key: . - Slash = 47, // Key: / - Zero = 48, // Key: 0 - One = 49, // Key: 1 - Two = 50, // Key: 2 - Three = 51, // Key: 3 - Four = 52, // Key: 4 - Five = 53, // Key: 5 - Six = 54, // Key: 6 - Seven = 55, // Key: 7 - Eight = 56, // Key: 8 - Nine = 57, // Key: 9 - Semicolon = 59, // Key: ; - Equal = 61, // Key: = - A = 65, // Key: A | a - B = 66, // Key: B | b - C = 67, // Key: C | c - D = 68, // Key: D | d - E = 69, // Key: E | e - F = 70, // Key: F | f - G = 71, // Key: G | g - H = 72, // Key: H | h - I = 73, // Key: I | i - J = 74, // Key: J | j - K = 75, // Key: K | k - L = 76, // Key: L | l - M = 77, // Key: M | m - N = 78, // Key: N | n - O = 79, // Key: O | o - P = 80, // Key: P | p - Q = 81, // Key: Q | q - R = 82, // Key: R | r - S = 83, // Key: S | s - T = 84, // Key: T | t - U = 85, // Key: U | u - V = 86, // Key: V | v - W = 87, // Key: W | w - X = 88, // Key: X | x - Y = 89, // Key: Y | y - Z = 90, // Key: Z | z - LeftBracket = 91, // Key: [ - Backslash = 92, // Key: '\' - RightBracket = 93, // Key: ] - Grave = 96, // Key: ` - // Function keys - Space = 32, // Key: Space - Escape = 256, // Key: Esc - Enter = 257, // Key: Enter - Tab = 258, // Key: Tab - Backspace = 259, // Key: Backspace - Insert = 260, // Key: Ins - Delete = 261, // Key: Del - Right = 262, // Key: Cursor right - Left = 263, // Key: Cursor left - Down = 264, // Key: Cursor down - Up = 265, // Key: Cursor up - PageUp = 266, // Key: Page up - PageDown = 267, // Key: Page down - Home = 268, // Key: Home - End = 269, // Key: End - CapsLock = 280, // Key: Caps lock - ScrollLock = 281, // Key: Scroll down - NumLock = 282, // Key: Num lock - PrintScreen = 283, // Key: Print screen - Pause = 284, // Key: Pause - F1 = 290, // Key: F1 - F2 = 291, // Key: F2 - F3 = 292, // Key: F3 - F4 = 293, // Key: F4 - F5 = 294, // Key: F5 - F6 = 295, // Key: F6 - F7 = 296, // Key: F7 - F8 = 297, // Key: F8 - F9 = 298, // Key: F9 - F10 = 299, // Key: F10 - F11 = 300, // Key: F11 - F12 = 301, // Key: F12 - LeftShift = 340, // Key: Shift left - LeftControl = 341, // Key: Control left - LeftAlt = 342, // Key: Alt left - LeftSuper = 343, // Key: Super left - RightShift = 344, // Key: Shift right - RightControl = 345, // Key: Control right - RightAlt = 346, // Key: Alt right - RightSuper = 347, // Key: Super right - KbMenu = 348, // Key: KB menu - // Keypad keys - Kp0 = 320, // Key: Keypad 0 - Kp1 = 321, // Key: Keypad 1 - Kp2 = 322, // Key: Keypad 2 - Kp3 = 323, // Key: Keypad 3 - Kp4 = 324, // Key: Keypad 4 - Kp5 = 325, // Key: Keypad 5 - Kp6 = 326, // Key: Keypad 6 - Kp7 = 327, // Key: Keypad 7 - Kp8 = 328, // Key: Keypad 8 - Kp9 = 329, // Key: Keypad 9 - KpDecimal = 330, // Key: Keypad . - KpDivide = 331, // Key: Keypad / - KpMultiply = 332, // Key: Keypad * - KpSubtract = 333, // Key: Keypad - - KpAdd = 334, // Key: Keypad + - KpEnter = 335, // Key: Keypad Enter - KpEqual = 336, // Key: Keypad = - // Android key buttons - Back = 4, // Key: Android back button - VolumeUp = 24, // Key: Android volume up button - VolumeDown = 25, // Key: Android volume down button + UNKNOWN = 0, + Enter = 13, + ESCAPE = 27, + BACKSPACE = 8, + TAB = 9, + Space = 32, + EXCLAIM = 33, + QUOTEDBL = 34, + HASH = 35, + PERCENT = 37, + DOLLAR = 36, + AMPERSAND = 38, + QUOTE = 39, + LEFTPAREN = 40, + RIGHTPAREN = 41, + ASTERISK = 42, + PLUS = 43, + COMMA = 44, + MINUS = 45, + PERIOD = 46, + SLASH = 47, + NUM0 = 48, + NUM1 = 49, + NUM2 = 50, + NUM3 = 51, + NUM4 = 52, + NUM5 = 53, + NUM6 = 54, + NUM7 = 55, + NUM8 = 56, + NUM9 = 57, + COLON = 58, + SEMICOLON = 59, + LESS = 60, + EQUAL = 61, + GREATER = 62, + QUESTION = 63, + AT = 64, + LEFTBRACKET = 91, + BACKSLASH = 92, + RIGHTBRACKET = 93, + CARET = 94, + UNDERSCORE = 95, + BACKQUOTE = 96, + A = 97, + B = 98, + C = 99, + D = 100, + E = 101, + F = 102, + G = 103, + H = 104, + I = 105, + J = 106, + K = 107, + L = 108, + M = 109, + N = 110, + O = 111, + P = 112, + Q = 113, + R = 114, + S = 115, + T = 116, + U = 117, + V = 118, + W = 119, + X = 120, + Y = 121, + Z = 122, + CAPSLOCK = 1073741881, + F1 = 1073741882, + F2 = 1073741883, + F3 = 1073741884, + F4 = 1073741885, + F5 = 1073741886, + F6 = 1073741887, + F7 = 1073741888, + F8 = 1073741889, + F9 = 1073741890, + F10 = 1073741891, + F11 = 1073741892, + F12 = 1073741893, + PRINTSCREEN = 1073741894, + SCROLLLOCK = 1073741895, + PAUSE = 1073741896, + INSERT = 1073741897, + HOME = 1073741898, + PAGEUP = 1073741899, + DELETE = 127, + END = 1073741901, + PAGEDOWN = 1073741902, + RIGHT = 1073741903, + LEFT = 1073741904, + DOWN = 1073741905, + UP = 1073741906, + NUMLOCKCLEAR = 1073741907, + KpDivide = 1073741908, + KpMultiply = 1073741909, + KpMinus = 1073741910, + KpPlus = 1073741911, + KpEnter = 1073741912, + Kp1 = 1073741913, + Kp2 = 1073741914, + Kp3 = 1073741915, + Kp4 = 1073741916, + Kp5 = 1073741917, + Kp6 = 1073741918, + Kp7 = 1073741919, + Kp8 = 1073741920, + Kp9 = 1073741921, + Kp0 = 1073741922, + KpPeriod = 1073741923, + APPLICATION = 1073741925, + POWER = 1073741926, + KpEquals = 1073741927, + F13 = 1073741928, + F14 = 1073741929, + F15 = 1073741930, + F16 = 1073741931, + F17 = 1073741932, + F18 = 1073741933, + F19 = 1073741934, + F20 = 1073741935, + F21 = 1073741936, + F22 = 1073741937, + F23 = 1073741938, + F24 = 1073741939, + EXECUTE = 1073741940, + HELP = 1073741941, + MENU = 1073741942, + SELECT = 1073741943, + STOP = 1073741944, + AGAIN = 1073741945, + UNDO = 1073741946, + CUT = 1073741947, + COPY = 1073741948, + PASTE = 1073741949, + FIND = 1073741950, + MUTE = 1073741951, + VOLUMEUP = 1073741952, + VOLUMEDOWN = 1073741953, + KpComma = 1073741957, + KpEqualsas400 = 1073741958, + ALTERASE = 1073741977, + SYSREQ = 1073741978, + CANCEL = 1073741979, + CLEAR = 1073741980, + PRIOR = 1073741981, + RETURN2 = 1073741982, + SEPARATOR = 1073741983, + OUT = 1073741984, + OPER = 1073741985, + CLEARAGAIN = 1073741986, + CRSEL = 1073741987, + EXSEL = 1073741988, + Kp00 = 1073742000, + Kp000 = 1073742001, + THOUSANDSSEPARATOR = 1073742002, + DECIMALSEPARATOR = 1073742003, + CURRENCYUNIT = 1073742004, + CURRENCYSUBUNIT = 1073742005, + KpLeftparen = 1073742006, + KpRightparen = 1073742007, + KpLeftbrace = 1073742008, + KpRightbrace = 1073742009, + KpTab = 1073742010, + KpBackspace = 1073742011, + KpA = 1073742012, + KpB = 1073742013, + KpC = 1073742014, + KpD = 1073742015, + KpE = 1073742016, + KpF = 1073742017, + KpXor = 1073742018, + KpPower = 1073742019, + KpPercent = 1073742020, + KpLess = 1073742021, + KpGreater = 1073742022, + KpAmpersand = 1073742023, + KpDblampersand = 1073742024, + KpVerticalbar = 1073742025, + KpDblverticalbar = 1073742026, + KpColon = 1073742027, + KpHash = 1073742028, + KpSpace = 1073742029, + KpAt = 1073742030, + KpExclam = 1073742031, + KpMemstore = 1073742032, + KpMemrecall = 1073742033, + KpMemclear = 1073742034, + KpMemadd = 1073742035, + KpMemsubtract = 1073742036, + KpMemmultiply = 1073742037, + KpMemdivide = 1073742038, + KpPlusminus = 1073742039, + KpClear = 1073742040, + KpClearentry = 1073742041, + KpBinary = 1073742042, + KpOctal = 1073742043, + KpDecimal = 1073742044, + KpHexadecimal = 1073742045, + LCTRL = 1073742048, + LSHIFT = 1073742049, + LALT = 1073742050, + LGUI = 1073742051, + RCTRL = 1073742052, + RSHIFT = 1073742053, + RALT = 1073742054, + RGUI = 1073742055, + MODE = 1073742081, + AUDIONEXT = 1073742082, + AUDIOPREV = 1073742083, + AUDIOSTOP = 1073742084, + AUDIOPLAY = 1073742085, + AUDIOMUTE = 1073742086, + MEDIASELECT = 1073742087, + WWW = 1073742088, + MAIL = 1073742089, + CALCULATOR = 1073742090, + COMPUTER = 1073742091, + AcSearch = 1073742092, + AcHome = 1073742093, + AcBack = 1073742094, + AcForward = 1073742095, + AcStop = 1073742096, + AcRefresh = 1073742097, + AcBookmarks = 1073742098, + BRIGHTNESSDOWN = 1073742099, + BRIGHTNESSUP = 1073742100, + DISPLAYSWITCH = 1073742101, + KBDILLUMTOGGLE = 1073742102, + KBDILLUMDOWN = 1073742103, + KBDILLUMUP = 1073742104, + EJECT = 1073742105, + SLEEP = 1073742106, + APP1 = 1073742107, + APP2 = 1073742108, + AUDIOREWIND = 1073742109, + AUDIOFASTFORWARD = 1073742110, } #[repr(i32)] diff --git a/plugins/buffer_search/plugin.odin b/plugins/buffer_search/plugin.odin index 41d71fc..3bc992c 100644 --- a/plugins/buffer_search/plugin.odin +++ b/plugins/buffer_search/plugin.odin @@ -4,7 +4,6 @@ package buffer_search; import "core:runtime" import "core:fmt" import "core:path/filepath" -import "vendor:raylib" import p "../../src/plugin" import "../../src/theme" @@ -109,24 +108,24 @@ draw_buffer_window :: proc "c" (plugin: Plugin, win: rawptr) { source_font_width := plugin.get_font_width(); source_font_height := plugin.get_font_height(); - win_rec := raylib.Rectangle { - x = f32(screen_width/8), - y = f32(screen_height/8), - width = f32(screen_width - screen_width/4), - height = f32(screen_height - screen_height/4), + win_rec := [4]f32 { + f32(screen_width/8), + f32(screen_height/8), + f32(screen_width - screen_width/4), + f32(screen_height - screen_height/4), }; plugin.draw_rect( i32(win_rec.x), i32(win_rec.y), - i32(win_rec.width), - i32(win_rec.height), + i32(win_rec.z), + i32(win_rec.w), .Background4 ); - win_margin := raylib.Vector2 { f32(source_font_width), f32(source_font_height) }; + win_margin := [2]f32 { f32(source_font_width), f32(source_font_height) }; - buffer_prev_width := (win_rec.width - win_margin.x*2) / 2; - buffer_prev_height := win_rec.height - win_margin.y*2; + buffer_prev_width := (win_rec.z - win_margin.x*2) / 2; + buffer_prev_height := win_rec.w - win_margin.y*2; glyph_buffer_width := int(buffer_prev_width) / source_font_width - 1; glyph_buffer_height := int(buffer_prev_height) / source_font_height; @@ -134,7 +133,7 @@ draw_buffer_window :: proc "c" (plugin: Plugin, win: rawptr) { directory := string(plugin.get_current_directory()); plugin.draw_rect( - i32(win_rec.x + win_rec.width / 2), + i32(win_rec.x + win_rec.z / 2), i32(win_rec.y + win_margin.y), i32(buffer_prev_width), i32(buffer_prev_height), @@ -151,7 +150,7 @@ draw_buffer_window :: proc "c" (plugin: Plugin, win: rawptr) { if index == win.selected_index { plugin.draw_buffer_from_index( index, - int(win_rec.x + win_margin.x + win_rec.width / 2), + int(win_rec.x + win_margin.x + win_rec.z / 2), int(win_rec.y + win_margin.y), glyph_buffer_width, glyph_buffer_height, diff --git a/plugins/grep/src/lib.rs b/plugins/grep/src/lib.rs index 8ce5a34..74e5c3b 100644 --- a/plugins/grep/src/lib.rs +++ b/plugins/grep/src/lib.rs @@ -8,7 +8,7 @@ use std::{ }; use grep::{ - regex::{RegexMatcherBuilder}, + regex::RegexMatcherBuilder, searcher::{BinaryDetection, SearcherBuilder, Sink, SinkError}, }; use plugin_rs_bindings::{Buffer, Closure, Hook, InputMap, Key, PaletteColor, Plugin}; @@ -213,17 +213,19 @@ pub extern "C" fn OnInitialize(plugin: Plugin) { None => 0, }; - let index_threshold = std::cmp::max(max_mats_to_draw-4, 0) as usize; + if match_count > 0 { + let index_threshold = std::cmp::max(max_mats_to_draw-4, 0) as usize; - if window.selected_match < match_count-1 { - window.selected_match += 1; + if window.selected_match < match_count-1 { + window.selected_match += 1; - if window.selected_match - window.top_index > index_threshold { - window.top_index += 1; + if window.selected_match - window.top_index > index_threshold { + window.top_index += 1; + } + } else { + window.selected_match = 0; + window.top_index = 0; } - } else { - window.selected_match = 0; - window.top_index = 0; } } }), "move selection down\0".as_ptr()); diff --git a/src/core/core.odin b/src/core/core.odin index 9c0fe01..d5156ee 100644 --- a/src/core/core.odin +++ b/src/core/core.odin @@ -2,7 +2,7 @@ package core import "core:runtime" import "core:fmt" -import "vendor:raylib" +import "vendor:sdl2" import "../plugin" @@ -42,12 +42,13 @@ close_window_and_free :: proc(state: ^State) { State :: struct { ctx: runtime.Context, + sdl_renderer: ^sdl2.Renderer, + font_atlas: FontAtlas, mode: Mode, should_close: bool, screen_height: int, screen_width: int, - font: raylib.Font, directory: string, @@ -86,14 +87,14 @@ Action :: struct { description: string, } InputMap :: struct { - key_actions: map[raylib.KeyboardKey]Action, - ctrl_key_actions: map[raylib.KeyboardKey]Action, + key_actions: map[plugin.Key]Action, + ctrl_key_actions: map[plugin.Key]Action, } new_input_map :: proc() -> InputMap { input_map := InputMap { - key_actions = make(map[raylib.KeyboardKey]Action), - ctrl_key_actions = make(map[raylib.KeyboardKey]Action), + key_actions = make(map[plugin.Key]Action), + ctrl_key_actions = make(map[plugin.Key]Action), } return input_map; @@ -106,7 +107,7 @@ delete_input_map :: proc(input_map: ^InputMap) { // NOTE(pcleavelin): might be a bug in the compiler where it can't coerce // `EditorAction` to `InputGroup` when given as a proc parameter, that is why there // are two functions -register_plugin_key_action_single :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, action: PluginEditorAction, description: string = "") { +register_plugin_key_action_single :: proc(input_map: ^InputMap, key: plugin.Key, action: PluginEditorAction, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered fmt.eprintln("plugin key already registered with single action", key); @@ -118,7 +119,7 @@ register_plugin_key_action_single :: proc(input_map: ^InputMap, key: raylib.Keyb }; } -register_key_action_single :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, action: EditorAction, description: string = "") { +register_key_action_single :: proc(input_map: ^InputMap, key: plugin.Key, action: EditorAction, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered fmt.eprintln("key already registered with single action", key); @@ -130,7 +131,7 @@ register_key_action_single :: proc(input_map: ^InputMap, key: raylib.KeyboardKey }; } -register_key_action_group :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, input_group: InputGroup, description: string = "") { +register_key_action_group :: proc(input_map: ^InputMap, key: plugin.Key, input_group: InputGroup, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered fmt.eprintln("key already registered with single action", key); @@ -142,7 +143,7 @@ register_key_action_group :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, }; } -register_ctrl_key_action_single :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, action: EditorAction, description: string = "") { +register_ctrl_key_action_single :: proc(input_map: ^InputMap, key: plugin.Key, action: EditorAction, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered fmt.eprintln("key already registered with single action", key); @@ -154,7 +155,7 @@ register_ctrl_key_action_single :: proc(input_map: ^InputMap, key: raylib.Keyboa }; } -register_ctrl_key_action_group :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, input_group: InputGroup, description: string = "") { +register_ctrl_key_action_group :: proc(input_map: ^InputMap, key: plugin.Key, input_group: InputGroup, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered fmt.eprintln("key already registered with single action", key); diff --git a/src/core/file_buffer.odin b/src/core/file_buffer.odin index 7c9b347..b7ea8fb 100644 --- a/src/core/file_buffer.odin +++ b/src/core/file_buffer.odin @@ -8,7 +8,6 @@ import "core:math" import "core:slice" import "core:runtime" import "core:strings" -import "vendor:raylib" import "../theme" import "../plugin" @@ -752,7 +751,7 @@ update_glyph_buffer :: proc(buffer: ^FileBuffer) { } } -draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, font: raylib.Font, show_line_numbers: bool = true) { +draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, show_line_numbers: bool = true) { update_glyph_buffer(buffer); if highlighter, exists := state.highlighters[buffer.extension]; exists { highlighter(state.plugin_vtable, buffer); @@ -771,9 +770,11 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, fon // draw cursor if state.mode == .Normal { - raylib.DrawRectangle(i32(cursor_x), i32(cursor_y), i32(state.source_font_width), i32(state.source_font_height), theme.get_palette_raylib_color(.Background4)); + draw_rect(state, cursor_x, cursor_y, state.source_font_width, state.source_font_height, .Background4); + //raylib.DrawRectangle(i32(cursor_x), i32(cursor_y), i32(state.source_font_width), i32(state.source_font_height), theme.get_palette_raylib_color(.Background4)); } else if state.mode == .Insert { - raylib.DrawRectangle(i32(cursor_x), i32(cursor_y), i32(state.source_font_width), i32(state.source_font_height), theme.get_palette_raylib_color(.Green)); + draw_rect(state, cursor_x, cursor_y, state.source_font_width, state.source_font_height, .Green); + // raylib.DrawRectangle(i32(cursor_x), i32(cursor_y), i32(state.source_font_width), i32(state.source_font_height), theme.get_palette_raylib_color(.Green)); num_line_break := 0; line_length := 0; @@ -793,14 +794,16 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, fon cursor_x += line_length * state.source_font_width; } - raylib.DrawRectangle(i32(cursor_x), i32(cursor_y), i32(state.source_font_width), i32(state.source_font_height), theme.get_palette_raylib_color(.Blue)); + draw_rect(state, cursor_x, cursor_y, state.source_font_width, state.source_font_height, .Blue); + //raylib.DrawRectangle(i32(cursor_x), i32(cursor_y), i32(state.source_font_width), i32(state.source_font_height), theme.get_palette_raylib_color(.Blue)); } for j in 0.. FontAtlas { + free_font_atlas(state.font_atlas); + + font_height := i32(state.source_font_height*scale); + + atlas := FontAtlas { + // FIXME: check if this failed + font = ttf.OpenFont(path, font_height), + } + ttf.SetFontKerning(atlas.font, false); + ttf.SetFontStyle(atlas.font, ttf.STYLE_NORMAL); + ttf.SetFontOutline(atlas.font, 0); + + minx, maxx, miny, maxy: i32; + advanced: i32; + for char in start_char..=end_char { + ttf.GlyphMetrics32(atlas.font, char, &minx, &maxx, &miny, &maxy, &advanced); + + width := maxx-minx; + height := maxy+miny; + + if width > i32(atlas.max_width) { + atlas.max_width = int(width); + } + if height > i32(atlas.max_height) { + atlas.max_height = int(height); + } + + // if atlas.max_width%2 != 0 { + // atlas.max_width += 1; + // } + } + + font_width := i32(atlas.max_width); + font_height = i32(atlas.max_height); + state.source_font_width = int(font_width/scale);// int(font_width/scale); + state.source_font_height = int(font_height/scale);//int(font_height/scale); + //fmt.println("font_width:", font_width, "font height:", font_height); + //state.source_font_width = int(f32(font_width)/f32(scale)); + + temp_surface: ^sdl2.Surface; + sdl2.SetHint(sdl2.HINT_RENDER_SCALE_QUALITY, "2"); + // FIXME: check if this failed + font_surface := sdl2.CreateRGBSurface(0, font_width * (end_char-start_char + 1), font_height, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); + + rect: sdl2.Rect; + + white := sdl2.Color { 0xff, 0xff, 0xff, 0xff }; + for char, index in start_char..=end_char { + // ttf.GlyphMetrics32(atlas.font, char, &minx, &maxx, &miny, &maxy, &advanced); + + rect.x = i32(index) * font_width; + rect.y = 0;//-font_height/8; + + // FIXME: check if this failed + temp_surface = ttf.RenderGlyph32_Blended(atlas.font, char, white); + + src_rect := sdl2.Rect { + 0, + 0, + temp_surface.w, + temp_surface.h + }; + //fmt.println("char", char, src_rect.x, src_rect.y, src_rect.w, src_rect.h, atlas.max_width, atlas.max_height); + + // FIXME: check if this failed + sdl2.BlitSurface(temp_surface, &src_rect, font_surface, &rect); + sdl2.FreeSurface(temp_surface); + } + + // FIXME: check if this failed + atlas.texture = sdl2.CreateTextureFromSurface(state.sdl_renderer, font_surface); + sdl2.SetTextureScaleMode(atlas.texture, .Best); + // sdl2.SetTextureAlphaMod(atlas.texture, 0xff); + // sdl2.SetTextureBlendMode(atlas.texture, .BLEND); + return atlas; +} + +free_font_atlas :: proc(font_atlas: FontAtlas) { + if font_atlas.font != nil { + ttf.CloseFont(font_atlas.font); + } + if font_atlas.texture != nil { + sdl2.DestroyTexture(font_atlas.texture); + } +} + +draw_rect_outline :: proc(state: ^State, x,y,w,h: int, color: theme.PaletteColor) { + color := theme.get_palette_color(color); + + sdl2.SetRenderDrawColor(state.sdl_renderer, color.r, color.g, color.b, color.a); + sdl2.RenderDrawRect(state.sdl_renderer, &sdl2.Rect { i32(x), i32(y), i32(w), i32(h) }); +} + +draw_rect :: proc(state: ^State, x,y,w,h: int, color: theme.PaletteColor) { + color := theme.get_palette_color(color); + + sdl2.SetRenderDrawColor(state.sdl_renderer, color.r, color.g, color.b, color.a); + sdl2.RenderFillRect(state.sdl_renderer, &sdl2.Rect { i32(x), i32(y), i32(w), i32(h) }); +} + +draw_codepoint :: proc(state: ^State, codepoint: rune, x,y: int, color: theme.PaletteColor) { + color := theme.get_palette_color(color); + + if codepoint >= start_char && codepoint <= end_char { + codepoint := codepoint - start_char; + + src_rect := sdl2.Rect { + x = i32(codepoint) * i32(state.font_atlas.max_width), + y = 0, + w = i32(state.font_atlas.max_width), + h = i32(state.font_atlas.max_height), + }; + + dest_rect := sdl2.Rect { + x = i32(x), + y = i32(y), + w = i32(state.font_atlas.max_width/scale), + h = i32(state.font_atlas.max_height/scale), + }; + + sdl2.SetTextureColorMod(state.font_atlas.texture, color.r, color.g, color.b); + sdl2.RenderCopy(state.sdl_renderer, state.font_atlas.texture, &src_rect, &dest_rect); + } +} + +draw_text :: proc(state: ^State, text: string, x,y: int, color: theme.PaletteColor = .Foreground1) { + for char, idx in text { + if char < start_char || char > end_char { + draw_codepoint(state, '?', x + idx * state.source_font_width, y, color); + } else { + draw_codepoint(state, char, x + idx * state.source_font_width, y, color); + } + + } +} diff --git a/src/main.odin b/src/main.odin index 82a0ddc..266dbfd 100644 --- a/src/main.odin +++ b/src/main.odin @@ -8,7 +8,8 @@ import "core:runtime" import "core:fmt" import "core:mem" import "core:slice" -import "vendor:raylib" +import "vendor:sdl2" +import "vendor:sdl2/ttf" import "core" import "theme" @@ -20,44 +21,49 @@ FileBuffer :: core.FileBuffer; state := core.State {}; +StateWithUi :: struct { + state: ^State, + ui_context: ^ui.Context, +} + // TODO: use buffer list in state do_normal_mode :: proc(state: ^State, buffer: ^FileBuffer) { if state.current_input_map != nil { - if raylib.IsKeyPressed(.ESCAPE) { - core.request_window_close(state); - } else if raylib.IsKeyDown(.LEFT_CONTROL) { - for key, action in &state.current_input_map.ctrl_key_actions { - if raylib.IsKeyPressed(key) { - switch value in action.action { - case core.PluginEditorAction: - value(state.plugin_vtable); - case core.EditorAction: - value(state); - case core.InputMap: - state.current_input_map = &(&state.current_input_map.ctrl_key_actions[key]).action.(core.InputMap) - } - } - } - } else { - for key, action in state.current_input_map.key_actions { - if raylib.IsKeyPressed(key) { - switch value in action.action { - case core.PluginEditorAction: - value(state.plugin_vtable); - case core.EditorAction: - value(state); - case core.InputMap: - state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputMap) - } - } - } - } + // if raylib.IsKeyPressed(.ESCAPE) { + // core.request_window_close(state); + // } else if raylib.IsKeyDown(.LEFT_CONTROL) { + // for key, action in &state.current_input_map.ctrl_key_actions { + // if raylib.IsKeyPressed(key) { + // switch value in action.action { + // case core.PluginEditorAction: + // value(state.plugin_vtable); + // case core.EditorAction: + // value(state); + // case core.InputMap: + // state.current_input_map = &(&state.current_input_map.ctrl_key_actions[key]).action.(core.InputMap) + // } + // } + // } + // } else { + // for key, action in state.current_input_map.key_actions { + // if raylib.IsKeyPressed(key) { + // switch value in action.action { + // case core.PluginEditorAction: + // value(state.plugin_vtable); + // case core.EditorAction: + // value(state); + // case core.InputMap: + // state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputMap) + // } + // } + // } + // } } } // TODO: use buffer list in state do_insert_mode :: proc(state: ^State, buffer: ^FileBuffer) { - key := raylib.GetCharPressed(); + key := 0; // raylib.GetCharPressed(); for key > 0 { if key >= 32 && key <= 125 && len(buffer.input_buffer) < 1024-1 { @@ -68,38 +74,38 @@ do_insert_mode :: proc(state: ^State, buffer: ^FileBuffer) { } } - key = raylib.GetCharPressed(); + key = 0; // raylib.GetCharPressed(); } - if raylib.IsKeyPressed(.ENTER) { - append(&buffer.input_buffer, '\n'); - } + // if raylib.IsKeyPressed(.ENTER) { + // append(&buffer.input_buffer, '\n'); + // } - if raylib.IsKeyPressed(.ESCAPE) { - state.mode = .Normal; + // if raylib.IsKeyPressed(.ESCAPE) { + // state.mode = .Normal; - core.insert_content(buffer, buffer.input_buffer[:]); - runtime.clear(&buffer.input_buffer); - return; - } + // core.insert_content(buffer, buffer.input_buffer[:]); + // runtime.clear(&buffer.input_buffer); + // return; + // } - if raylib.IsKeyPressed(.BACKSPACE) { - core.delete_content(buffer, 1); + // if raylib.IsKeyPressed(.BACKSPACE) { + // core.delete_content(buffer, 1); - for hook_proc in state.hooks[plugin.Hook.BufferInput] { - hook_proc(state.plugin_vtable, buffer); - } - } + // for hook_proc in state.hooks[plugin.Hook.BufferInput] { + // hook_proc(state.plugin_vtable, buffer); + // } + // } } -switch_to_buffer :: proc(state: ^State, item: ^ui.MenuBarItem) { - for buffer, index in state.buffers { - if strings.compare(buffer.file_path, item.text) == 0 { - state.current_buffer = index; - break; - } - } -} +// switch_to_buffer :: proc(state: ^State, item: ^ui.MenuBarItem) { +// for buffer, index in state.buffers { +// if strings.compare(buffer.file_path, item.text) == 0 { +// state.current_buffer = index; +// break; +// } +// } +// } register_default_leader_actions :: proc(input_map: ^core.InputMap) { core.register_key_action(input_map, .Q, proc(state: ^State) { @@ -160,16 +166,18 @@ register_default_input_actions :: proc(input_map: ^core.InputMap) { state.source_font_height -= 2; state.source_font_width = state.source_font_height / 2; - state.font = raylib.LoadFontEx("/System/Library/Fonts/Supplemental/Andale Mono.ttf", i32(state.source_font_height*2), nil, 0); - raylib.SetTextureFilter(state.font.texture, .BILINEAR); + state.font_atlas = core.gen_font_atlas(state, "/System/Library/Fonts/Supplemental/Andale Mono.ttf"); + //state.font = raylib.LoadFontEx("/System/Library/Fonts/Supplemental/Andale Mono.ttf", i32(state.source_font_height*2), nil, 0); + //raylib.SetTextureFilter(state.font.texture, .BILINEAR); } }, "increase font size"); core.register_ctrl_key_action(input_map, .EQUAL, proc(state: ^State) { state.source_font_height += 2; state.source_font_width = state.source_font_height / 2; - state.font = raylib.LoadFontEx("/System/Library/Fonts/Supplemental/Andale Mono.ttf", i32(state.source_font_height*2), nil, 0); - raylib.SetTextureFilter(state.font.texture, .BILINEAR); + state.font_atlas = core.gen_font_atlas(state, "/System/Library/Fonts/Supplemental/Andale Mono.ttf"); + //state.font = raylib.LoadFontEx("/System/Library/Fonts/Supplemental/Andale Mono.ttf", i32(state.source_font_height*2), nil, 0); + //raylib.SetTextureFilter(state.font.texture, .BILINEAR); }, "decrease font size"); } @@ -219,11 +227,159 @@ ui_font_height :: proc() -> i32 { return i32(state.source_font_height); } +draw :: proc(state_with_ui: ^StateWithUi) { + buffer := &state_with_ui.state.buffers[state_with_ui.state.current_buffer]; + + buffer.glyph_buffer_height = math.min(256, int((state_with_ui.state.screen_height - state_with_ui.state.source_font_height*2) / state_with_ui.state.source_font_height)) + 1; + buffer.glyph_buffer_width = math.min(256, int((state_with_ui.state.screen_width - state_with_ui.state.source_font_width) / state_with_ui.state.source_font_width)); + + // raylib.BeginDrawing(); + // defer raylib.EndDrawing(); + + render_color := theme.get_palette_color(.Background); + sdl2.SetRenderDrawColor(state_with_ui.state.sdl_renderer, render_color.r, render_color.g, render_color.b, render_color.a); + sdl2.RenderClear(state_with_ui.state.sdl_renderer); + + // raylib.ClearBackground(theme.get_palette_raylib_color(.Background)); + + // core.draw_file_buffer(state_with_ui.state, buffer, 32, state_with_ui.state.source_font_height); + + ui.compute_layout(state_with_ui.ui_context, { state_with_ui.state.screen_width, state_with_ui.state.screen_height }, state_with_ui.state.source_font_width, state_with_ui.state.source_font_height, state_with_ui.ui_context.root); + ui.draw(state_with_ui.ui_context, state_with_ui.state, state_with_ui.state.source_font_width, state_with_ui.state.source_font_height, state_with_ui.ui_context.root); + //ui.draw_menu_bar(&state_with_ui.state, &menu_bar_state_with_ui.state, 0, 0, i32(state_with_ui.state.screen_width), i32(state_with_ui.state.screen_height), state_with_ui.state.source_font_height); + + //raylib.DrawRectangle(0, i32(state_with_ui.state.screen_height - state_with_ui.state.source_font_height), i32(state_with_ui.state.screen_width), i32(state_with_ui.state.source_font_height), theme.get_palette_raylib_color(.Background2)); + + if state_with_ui.state.window != nil && state_with_ui.state.window.draw != nil { + state_with_ui.state.window.draw(state_with_ui.state.plugin_vtable, state_with_ui.state.window.user_data); + } + + if state_with_ui.state.current_input_map != &state_with_ui.state.input_map { + longest_description := 0; + for key, action in state_with_ui.state.current_input_map.key_actions { + if len(action.description) > longest_description { + longest_description = len(action.description); + } + } + for key, action in state_with_ui.state.current_input_map.ctrl_key_actions { + if len(action.description) > longest_description { + longest_description = len(action.description); + } + } + longest_description += 8; + + helper_height := state_with_ui.state.source_font_height * (len(state_with_ui.state.current_input_map.key_actions) + len(state_with_ui.state.current_input_map.ctrl_key_actions)); + offset_from_bottom := state_with_ui.state.source_font_height * 2; + + core.draw_rect( + state_with_ui.state, + state_with_ui.state.screen_width - longest_description * state_with_ui.state.source_font_width, + state_with_ui.state.screen_height - helper_height - offset_from_bottom, + longest_description*state_with_ui.state.source_font_width, + helper_height, + .Background2 + ); + //raylib.DrawRectangle( + // i32(state_with_ui.state.screen_width - longest_description * state_with_ui.state.source_font_width), + // i32(state_with_ui.state.screen_height - helper_height - offset_from_bottom), + // i32(longest_description*state_with_ui.state.source_font_width), + // i32(helper_height), + // theme.get_palette_raylib_color(.Background2) + //); + + index := 0; + for key, action in state_with_ui.state.current_input_map.key_actions { + core.draw_text( + state_with_ui.state, + fmt.tprintf("%s - %s", key, action.description), + state_with_ui.state.screen_width - longest_description * state_with_ui.state.source_font_width, + state_with_ui.state.screen_height - helper_height + index * state_with_ui.state.source_font_height - offset_from_bottom + ); + + // raylib.DrawTextEx( + // state_with_ui.state.font, + // raylib.TextFormat("%s - %s", key, action.description), + // raylib.Vector2 { f32(state_with_ui.state.screen_width - longest_description * state_with_ui.state.source_font_width), f32(state_with_ui.state.screen_height - helper_height + index * state_with_ui.state.source_font_height - offset_from_bottom) }, + // f32(state_with_ui.state.source_font_height), + // 0, + // theme.get_palette_raylib_color(.Foreground1) + // ); + index += 1; + } + for key, action in state_with_ui.state.current_input_map.ctrl_key_actions { + core.draw_text( + state_with_ui.state, + fmt.tprintf("-%s - %s", key, action.description), + state_with_ui.state.screen_width - longest_description * state_with_ui.state.source_font_width, + state_with_ui.state.screen_height - helper_height + index * state_with_ui.state.source_font_height - offset_from_bottom + ); + // raylib.DrawTextEx( + // state_with_ui.state.font, + // raylib.TextFormat("-%s - %s", key, action.description), + // raylib.Vector2 { f32(state_with_ui.state.screen_width - longest_description * state_with_ui.state.source_font_width), f32(state_with_ui.state.screen_height - helper_height + index * state_with_ui.state.source_font_height - offset_from_bottom) }, + // f32(state_with_ui.state.source_font_height), + // 0, + // theme.get_palette_raylib_color(.Foreground1) + // ); + index += 1; + } + } + + sdl2.RenderPresent(state_with_ui.state.sdl_renderer); +} + +// TODO: need to wrap state and ui context into one structure so that it can be used in this function +expose_event_watcher :: proc "c" (state: rawptr, event: ^sdl2.Event) -> i32 { + if event.type == .WINDOWEVENT { + state := transmute(^StateWithUi)state; + context = state.state.ctx; + + if event.window.event == .EXPOSED { + //draw(state); + } else if event.window.event == .SIZE_CHANGED { + w,h: i32; + + sdl2.GetRendererOutputSize(state.state.sdl_renderer, &w, &h); + + state.state.screen_width = int(w); + state.state.screen_height = int(h); + draw(state); + } + } + + return 0; +} + +ui_file_buffer :: proc(ctx: ^ui.Context, buffer: ^FileBuffer) -> ui.Interaction { + draw_func := proc(state: ^State, box: ^ui.Box, user_data: rawptr) { + buffer := transmute(^FileBuffer)user_data; + buffer.glyph_buffer_width = box.computed_size.x / state.source_font_width; + buffer.glyph_buffer_height = box.computed_size.y / state.source_font_height + 1; + + core.draw_file_buffer(state, buffer, box.computed_pos.x, box.computed_pos.y); + }; + + relative_file_path, _ := filepath.rel(state.directory, buffer.file_path, context.temp_allocator) + ui.push_parent(ctx, ui.push_box(ctx, relative_file_path, {}, .Vertical, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.Fill, 0)})); + defer ui.pop_parent(ctx); + + interaction := ui.custom(ctx, "buffer1", draw_func, transmute(rawptr)buffer); + + { + ui.push_parent(ctx, ui.push_box(ctx, "buffer info", {}, semantic_size = {ui.make_semantic_size(.Fill), ui.make_semantic_size(.Exact, state.source_font_height)})); + defer ui.pop_parent(ctx); + + ui.label(ctx, relative_file_path); + } + + return interaction; +} + main :: proc() { state = State { ctx = context, - source_font_width = 8, - source_font_height = 16, + source_font_width = 8 + 2 * 3, + source_font_height = 16 + 2 * 3, input_map = core.new_input_map(), window = nil, directory = os.get_current_directory(), @@ -232,11 +388,6 @@ main :: proc() { hooks = make(map[plugin.Hook][dynamic]plugin.OnHookProc), }; - ui_ctx := ui.Context { - text_width = ui_font_width, - text_height = ui_font_height, - } - state.plugin_vtable = plugin.Plugin { state = cast(rawptr)&state, register_hook = proc "c" (hook: plugin.Hook, on_hook: plugin.OnHookProc) { @@ -259,7 +410,6 @@ main :: proc() { context = state.ctx; to_be_edited_map: ^core.InputMap = nil; - key := raylib.KeyboardKey(int(key)); if input_map != nil { to_be_edited_map = transmute(^core.InputMap)input_map; @@ -286,7 +436,6 @@ main :: proc() { context = state.ctx; to_be_edited_map: ^core.InputMap = nil; - key := raylib.KeyboardKey(int(key)); description := strings.clone(string(description)); if input_map != nil { @@ -362,21 +511,22 @@ main :: proc() { draw_rect = proc "c" (x: i32, y: i32, width: i32, height: i32, color: theme.PaletteColor) { context = state.ctx; - raylib.DrawRectangle(x, y, width, height, theme.get_palette_raylib_color(color)); + core.draw_rect(&state, int(x), int(y), int(width), int(height), color); + //raylib.DrawRectangle(x, y, width, height, theme.get_palette_raylib_color(color)); }, draw_text = proc "c" (text: cstring, x: f32, y: f32, color: theme.PaletteColor) { context = state.ctx; - text := string(text); - for codepoint, index in text { - raylib.DrawTextCodepoint( - state.font, - rune(codepoint), - raylib.Vector2 { x + f32(index * state.source_font_width), y }, - f32(state.source_font_height), - theme.get_palette_raylib_color(color) - ); - } + core.draw_text(&state, string(text), int(x), int(y), color); + // for codepoint, index in text { + // raylib.DrawTextCodepoint( + // state.font, + // rune(codepoint), + // raylib.Vector2 { x + f32(index * state.source_font_width), y }, + // f32(state.source_font_height), + // theme.get_palette_raylib_color(color) + // ); + // } }, draw_buffer_from_index = proc "c" (buffer_index: int, x: int, y: int, glyph_buffer_width: int, glyph_buffer_height: int, show_line_numbers: bool) { context = state.ctx; @@ -388,7 +538,6 @@ main :: proc() { &state.buffers[buffer_index], x, y, - state.font, show_line_numbers); }, draw_buffer = proc "c" (buffer: rawptr, x: int, y: int, glyph_buffer_width: int, glyph_buffer_height: int, show_line_numbers: bool) { @@ -403,7 +552,6 @@ main :: proc() { buffer, x, y, - state.font, show_line_numbers); }, iter = plugin.Iterator { @@ -730,15 +878,6 @@ main :: proc() { runtime.append(&state.buffers, buffer); } - buffer_items := make([dynamic]ui.MenuBarItem, 0, len(state.buffers)); - for buffer, index in state.buffers { - item := ui.MenuBarItem { - text = buffer.file_path, - on_click = switch_to_buffer, - }; - - runtime.append(&buffer_items, item); - } // Load plugins // TODO(pcleavelin): Get directory of binary instead of shells current working directory @@ -750,245 +889,260 @@ main :: proc() { } } - raylib.InitWindow(640, 480, "odin_editor - [now with more ui]"); - raylib.SetWindowState({ .WINDOW_RESIZABLE, .VSYNC_HINT }); - raylib.SetTargetFPS(144); - raylib.SetExitKey(.KEY_NULL); + if sdl2.Init({.VIDEO}) < 0 { + fmt.eprintln("SDL failed to initialize:", sdl2.GetError()); + return; + } + + if ttf.Init() < 0 { + fmt.eprintln("SDL_TTF failed to initialize:", ttf.GetError()); + return; + } + defer ttf.Quit(); + + sdl_window := sdl2.CreateWindow( + "odin_editor - [now with more ui]", + sdl2.WINDOWPOS_UNDEFINED, + 0, + 640, + 480, + {.SHOWN, .RESIZABLE, .METAL, .ALLOW_HIGHDPI} + ); + defer if sdl_window != nil { + sdl2.DestroyWindow(sdl_window); + } + + if sdl_window == nil { + fmt.eprintln("Failed to create window:", sdl2.GetError()); + return; + } + + state.sdl_renderer = sdl2.CreateRenderer(sdl_window, -1, {.ACCELERATED, .PRESENTVSYNC}); + defer if state.sdl_renderer != nil { + sdl2.DestroyRenderer(state.sdl_renderer); + } + + if state.sdl_renderer == nil { + fmt.eprintln("Failed to create renderer:", sdl2.GetError()); + return; + } + state.font_atlas = core.gen_font_atlas(&state, "/System/Library/Fonts/Supplemental/Andale Mono.ttf"); + defer { + if state.font_atlas.font != nil { + ttf.CloseFont(state.font_atlas.font); + } + if state.font_atlas.texture != nil { + sdl2.DestroyTexture(state.font_atlas.texture); + } + } + + ui_context := ui.init(state.sdl_renderer); + + sdl2.AddEventWatch(expose_event_watcher, &StateWithUi { &state, &ui_context }); + + // raylib.InitWindow(640, 480, "odin_editor - [now with more ui]"); + // raylib.SetWindowState({ .WINDOW_RESIZABLE, .VSYNC_HINT }); + // raylib.SetTargetFPS(144); + // raylib.SetExitKey(.KEY_NULL); // TODO: don't just hard code a MacOS font path - state.font = raylib.LoadFontEx("/System/Library/Fonts/Supplemental/Andale Mono.ttf", i32(state.source_font_height*2), nil, 0); - raylib.SetTextureFilter(state.font.texture, .BILINEAR); - menu_bar_state := ui.MenuBarState{ - items = []ui.MenuBarItem { - ui.MenuBarItem { - text = "Buffers", - sub_items = buffer_items[:], - } - } - }; + // state.font = raylib.LoadFontEx("/System/Library/Fonts/Supplemental/Andale Mono.ttf", i32(state.source_font_height), nil, 0); + // raylib.SetTextureFilter(state.font.texture, .BILINEAR); - ui.init(); - for !raylib.WindowShouldClose() && !state.should_close { - state.screen_width = int(raylib.GetScreenWidth()); - state.screen_height = int(raylib.GetScreenHeight()); - mouse_pos := raylib.GetMousePosition(); - - buffer := &state.buffers[state.current_buffer]; - - buffer.glyph_buffer_height = math.min(256, int((state.screen_height - state.source_font_height*2) / state.source_font_height)) + 1; - buffer.glyph_buffer_width = math.min(256, int((state.screen_width - state.source_font_width) / state.source_font_width)); + control_key_pressed: bool; + state.screen_width = 640; //int(raylib.GetScreenWidth()); + state.screen_height = 480; //int(raylib.GetScreenHeight()); + for !state.should_close { { - raylib.BeginDrawing(); - defer raylib.EndDrawing(); + buffer := &state.buffers[state.current_buffer]; - raylib.ClearBackground(theme.get_palette_raylib_color(.Background)); - - core.draw_file_buffer(&state, buffer, 32, state.source_font_height, state.font); + ui.push_parent(&ui_context, ui.push_box(&ui_context, "main", {}, .Vertical, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.PercentOfParent, 100)})); + defer ui.pop_parent(&ui_context); { - 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_context, ui.push_box(&ui_context, "top_nav", {.DrawBackground}, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.Exact, state.source_font_height)})); + defer ui.pop_parent(&ui_context); - { - 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.label("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.label("Buffers").clicked { - fmt.println("you clicked the button"); - } + if ui.label(&ui_context, "Editor").clicked { + fmt.println("you clicked the button"); } - { - ui.push_box("deezbuffer", {}, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.Fill, 0)}); + + ui.push_box( + &ui_context, + "nav spacer", + {.DrawBackground}, + semantic_size = { + ui.make_semantic_size(.Exact, 16), + ui.make_semantic_size(.Exact, state.source_font_height) + } + ); + + if ui.label(&ui_context, "Buffers").clicked { + fmt.println("you clicked the button"); } + } + { + ui.push_parent(&ui_context, ui.push_box(&ui_context, "deezbuffer", {}, .Horizontal, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.Fill, 0)})); + defer ui.pop_parent(&ui_context); + { - 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"); + ui.push_parent(&ui_context, ui.push_box(&ui_context, "left side", {}, .Vertical, semantic_size = {ui.make_semantic_size(.Fill), ui.make_semantic_size(.Fill, 0)})); + defer ui.pop_parent(&ui_context); { - ui.push_parent(ui.push_box("center info", {}, semantic_size = ui.ChildrenSum)); - defer ui.pop_parent(); - - line_info_text := raylib.TextFormat( - //"Line: %d, Col: %d, Len: %d --- Slice Index: %d, Content Index: %d --- Frame Time: %fms", - "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.label(string(line_info_text)); + if ui_file_buffer(&ui_context, &state.buffers[0]).clicked { + state.current_buffer = 0; + } } + { + if ui_file_buffer(&ui_context, &state.buffers[0+1]).clicked { + state.current_buffer = 1; + } + } + { + if ui_file_buffer(&ui_context, &state.buffers[0+2]).clicked { + state.current_buffer = 2; + } + } + } + { + ui.push_parent(&ui_context, ui.push_box(&ui_context, "right side", {}, .Vertical, semantic_size = {ui.make_semantic_size(.Fill), ui.make_semantic_size(.Fill, 0)})); + defer ui.pop_parent(&ui_context); - ui.spacer("frame time spacer"); - frame_time := (60.0/f32(raylib.GetFPS())) * 10; - frame_time_text := raylib.TextFormat("frame time: %fms", frame_time); - ui.label(string(frame_time_text)); + { + if ui_file_buffer(&ui_context, &state.buffers[0+3]).clicked { + state.current_buffer = 3; + } + } } } + { + ui.push_parent(&ui_context, ui.push_box(&ui_context, "bottom stats", {.DrawBackground}, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.Exact, state.source_font_height)})); + defer ui.pop_parent(&ui_context); - ui.compute_layout({ state.screen_width, state.screen_height }, state.source_font_width, state.source_font_height); + label := ""; + if state.mode == .Insert { + label = "INSERT"; + } else if state.mode == .Normal { + label = "NORMAL"; + } + if ui.label(&ui_context, label).clicked { + fmt.println("you clicked the button"); + } + ui.spacer(&ui_context, "mode spacer", semantic_size = {ui.make_semantic_size(.Exact, 16), ui.make_semantic_size(.Fill)}); + relative_file_path, _ := filepath.rel(state.directory, buffer.file_path, context.temp_allocator) + ui.label(&ui_context, relative_file_path); - 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); + ui.spacer(&ui_context, "stats inbetween"); - //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_context, ui.push_box(&ui_context, "center info", {}, semantic_size = ui.ChildrenSum)); + defer ui.pop_parent(&ui_context); - 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 - ); - line_info_width := raylib.MeasureTextEx(state.font, line_info_text, f32(state.source_font_height), 0).x; + line_info_text := fmt.tprintf( + //"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.label(&ui_context, line_info_text); - // 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)); - // } + mouse_pos_str := fmt.tprintf("x,y: [%d,%d]", ui_context.mouse_x, ui_context.mouse_y); + ui.label(&ui_context, mouse_pos_str); + } - 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)); - - if state.window != nil && state.window.draw != nil { - state.window.draw(state.plugin_vtable, state.window.user_data); + //ui.spacer(&ui_context, "frame time spacer"); + //frame_time := (60.0/f32(raylib.GetFPS())) * 10; + //frame_time_text := raylib.TextFormat("frame time: %fms", frame_time); + //ui.label(&ui_context, "lol have to figure out how to get the frame time"); } + } - if state.current_input_map != &state.input_map { - longest_description := 0; - for key, action in state.current_input_map.key_actions { - if len(action.description) > longest_description { - longest_description = len(action.description); + { + ui_context.last_mouse_left_down = ui_context.mouse_left_down; + ui_context.last_mouse_right_down = ui_context.mouse_right_down; + + sdl_event: sdl2.Event; + for(sdl2.PollEvent(&sdl_event)) { + if sdl_event.type == .QUIT { + state.should_close = true; + } + + if sdl_event.type == .MOUSEMOTION { + ui_context.mouse_x = int(sdl_event.motion.x); + ui_context.mouse_y = int(sdl_event.motion.y); + } + + if sdl_event.type == .MOUSEBUTTONDOWN || sdl_event.type == .MOUSEBUTTONUP { + event := sdl_event.button; + + if event.button == sdl2.BUTTON_LEFT { + ui_context.mouse_left_down = sdl_event.type == .MOUSEBUTTONDOWN; + } + if event.button == sdl2.BUTTON_RIGHT { + ui_context.mouse_left_down = sdl_event.type == .MOUSEBUTTONDOWN; } } - for key, action in state.current_input_map.ctrl_key_actions { - if len(action.description) > longest_description { - longest_description = len(action.description); + + if sdl_event.type == .KEYDOWN { + key := plugin.Key(sdl_event.key.keysym.sym); + if key == .LCTRL { + control_key_pressed = true; + } else if state.current_input_map != nil { + if control_key_pressed { + if action, exists := state.current_input_map.ctrl_key_actions[key]; exists { + switch value in action.action { + case core.PluginEditorAction: + value(state.plugin_vtable); + case core.EditorAction: + value(&state); + case core.InputMap: + state.current_input_map = &(&state.current_input_map.ctrl_key_actions[key]).action.(core.InputMap) + } + } + } else { + if action, exists := state.current_input_map.key_actions[key]; exists { + switch value in action.action { + case core.PluginEditorAction: + value(state.plugin_vtable); + case core.EditorAction: + value(&state); + case core.InputMap: + state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputMap) + } + } + } } } - longest_description += 8; - - helper_height := state.source_font_height * (len(state.current_input_map.key_actions) + len(state.current_input_map.ctrl_key_actions)); - offset_from_bottom := state.source_font_height * 2; - - raylib.DrawRectangle( - i32(state.screen_width - longest_description * state.source_font_width), - i32(state.screen_height - helper_height - offset_from_bottom), - i32(longest_description*state.source_font_width), - i32(helper_height), - theme.get_palette_raylib_color(.Background2)); - - index := 0; - for key, action in state.current_input_map.key_actions { - raylib.DrawTextEx( - state.font, - raylib.TextFormat("%s - %s", key, action.description), - raylib.Vector2 { f32(state.screen_width - longest_description * state.source_font_width), f32(state.screen_height - helper_height + index * state.source_font_height - offset_from_bottom) }, - f32(state.source_font_height), - 0, - theme.get_palette_raylib_color(.Foreground1)); - index += 1; - } - for key, action in state.current_input_map.ctrl_key_actions { - raylib.DrawTextEx( - state.font, - raylib.TextFormat("-%s - %s", key, action.description), - raylib.Vector2 { f32(state.screen_width - longest_description * state.source_font_width), f32(state.screen_height - helper_height + index * state.source_font_height - offset_from_bottom) }, - f32(state.source_font_height), - 0, - theme.get_palette_raylib_color(.Foreground1)); - index += 1; + if sdl_event.type == .KEYUP { + key := plugin.Key(sdl_event.key.keysym.sym); + if key == .LCTRL { + control_key_pressed = false; + } } } } + // ui.debug_print(); + + draw(&StateWithUi { &state, &ui_context }); + + ui.prune(&ui_context); + switch state.mode { case .Normal: if state.window != nil && state.window.get_buffer != nil { buffer := transmute(^core.FileBuffer)(state.window.get_buffer(state.plugin_vtable, state.window.user_data)); do_normal_mode(&state, buffer); } else { + buffer := &state.buffers[state.current_buffer]; do_normal_mode(&state, buffer); } case .Insert: @@ -996,6 +1150,7 @@ main :: proc() { buffer := transmute(^core.FileBuffer)(state.window.get_buffer(state.plugin_vtable, state.window.user_data)); do_insert_mode(&state, buffer); } else { + buffer := &state.buffers[state.current_buffer]; do_insert_mode(&state, buffer); } } @@ -1005,8 +1160,6 @@ main :: proc() { core.close_window_and_free(&state); } - ui.test_menu_bar(&state, &menu_bar_state, 0,0, mouse_pos, raylib.IsMouseButtonReleased(.LEFT), state.source_font_height); - runtime.free_all(context.temp_allocator); } @@ -1015,4 +1168,6 @@ main :: proc() { plugin.on_exit(); } } + + sdl2.Quit(); } diff --git a/src/plugin/plugin.odin b/src/plugin/plugin.odin index 83365d6..d9684b8 100644 --- a/src/plugin/plugin.odin +++ b/src/plugin/plugin.odin @@ -3,7 +3,6 @@ package plugin; import "core:intrinsics" import "core:dynlib" import "core:fmt" -import "vendor:raylib" import "../theme" @@ -127,120 +126,246 @@ Hook :: enum { } Key :: enum { - KEY_NULL = 0, // Key: NULL, used for no key pressed - // Alphanumeric keys - APOSTROPHE = 39, // Key: ' - COMMA = 44, // Key: , - MINUS = 45, // Key: - - PERIOD = 46, // Key: . - SLASH = 47, // Key: / - ZERO = 48, // Key: 0 - ONE = 49, // Key: 1 - TWO = 50, // Key: 2 - THREE = 51, // Key: 3 - FOUR = 52, // Key: 4 - FIVE = 53, // Key: 5 - SIX = 54, // Key: 6 - SEVEN = 55, // Key: 7 - EIGHT = 56, // Key: 8 - NINE = 57, // Key: 9 - SEMICOLON = 59, // Key: ; - EQUAL = 61, // Key: = - A = 65, // Key: A | a - B = 66, // Key: B | b - C = 67, // Key: C | c - D = 68, // Key: D | d - E = 69, // Key: E | e - F = 70, // Key: F | f - G = 71, // Key: G | g - H = 72, // Key: H | h - I = 73, // Key: I | i - J = 74, // Key: J | j - K = 75, // Key: K | k - L = 76, // Key: L | l - M = 77, // Key: M | m - N = 78, // Key: N | n - O = 79, // Key: O | o - P = 80, // Key: P | p - Q = 81, // Key: Q | q - R = 82, // Key: R | r - S = 83, // Key: S | s - T = 84, // Key: T | t - U = 85, // Key: U | u - V = 86, // Key: V | v - W = 87, // Key: W | w - X = 88, // Key: X | x - Y = 89, // Key: Y | y - Z = 90, // Key: Z | z - LEFT_BRACKET = 91, // Key: [ - BACKSLASH = 92, // Key: '\' - RIGHT_BRACKET = 93, // Key: ] - GRAVE = 96, // Key: ` - // Function keys - SPACE = 32, // Key: Space - ESCAPE = 256, // Key: Esc - ENTER = 257, // Key: Enter - TAB = 258, // Key: Tab - BACKSPACE = 259, // Key: Backspace - INSERT = 260, // Key: Ins - DELETE = 261, // Key: Del - RIGHT = 262, // Key: Cursor right - LEFT = 263, // Key: Cursor left - DOWN = 264, // Key: Cursor down - UP = 265, // Key: Cursor up - PAGE_UP = 266, // Key: Page up - PAGE_DOWN = 267, // Key: Page down - HOME = 268, // Key: Home - END = 269, // Key: End - CAPS_LOCK = 280, // Key: Caps lock - SCROLL_LOCK = 281, // Key: Scroll down - NUM_LOCK = 282, // Key: Num lock - PRINT_SCREEN = 283, // Key: Print screen - PAUSE = 284, // Key: Pause - F1 = 290, // Key: F1 - F2 = 291, // Key: F2 - F3 = 292, // Key: F3 - F4 = 293, // Key: F4 - F5 = 294, // Key: F5 - F6 = 295, // Key: F6 - F7 = 296, // Key: F7 - F8 = 297, // Key: F8 - F9 = 298, // Key: F9 - F10 = 299, // Key: F10 - F11 = 300, // Key: F11 - F12 = 301, // Key: F12 - LEFT_SHIFT = 340, // Key: Shift left - LEFT_CONTROL = 341, // Key: Control left - LEFT_ALT = 342, // Key: Alt left - LEFT_SUPER = 343, // Key: Super left - RIGHT_SHIFT = 344, // Key: Shift right - RIGHT_CONTROL = 345, // Key: Control right - RIGHT_ALT = 346, // Key: Alt right - RIGHT_SUPER = 347, // Key: Super right - KB_MENU = 348, // Key: KB menu - // Keypad keys - KP_0 = 320, // Key: Keypad 0 - KP_1 = 321, // Key: Keypad 1 - KP_2 = 322, // Key: Keypad 2 - KP_3 = 323, // Key: Keypad 3 - KP_4 = 324, // Key: Keypad 4 - KP_5 = 325, // Key: Keypad 5 - KP_6 = 326, // Key: Keypad 6 - KP_7 = 327, // Key: Keypad 7 - KP_8 = 328, // Key: Keypad 8 - KP_9 = 329, // Key: Keypad 9 - KP_DECIMAL = 330, // Key: Keypad . - KP_DIVIDE = 331, // Key: Keypad / - KP_MULTIPLY = 332, // Key: Keypad * - KP_SUBTRACT = 333, // Key: Keypad - - KP_ADD = 334, // Key: Keypad + - KP_ENTER = 335, // Key: Keypad Enter - KP_EQUAL = 336, // Key: Keypad = - // Android key buttons - BACK = 4, // Key: Android back button - MENU = 82, // Key: Android menu button - VOLUME_UP = 24, // Key: Android volume up button - VOLUME_DOWN = 25, // Key: Android volume down button + UNKNOWN = 0, + ENTER = 13, + ESCAPE = 27, + BACKSPACE = 8, + TAB = 9, + SPACE = 32, + EXCLAIM = 33, + QUOTEDBL = 34, + HASH = 35, + PERCENT = 37, + DOLLAR = 36, + AMPERSAND = 38, + QUOTE = 39, + LEFTPAREN = 40, + RIGHTPAREN = 41, + ASTERISK = 42, + PLUS = 43, + COMMA = 44, + MINUS = 45, + PERIOD = 46, + SLASH = 47, + NUM0 = 48, + NUM1 = 49, + NUM2 = 50, + NUM3 = 51, + NUM4 = 52, + NUM5 = 53, + NUM6 = 54, + NUM7 = 55, + NUM8 = 56, + NUM9 = 57, + COLON = 58, + SEMICOLON = 59, + LESS = 60, + EQUAL = 61, + GREATER = 62, + QUESTION = 63, + AT = 64, + LEFTBRACKET = 91, + BACKSLASH = 92, + RIGHTBRACKET = 93, + CARET = 94, + UNDERSCORE = 95, + BACKQUOTE = 96, + A = 97, + B = 98, + C = 99, + D = 100, + E = 101, + F = 102, + G = 103, + H = 104, + I = 105, + J = 106, + K = 107, + L = 108, + M = 109, + N = 110, + O = 111, + P = 112, + Q = 113, + R = 114, + S = 115, + T = 116, + U = 117, + V = 118, + W = 119, + X = 120, + Y = 121, + Z = 122, + CAPSLOCK = 1073741881, + F1 = 1073741882, + F2 = 1073741883, + F3 = 1073741884, + F4 = 1073741885, + F5 = 1073741886, + F6 = 1073741887, + F7 = 1073741888, + F8 = 1073741889, + F9 = 1073741890, + F10 = 1073741891, + F11 = 1073741892, + F12 = 1073741893, + PRINTSCREEN = 1073741894, + SCROLLLOCK = 1073741895, + PAUSE = 1073741896, + INSERT = 1073741897, + HOME = 1073741898, + PAGEUP = 1073741899, + DELETE = 127, + END = 1073741901, + PAGEDOWN = 1073741902, + RIGHT = 1073741903, + LEFT = 1073741904, + DOWN = 1073741905, + UP = 1073741906, + NUMLOCKCLEAR = 1073741907, + KP_DIVIDE = 1073741908, + KP_MULTIPLY = 1073741909, + KP_MINUS = 1073741910, + KP_PLUS = 1073741911, + KP_ENTER = 1073741912, + KP_1 = 1073741913, + KP_2 = 1073741914, + KP_3 = 1073741915, + KP_4 = 1073741916, + KP_5 = 1073741917, + KP_6 = 1073741918, + KP_7 = 1073741919, + KP_8 = 1073741920, + KP_9 = 1073741921, + KP_0 = 1073741922, + KP_PERIOD = 1073741923, + APPLICATION = 1073741925, + POWER = 1073741926, + KP_EQUALS = 1073741927, + F13 = 1073741928, + F14 = 1073741929, + F15 = 1073741930, + F16 = 1073741931, + F17 = 1073741932, + F18 = 1073741933, + F19 = 1073741934, + F20 = 1073741935, + F21 = 1073741936, + F22 = 1073741937, + F23 = 1073741938, + F24 = 1073741939, + EXECUTE = 1073741940, + HELP = 1073741941, + MENU = 1073741942, + SELECT = 1073741943, + STOP = 1073741944, + AGAIN = 1073741945, + UNDO = 1073741946, + CUT = 1073741947, + COPY = 1073741948, + PASTE = 1073741949, + FIND = 1073741950, + MUTE = 1073741951, + VOLUMEUP = 1073741952, + VOLUMEDOWN = 1073741953, + KP_COMMA = 1073741957, + KP_EQUALSAS400 = 1073741958, + ALTERASE = 1073741977, + SYSREQ = 1073741978, + CANCEL = 1073741979, + CLEAR = 1073741980, + PRIOR = 1073741981, + RETURN2 = 1073741982, + SEPARATOR = 1073741983, + OUT = 1073741984, + OPER = 1073741985, + CLEARAGAIN = 1073741986, + CRSEL = 1073741987, + EXSEL = 1073741988, + KP_00 = 1073742000, + KP_000 = 1073742001, + THOUSANDSSEPARATOR = 1073742002, + DECIMALSEPARATOR = 1073742003, + CURRENCYUNIT = 1073742004, + CURRENCYSUBUNIT = 1073742005, + KP_LEFTPAREN = 1073742006, + KP_RIGHTPAREN = 1073742007, + KP_LEFTBRACE = 1073742008, + KP_RIGHTBRACE = 1073742009, + KP_TAB = 1073742010, + KP_BACKSPACE = 1073742011, + KP_A = 1073742012, + KP_B = 1073742013, + KP_C = 1073742014, + KP_D = 1073742015, + KP_E = 1073742016, + KP_F = 1073742017, + KP_XOR = 1073742018, + KP_POWER = 1073742019, + KP_PERCENT = 1073742020, + KP_LESS = 1073742021, + KP_GREATER = 1073742022, + KP_AMPERSAND = 1073742023, + KP_DBLAMPERSAND = 1073742024, + KP_VERTICALBAR = 1073742025, + KP_DBLVERTICALBAR = 1073742026, + KP_COLON = 1073742027, + KP_HASH = 1073742028, + KP_SPACE = 1073742029, + KP_AT = 1073742030, + KP_EXCLAM = 1073742031, + KP_MEMSTORE = 1073742032, + KP_MEMRECALL = 1073742033, + KP_MEMCLEAR = 1073742034, + KP_MEMADD = 1073742035, + KP_MEMSUBTRACT = 1073742036, + KP_MEMMULTIPLY = 1073742037, + KP_MEMDIVIDE = 1073742038, + KP_PLUSMINUS = 1073742039, + KP_CLEAR = 1073742040, + KP_CLEARENTRY = 1073742041, + KP_BINARY = 1073742042, + KP_OCTAL = 1073742043, + KP_DECIMAL = 1073742044, + KP_HEXADECIMAL = 1073742045, + LCTRL = 1073742048, + LSHIFT = 1073742049, + LALT = 1073742050, + LGUI = 1073742051, + RCTRL = 1073742052, + RSHIFT = 1073742053, + RALT = 1073742054, + RGUI = 1073742055, + MODE = 1073742081, + AUDIONEXT = 1073742082, + AUDIOPREV = 1073742083, + AUDIOSTOP = 1073742084, + AUDIOPLAY = 1073742085, + AUDIOMUTE = 1073742086, + MEDIASELECT = 1073742087, + WWW = 1073742088, + MAIL = 1073742089, + CALCULATOR = 1073742090, + COMPUTER = 1073742091, + AC_SEARCH = 1073742092, + AC_HOME = 1073742093, + AC_BACK = 1073742094, + AC_FORWARD = 1073742095, + AC_STOP = 1073742096, + AC_REFRESH = 1073742097, + AC_BOOKMARKS = 1073742098, + BRIGHTNESSDOWN = 1073742099, + BRIGHTNESSUP = 1073742100, + DISPLAYSWITCH = 1073742101, + KBDILLUMTOGGLE = 1073742102, + KBDILLUMDOWN = 1073742103, + KBDILLUMUP = 1073742104, + EJECT = 1073742105, + SLEEP = 1073742106, + APP1 = 1073742107, + APP2 = 1073742108, + AUDIOREWIND = 1073742109, + AUDIOFASTFORWARD = 1073742110, } diff --git a/src/theme/theme.odin b/src/theme/theme.odin index f7d372b..2dbe393 100644 --- a/src/theme/theme.odin +++ b/src/theme/theme.odin @@ -1,7 +1,5 @@ package theme -import "vendor:raylib" - PaletteColor :: enum { Background, Foreground, @@ -97,7 +95,14 @@ light_palette := []u32 { 0x928374ff, }; -get_palette_raylib_color :: proc(palette_color: PaletteColor) -> raylib.Color { - return raylib.GetColor(palette[palette_color]); +get_palette_color :: proc(palette_color: PaletteColor) -> [4]u8 { + color: [4]u8; + + c := palette[palette_color]; + for i in 0..<4 { + color[i] = u8((c >> (8*u32(3-i)))&0xff); + } + + return color; } diff --git a/src/ui/imm.odin b/src/ui/imm.odin index aa8bf14..e1148d7 100644 --- a/src/ui/imm.odin +++ b/src/ui/imm.odin @@ -3,22 +3,30 @@ package ui import "core:fmt" import "core:strings" import "core:math" -import "vendor:raylib" +import "vendor:sdl2" +import "../core" import "../theme" Context :: struct { - text_width: proc() -> i32, - text_height: proc() -> i32, + root: ^Box, + current_parent: ^Box, + persistent: map[Key]^Box, + current_interaction_index: int, + + clips: [dynamic]Rect, + renderer: ^sdl2.Renderer, + + mouse_x: int, + mouse_y: int, + + mouse_left_down: bool, + last_mouse_left_down: bool, + + mouse_right_down: bool, + last_mouse_right_down: bool, } -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, @@ -30,6 +38,7 @@ Key :: struct { } Interaction :: struct { + hovering: bool, clicked: bool, } @@ -40,6 +49,7 @@ Flag :: enum { DrawText, DrawBorder, DrawBackground, + CustomDrawFunc, } SemanticSizeKind :: enum { @@ -60,6 +70,7 @@ Axis :: enum { Vertical = 1, } +CustomDrawFunc :: proc(state: ^core.State, box: ^Box, user_data: rawptr); Box :: struct { first: ^Box, last: ^Box, @@ -77,30 +88,36 @@ Box :: struct { axis: Axis, semantic_size: [2]SemanticSize, computed_size: [2]int, + computed_pos: [2]int, - computed_pos: [2]int + hot: int, + active: int, + + custom_draw_func: CustomDrawFunc, + user_data: rawptr, } -init :: proc() { - if persistent == nil { - persistent = make(map[Key]^Box); - } +init :: proc(renderer: ^sdl2.Renderer) -> Context { + root := new(Box); + root.key = gen_key(nil, "root", 69); - if clips == nil { - clips = make([dynamic]Rect); - } - - root = new(Box); - root.key = gen_key("root", 69); - current_parent = root; + return Context { + root = root, + current_parent = root, + persistent = make(map[Key]^Box), + clips = make([dynamic]Rect), + renderer = renderer, + }; } -gen_key :: proc(label: string, value: int) -> Key { +gen_key :: proc(ctx: ^Context, label: string, value: int) -> Key { key_label := "" - if current_parent == nil || len(current_parent.key.label) < 1 { + if ctx != nil && (ctx.current_parent == nil || len(ctx.current_parent.key.label) < 1) { key_label = strings.clone(label); + } else if ctx != nil { + key_label = fmt.aprintf("%s:%s", ctx.current_parent.key.label, label); } else { - key_label = fmt.aprintf("%s:%s", current_parent.key.label, label); + key_label = fmt.aprintf("%s",label); } return Key { @@ -109,22 +126,22 @@ gen_key :: proc(label: string, value: int) -> Key { }; } -make_box :: proc(key: Key, label: string, flags: bit_set[Flag], axis: Axis, semantic_size: [2]SemanticSize) -> ^Box { +make_box :: proc(ctx: ^Context, 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]; + if cached_box, exists := ctx.persistent[key]; exists { + if cached_box.last_interacted_index < ctx.current_interaction_index { + old_cached_box := ctx.persistent[key]; free(old_cached_box); box = new(Box); - persistent[key] = box; + ctx.persistent[key] = box; } else { box = cached_box; } } else { box = new(Box); - persistent[key] = box; + ctx.persistent[key] = box; } box.key = key; @@ -133,22 +150,20 @@ make_box :: proc(key: Key, label: string, flags: bit_set[Flag], axis: Axis, sema box.first = nil; box.last = nil; box.next = nil; - box.prev = current_parent.last; - box.parent = current_parent; + box.prev = ctx.current_parent.last; + box.parent = ctx.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 ctx.current_parent.last != nil { + ctx.current_parent.last.next = box; } - if current_parent.first == nil { - current_parent.first = box; + if ctx.current_parent.first == nil { + ctx.current_parent.first = box; } - current_parent.last = box; + ctx.current_parent.last = box; return box; } @@ -178,67 +193,89 @@ ChildrenSum :[2]SemanticSize: { } }; -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); +push_box :: proc(ctx: ^Context, label: string, flags: bit_set[Flag], axis: Axis = .Horizontal, semantic_size: [2]SemanticSize = FitText, value: int = 0) -> ^Box { + key := gen_key(ctx, label, value); + box := make_box(ctx, key, label, flags, axis, semantic_size); return box; } -push_parent :: proc(box: ^Box) { - current_parent = box; +push_parent :: proc(ctx: ^Context, box: ^Box) { + ctx.current_parent = box; } -pop_parent :: proc() { - if current_parent.parent != nil { - current_parent = current_parent.parent; +pop_parent :: proc(ctx: ^Context) { + if ctx.current_parent.parent != nil { + ctx.current_parent = ctx.current_parent.parent; } } -test_box :: proc(box: ^Box) -> Interaction { +test_box :: proc(ctx: ^Context, box: ^Box) -> Interaction { + hovering: bool; + + mouse_is_clicked := !ctx.last_mouse_left_down && ctx.mouse_left_down; + + if ctx.mouse_x >= box.computed_pos.x && ctx.mouse_x <= box.computed_pos.x + box.computed_size.x && + ctx.mouse_y >= box.computed_pos.y && ctx.mouse_y <= box.computed_pos.y + box.computed_size.y + { + hovering = true; + } + + if hovering { + box.hot += 1; + } else { + box.hot = 0; + } + + if hovering && mouse_is_clicked { + fmt.println("hot", box.hot); + } + return Interaction { - clicked = false, + hovering = hovering, + clicked = hovering && mouse_is_clicked, }; } -delete_box_children :: proc(box: ^Box, keep_persistent: bool = true) { +delete_box_children :: proc(ctx: ^Context, box: ^Box, keep_persistent: bool = true) { iter := BoxIter { box.first, 0 }; for box in iterate_box(&iter) { - delete_box(box, keep_persistent); + delete_box(ctx, box, keep_persistent); } } -delete_box :: proc(box: ^Box, keep_persistent: bool = true) { - delete_box_children(box, keep_persistent); +delete_box :: proc(ctx: ^Context, box: ^Box, keep_persistent: bool = true) { + delete_box_children(ctx, box, keep_persistent); - if !(box.key in persistent) || !keep_persistent { + if !(box.key in ctx.persistent) || !keep_persistent { delete(box.key.label); free(box); } } -prune :: proc() { - iter := BoxIter { root.first, 0 }; +prune :: proc(ctx: ^Context) { + iter := BoxIter { ctx.root.first, 0 }; for box in iterate_box(&iter) { - delete_box_children(box); + delete_box_children(ctx, box); - if !(box.key in persistent) { + if !(box.key in ctx.persistent) { free(box); } } - root_key := root.key; - root^ = { + root_key := ctx.root.key; + ctx.root^ = { key = root_key, }; - current_parent = root; + ctx.current_parent = ctx.root; } -ancestor_size :: proc(box: ^Box, axis: Axis) -> int { +// TODO: consider not using `ctx` here +ancestor_size :: proc(ctx: ^Context, box: ^Box, axis: Axis) -> int { if box == nil || box.parent == nil { - return root.computed_size[axis]; + return ctx.root.computed_size[axis]; } switch box.parent.semantic_size[axis].kind { @@ -249,13 +286,13 @@ ancestor_size :: proc(box: ^Box, axis: Axis) -> int { return box.parent.computed_size[axis]; case .ChildrenSum: - return ancestor_size(box.parent, axis); + return ancestor_size(ctx, box.parent, axis); } return 1337; } -compute_layout :: proc(canvas_size: [2]int, font_width: int, font_height: int, box: ^Box = root) { +compute_layout :: proc(ctx: ^Context, canvas_size: [2]int, font_width: int, font_height: int, box: ^Box) { if box == nil { return; } axis := Axis.Horizontal; @@ -268,8 +305,9 @@ compute_layout :: proc(canvas_size: [2]int, font_width: int, font_height: int, b box.computed_pos[axis] = box.prev.computed_pos[axis] + box.prev.computed_size[axis]; } + post_compute_size := [2]bool { false, false }; compute_children := true; - if box == root { + if box == ctx.root { box.computed_size = canvas_size; } else { switch box.semantic_size.x.kind { @@ -281,29 +319,30 @@ compute_layout :: proc(canvas_size: [2]int, font_width: int, font_height: int, b box.computed_size.x = box.semantic_size.x.value; } case .ChildrenSum: { - compute_children = false; - box.computed_size.x = 0; + //compute_children = false; + post_compute_size[int(Axis.Horizontal)] = true; + // 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); + // 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; - } - } - } - } + // 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)); + box.computed_size.x = int(f32(ancestor_size(ctx, box, .Horizontal))*(f32(box.semantic_size.x.value)/100.0)); } } switch box.semantic_size.y.kind { @@ -315,32 +354,34 @@ compute_layout :: proc(canvas_size: [2]int, font_width: int, font_height: int, b 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); + //compute_children = false; + post_compute_size[Axis.Vertical] = true; - iter := BoxIter { box.first, 0 }; - for child in iterate_box(&iter) { - compute_layout(canvas_size, font_width, font_height, child); + // should_post_compute := false; + // number_of_fills := 0; + // box.computed_size.y = 0; + // parent_size := ancestor_size(box, .Vertical); - if child.semantic_size.y.kind == .Fill { - number_of_fills += 1; - should_post_compute := true; - } + // 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: { - 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 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 }; @@ -354,7 +395,7 @@ compute_layout :: proc(canvas_size: [2]int, font_width: int, font_height: int, b case .Fill: { } case .PercentOfParent: { - box.computed_size.y = int(f32(ancestor_size(box, .Vertical))*(f32(box.semantic_size.y.value)/100.0)); + box.computed_size.y = int(f32(ancestor_size(ctx, box, .Vertical))*(f32(box.semantic_size.y.value)/100.0)); } } } @@ -374,7 +415,7 @@ compute_layout :: proc(canvas_size: [2]int, font_width: int, font_height: int, b our_size := box.computed_size; for child in iterate_box(&iter) { - compute_layout(canvas_size, font_width, font_height, child); + compute_layout(ctx, 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; @@ -383,12 +424,12 @@ compute_layout :: proc(canvas_size: [2]int, font_width: int, font_height: int, b } } - if should_post_compute { + if true || 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] { + if false && 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]; @@ -396,17 +437,56 @@ compute_layout :: proc(canvas_size: [2]int, font_width: int, font_height: int, b } } - compute_layout(canvas_size, font_width, font_height, child); + compute_layout(ctx, canvas_size, font_width, font_height, child); + + if child.label == "2" { + fmt.println(child.label, child.computed_size, box.label, our_size, child_size, number_of_fills); + } + } + } + } + + if post_compute_size[Axis.Horizontal] { + box.computed_size[Axis.Horizontal] = 0; + + iter := BoxIter { box.first, 0 }; + for child in iterate_box(&iter) { + switch box.axis { + case .Horizontal: { + box.computed_size[Axis.Horizontal] += child.computed_size[Axis.Horizontal]; + } + case .Vertical: { + if child.computed_size[Axis.Horizontal] > box.computed_size[Axis.Horizontal] { + box.computed_size[Axis.Horizontal] = child.computed_size[Axis.Horizontal]; + } + } + } + } + } + if post_compute_size[Axis.Vertical] { + box.computed_size[Axis.Vertical] = 0; + + iter := BoxIter { box.first, 0 }; + for child in iterate_box(&iter) { + switch box.axis { + case .Horizontal: { + if child.computed_size[Axis.Vertical] > box.computed_size[Axis.Vertical] { + box.computed_size[Axis.Vertical] = child.computed_size[Axis.Vertical]; + } + } + case .Vertical: { + box.computed_size[Axis.Vertical] += child.computed_size[Axis.Vertical]; + } } } } } -push_clip :: proc(pos: [2]int, size: [2]int) { +push_clip :: proc(ctx: ^Context, pos: [2]int, size: [2]int) { rect := Rect { pos, size }; - if len(clips) > 0 { - parent_rect := clips[len(clips)-1]; + if len(ctx.clips) > 0 { + parent_rect := ctx.clips[len(ctx.clips)-1]; if rect.pos.x >= parent_rect.pos.x && rect.pos.y >= parent_rect.pos.y && @@ -426,72 +506,96 @@ push_clip :: proc(pos: [2]int, size: [2]int) { } } - raylib.BeginScissorMode( + sdl2.RenderSetClipRect(ctx.renderer, &sdl2.Rect { i32(rect.pos.x), i32(rect.pos.y), i32(rect.size.x), i32(rect.size.y) - ); + }); - append(&clips, rect); + // raylib.BeginScissorMode( + // i32(rect.pos.x), + // i32(rect.pos.y), + // i32(rect.size.x), + // i32(rect.size.y) + // ); + + append(&ctx.clips, rect); } -pop_clip :: proc() { - raylib.EndScissorMode(); +pop_clip :: proc(ctx: ^Context) { + //raylib.EndScissorMode(); - if len(clips) > 0 { - rect := pop(&clips); + if len(ctx.clips) > 0 { + rect := pop(&ctx.clips); - raylib.BeginScissorMode( + sdl2.RenderSetClipRect(ctx.renderer, &sdl2.Rect { i32(rect.pos.x), i32(rect.pos.y), i32(rect.size.x), i32(rect.size.y) - ); + }); + // raylib.BeginScissorMode( + // i32(rect.pos.x), + // i32(rect.pos.y), + // i32(rect.size.x), + // i32(rect.size.y) + // ); + } else { + sdl2.RenderSetClipRect(ctx.renderer, nil); } } -draw :: proc(font: raylib.Font, font_width: int, font_height: int, box: ^Box = root) { +draw :: proc(ctx: ^Context, state: ^core.State, font_width: int, font_height: int, box: ^Box) { 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(); + push_clip(ctx, box.computed_pos, box.computed_size); + defer pop_clip(ctx); - 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 .Hoverable in box.flags && box.hot > 0 { + core.draw_rect( + state, + box.computed_pos.x, + box.computed_pos.y, + box.computed_size.x, + box.computed_size.y, + .Background2 ); } + else if .DrawBackground in box.flags { + core.draw_rect( + state, + box.computed_pos.x, + box.computed_pos.y, + box.computed_size.x, + box.computed_size.y, + .Background1 + ); + } + 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) + core.draw_rect_outline( + state, + box.computed_pos.x, + box.computed_pos.y, + box.computed_size.x, + box.computed_size.y, + .Background4 ); } 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) - ); - } + core.draw_text(state, box.label, box.computed_pos.x, box.computed_pos.y); + } + + if .CustomDrawFunc in box.flags && box.custom_draw_func != nil { + box.custom_draw_func(state, box, box.user_data); } iter := BoxIter { box.first, 0 }; for child in iterate_box(&iter) { - draw(font, font_width, font_height, child); + draw(ctx, state, font_width, font_height, child); } } @@ -514,7 +618,7 @@ iterate_box :: proc(iter: ^BoxIter, print: bool = false) -> (box: ^Box, idx: int return box, iter.index, true; } -debug_print :: proc(box: ^Box, depth: int = 0) { +debug_print :: proc(ctx: ^Context, box: ^Box, depth: int = 0) { iter := BoxIter { box.first, 0 }; for box, idx in iterate_box(&iter, true) { @@ -525,56 +629,69 @@ debug_print :: proc(box: ^Box, depth: int = 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); + debug_print(ctx, box, depth+1); } if depth == 0 { fmt.println("persistent"); - for p in persistent { + for p in ctx.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)}); +spacer :: proc(ctx: ^Context, label: string, flags: bit_set[Flag] = {}, semantic_size: [2]SemanticSize = {{.Fill, 0}, {.Fill,0}}) -> ^Box { + return push_box(ctx, label, flags, semantic_size = semantic_size); } -label :: proc(label: string) -> Interaction { - box := push_box(label, {.DrawText}); +label :: proc(ctx: ^Context, label: string) -> Interaction { + box := push_box(ctx, label, {.DrawText, .Hoverable}); - return test_box(box); + return test_box(ctx, box); } -button :: proc(label: string) -> Interaction { - box := push_box(label, {.Clickable, .Hoverable, .DrawText, .DrawBorder, .DrawBackground}); +button :: proc(ctx: ^Context, label: string) -> Interaction { + box := push_box(ctx, label, {.Clickable, .Hoverable, .DrawText, .DrawBorder, .DrawBackground}); - return test_box(box); + return test_box(ctx, box); } -two_buttons_test :: proc(label1: string, label2: string) { - push_parent(push_box("two_button_container", {.DrawBorder}, .Vertical, semantic_size = ChildrenSum)); +custom :: proc(ctx: ^Context, label: string, draw_func: CustomDrawFunc, user_data: rawptr) -> Interaction { + box := push_box(ctx, label, {.DrawBorder, .CustomDrawFunc}, semantic_size = { make_semantic_size(.Fill), make_semantic_size(.Fill) }); + box.custom_draw_func = draw_func; + box.user_data = user_data; - button("1"); - button("2"); - button(label1); - button(label2); - button("5"); - button("6"); + return test_box(ctx, box); +} + +two_buttons_test :: proc(ctx: ^Context, label1: string, label2: string) { + push_parent(ctx, push_box(ctx, "TWO BUTTONS TEST", {.DrawBorder}, .Vertical, semantic_size = {make_semantic_size(.PercentOfParent, 100), { .Fill, 256}})); + + button(ctx, "Row 1"); + button(ctx, "Row 2"); + button(ctx, label1); + button(ctx, label2); + button(ctx, "Row 5"); + button(ctx, "Row 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(); + push_parent(ctx, push_box(ctx, "two_button_container_inner", {.DrawBorder}, semantic_size = {make_semantic_size(.Fill, 0), { .Fill, 64}})); + defer pop_parent(ctx); - button("first inner most button"); - button("inner_button2"); - button("inner_button3"); + push_box(ctx, "1", {.DrawText, .DrawBackground, .DrawBorder}, semantic_size = {make_semantic_size(.Fill, 100), { .FitText, 256}}) + push_box(ctx, "2", {.DrawText, .DrawBackground, .DrawBorder}, semantic_size = {make_semantic_size(.Fill, 100), { .FitText, 256}}) + + { + push_parent(ctx, push_box(ctx, "two_button_container_inner_inner", {.DrawBorder}, .Vertical, semantic_size = {make_semantic_size(.Fill, 50), { .ChildrenSum, 256}})); + defer pop_parent(ctx); + + button(ctx, "this is a test button"); + button(ctx, "me in the middle"); + button(ctx, "look at me, I'm a test button too"); + } + + push_box(ctx, "End", {.DrawBorder, .DrawBackground, .DrawText}, .Horizontal, semantic_size = {make_semantic_size(.Fill, 0), { .FitText, 0}}) } - button("inner_button3"); - - pop_parent(); - button("Help me I'm falling"); - pop_parent(); + button(ctx, "Help me I'm falling"); + pop_parent(ctx); } diff --git a/src/ui/ui.odin b/src/ui/ui.odin index 5d22ab4..b540905 100644 --- a/src/ui/ui.odin +++ b/src/ui/ui.odin @@ -1,152 +1,151 @@ package ui import "core:math" -import "vendor:raylib" import "../core" import "../theme" -MenuBarItemOnClick :: proc(state: ^core.State, item: ^MenuBarItem); - -text_padding :: 4; - -MenuBarItem :: struct { - text: string, - selected: bool, - sub_items: []MenuBarItem, - on_click: MenuBarItemOnClick, -} - -MenuBarState :: struct { - items: []MenuBarItem, -} - -draw_menu_bar_item :: proc(state: ^core.State, item: ^MenuBarItem, x, y: i32, parent_width, parent_height: i32, font_height: int, horizontal: bool = false) { - foreground_color := theme.PaletteColor.Foreground3; - if horizontal { - if item.selected { - foreground_color = theme.PaletteColor.Background4; - } else { - foreground_color = theme.PaletteColor.Foreground4; - } - } - - item_text := raylib.TextFormat("%s", item.text); - item_width := raylib.MeasureTextEx(state.font, item_text, f32(font_height), 0).x; - - raylib.DrawRectangle(x, y, parent_width, i32(font_height), theme.get_palette_raylib_color(foreground_color)); - raylib.DrawTextEx(state.font, item_text, raylib.Vector2 { f32(x + text_padding), f32(y) }, f32(font_height), 0, theme.get_palette_raylib_color(.Background1)); - - if item.selected { - // TODO: change to parent_width - largest_sub_item: int - for sub_item in item.sub_items { - largest_sub_item = math.max(len(sub_item.text), largest_sub_item); - } - - this_width := i32(largest_sub_item * state.source_font_width + text_padding*2); - sub_list_x := x; - if horizontal { - sub_list_x += parent_width; - } - for _, index in item.sub_items { - sub_item := &item.sub_items[index]; - item_text := raylib.TextFormat("%s", sub_item.text); - item_width := raylib.MeasureTextEx(state.font, item_text, f32(font_height), 0).x; - - index_offset := 1; - if horizontal { - index_offset = 0; - } - item_y := y + i32(font_height * (index+index_offset)); - draw_menu_bar_item(state, sub_item, sub_list_x, item_y, this_width, 0, font_height, true); - } - } -} - -draw_menu_bar :: proc(state: ^core.State, data: ^MenuBarState, x, y: i32, parent_width, parent_height: i32, font_height: int) { - raylib.DrawRectangle(x, y, parent_width, i32(font_height), theme.get_palette_raylib_color(.Background3)); - - raylib.DrawTextEx(state.font, "Editor", raylib.Vector2 { f32(x), f32(y) }, f32(font_height), 0, theme.get_palette_raylib_color(.Foreground1)); - - x := x + i32((len("Editor") + 4) * state.source_font_width); - - for _, index in data.items { - item := &data.items[index]; - item_text := raylib.TextFormat("%s", item.text); - item_width := raylib.MeasureTextEx(state.font, item_text, f32(font_height), 0).x; - - item_x := x + (i32(item_width) + text_padding*2) * i32(index); - draw_menu_bar_item(state, item, item_x, y, i32(item_width + text_padding*2), i32(font_height), font_height); - } -} - -test_menu_item :: proc(state: ^core.State, item: ^MenuBarItem, rect: raylib.Rectangle, mouse_pos: raylib.Vector2, mouse_has_clicked: bool, font_height: int, horizontal: bool) -> bool { - if raylib.CheckCollisionPointRec(mouse_pos, rect) { - item.selected = true; - - if item.on_click != nil && mouse_has_clicked { - item.on_click(state, item); - } - } else if item.selected { - largest_sub_item: int - for sub_item in item.sub_items { - largest_sub_item = math.max(len(sub_item.text), largest_sub_item); - } - - this_width := i32(largest_sub_item * state.source_font_width + text_padding*2); - sub_list_x := rect.x; - if horizontal { - sub_list_x += rect.width; - } - - has_sub_item_selected := false; - for _, index in item.sub_items { - sub_item := &item.sub_items[index]; - item_text := raylib.TextFormat("%s", sub_item.text); - item_width := raylib.MeasureTextEx(state.font, item_text, f32(font_height), 0).x; - - index_offset := 1; - if horizontal { - index_offset = 0; - } - item_y := rect.y + f32(font_height * (index+index_offset)); - - sub_rec := raylib.Rectangle { - x = sub_list_x, - y = item_y, - width = f32(this_width), - height = f32(font_height), - }; - - if test_menu_item(state, sub_item, sub_rec, mouse_pos, mouse_has_clicked, font_height, true) { - has_sub_item_selected = true; - } - } - - item.selected = has_sub_item_selected; - } else { - item.selected = false; - } - - return item.selected; -} - -test_menu_bar :: proc(state: ^core.State, menu_bar: ^MenuBarState, x, y: i32, mouse_pos: raylib.Vector2, mouse_has_clicked: bool, font_height: int) { - x := x + i32((len("Editor") + 4) * state.source_font_width); - - for _, index in menu_bar.items { - item := &menu_bar.items[index]; - item_text := raylib.TextFormat("%s", item.text); - item_width := raylib.MeasureTextEx(state.font, item_text, f32(font_height), 0).x; - - item_rec := raylib.Rectangle { - x = f32(x) + (item_width + f32(text_padding*2)) * f32(index), - y = f32(y), - width = f32(item_width + text_padding*2), - height = f32(font_height), - }; - - test_menu_item(state, item, item_rec, mouse_pos, mouse_has_clicked, font_height, false); - } -} +// MenuBarItemOnClick :: proc(state: ^core.State, item: ^MenuBarItem); +// +// text_padding :: 4; +// +// MenuBarItem :: struct { +// text: string, +// selected: bool, +// sub_items: []MenuBarItem, +// on_click: MenuBarItemOnClick, +// } +// +// MenuBarState :: struct { +// items: []MenuBarItem, +// } +// +// draw_menu_bar_item :: proc(state: ^core.State, item: ^MenuBarItem, x, y: i32, parent_width, parent_height: i32, font_height: int, horizontal: bool = false) { +// foreground_color := theme.PaletteColor.Foreground3; +// if horizontal { +// if item.selected { +// foreground_color = theme.PaletteColor.Background4; +// } else { +// foreground_color = theme.PaletteColor.Foreground4; +// } +// } +// +// item_text := raylib.TextFormat("%s", item.text); +// item_width := raylib.MeasureTextEx(state.font, item_text, f32(font_height), 0).x; +// +// raylib.DrawRectangle(x, y, parent_width, i32(font_height), theme.get_palette_raylib_color(foreground_color)); +// raylib.DrawTextEx(state.font, item_text, raylib.Vector2 { f32(x + text_padding), f32(y) }, f32(font_height), 0, theme.get_palette_raylib_color(.Background1)); +// +// if item.selected { +// // TODO: change to parent_width +// largest_sub_item: int +// for sub_item in item.sub_items { +// largest_sub_item = math.max(len(sub_item.text), largest_sub_item); +// } +// +// this_width := i32(largest_sub_item * state.source_font_width + text_padding*2); +// sub_list_x := x; +// if horizontal { +// sub_list_x += parent_width; +// } +// for _, index in item.sub_items { +// sub_item := &item.sub_items[index]; +// item_text := raylib.TextFormat("%s", sub_item.text); +// item_width := raylib.MeasureTextEx(state.font, item_text, f32(font_height), 0).x; +// +// index_offset := 1; +// if horizontal { +// index_offset = 0; +// } +// item_y := y + i32(font_height * (index+index_offset)); +// draw_menu_bar_item(state, sub_item, sub_list_x, item_y, this_width, 0, font_height, true); +// } +// } +// } +// +// draw_menu_bar :: proc(state: ^core.State, data: ^MenuBarState, x, y: i32, parent_width, parent_height: i32, font_height: int) { +// raylib.DrawRectangle(x, y, parent_width, i32(font_height), theme.get_palette_raylib_color(.Background3)); +// +// raylib.DrawTextEx(state.font, "Editor", raylib.Vector2 { f32(x), f32(y) }, f32(font_height), 0, theme.get_palette_raylib_color(.Foreground1)); +// +// x := x + i32((len("Editor") + 4) * state.source_font_width); +// +// for _, index in data.items { +// item := &data.items[index]; +// item_text := raylib.TextFormat("%s", item.text); +// item_width := raylib.MeasureTextEx(state.font, item_text, f32(font_height), 0).x; +// +// item_x := x + (i32(item_width) + text_padding*2) * i32(index); +// draw_menu_bar_item(state, item, item_x, y, i32(item_width + text_padding*2), i32(font_height), font_height); +// } +// } +// +// test_menu_item :: proc(state: ^core.State, item: ^MenuBarItem, rect: raylib.Rectangle, mouse_pos: raylib.Vector2, mouse_has_clicked: bool, font_height: int, horizontal: bool) -> bool { +// if raylib.CheckCollisionPointRec(mouse_pos, rect) { +// item.selected = true; +// +// if item.on_click != nil && mouse_has_clicked { +// item.on_click(state, item); +// } +// } else if item.selected { +// largest_sub_item: int +// for sub_item in item.sub_items { +// largest_sub_item = math.max(len(sub_item.text), largest_sub_item); +// } +// +// this_width := i32(largest_sub_item * state.source_font_width + text_padding*2); +// sub_list_x := rect.x; +// if horizontal { +// sub_list_x += rect.width; +// } +// +// has_sub_item_selected := false; +// for _, index in item.sub_items { +// sub_item := &item.sub_items[index]; +// item_text := raylib.TextFormat("%s", sub_item.text); +// item_width := raylib.MeasureTextEx(state.font, item_text, f32(font_height), 0).x; +// +// index_offset := 1; +// if horizontal { +// index_offset = 0; +// } +// item_y := rect.y + f32(font_height * (index+index_offset)); +// +// sub_rec := raylib.Rectangle { +// x = sub_list_x, +// y = item_y, +// width = f32(this_width), +// height = f32(font_height), +// }; +// +// if test_menu_item(state, sub_item, sub_rec, mouse_pos, mouse_has_clicked, font_height, true) { +// has_sub_item_selected = true; +// } +// } +// +// item.selected = has_sub_item_selected; +// } else { +// item.selected = false; +// } +// +// return item.selected; +// } +// +// test_menu_bar :: proc(state: ^core.State, menu_bar: ^MenuBarState, x, y: i32, mouse_pos: raylib.Vector2, mouse_has_clicked: bool, font_height: int) { +// x := x + i32((len("Editor") + 4) * state.source_font_width); +// +// for _, index in menu_bar.items { +// item := &menu_bar.items[index]; +// item_text := raylib.TextFormat("%s", item.text); +// item_width := raylib.MeasureTextEx(state.font, item_text, f32(font_height), 0).x; +// +// item_rec := raylib.Rectangle { +// x = f32(x) + (item_width + f32(text_padding*2)) * f32(index), +// y = f32(y), +// width = f32(item_width + text_padding*2), +// height = f32(font_height), +// }; +// +// test_menu_item(state, item, item_rec, mouse_pos, mouse_has_clicked, font_height, false); +// } +// } From de908cfe06e6fc10fa0525475d2f57ad1dd7d04a Mon Sep 17 00:00:00 2001 From: Patrick Cleavelin Date: Fri, 26 Jan 2024 01:32:16 -0600 Subject: [PATCH 3/6] get plugin interface sorta working with the ui lib --- Makefile | 2 +- flake.lock | 18 +- flake.nix | 8 + plugin-rs-bindings/src/lib.rs | 133 +++++++++++++ plugins/buffer_search/plugin.odin | 48 +++++ plugins/grep/rust-toolchain.toml | 3 + plugins/grep/src/lib.rs | 215 +++++++++++--------- plugins/highlighter/src/plugin.odin | 3 + src/core/gfx.odin | 2 +- src/main.odin | 292 +++++++++++++++++++++------- src/plugin/plugin.odin | 42 ++++ src/ui/imm.odin | 69 +++++-- 12 files changed, 646 insertions(+), 189 deletions(-) create mode 100644 plugins/grep/rust-toolchain.toml diff --git a/Makefile b/Makefile index 5926359..ab9378d 100644 --- a/Makefile +++ b/Makefile @@ -9,5 +9,5 @@ odin_highlighter: odin build plugins/highlighter/src/ -build-mode:dll -no-entry-point -out:bin/highlighter grep: - cargo b --manifest-path=plugins/grep/Cargo.toml + nightly-cargo b --manifest-path=plugins/grep/Cargo.toml cp plugins/grep/target/debug/libgrep_plugin.dylib bin/ diff --git a/flake.lock b/flake.lock index 6b846bc..7dbe085 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", "owner": "numtide", "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", "type": "github" }, "original": { @@ -87,11 +87,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1703013332, - "narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=", + "lastModified": 1705856552, + "narHash": "sha256-JXfnuEf5Yd6bhMs/uvM67/joxYKoysyE3M2k6T3eWbg=", "owner": "nixos", "repo": "nixpkgs", - "rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6", + "rev": "612f97239e2cc474c13c9dafa0df378058c5ad8d", "type": "github" }, "original": { @@ -131,11 +131,11 @@ "nixpkgs": "nixpkgs_3" }, "locked": { - "lastModified": 1704075545, - "narHash": "sha256-L3zgOuVKhPjKsVLc3yTm2YJ6+BATyZBury7wnhyc8QU=", + "lastModified": 1706235145, + "narHash": "sha256-3jh5nahTlcsX6QFcMPqxtLn9p9CgT9RSce5GLqjcpi4=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "a0df72e106322b67e9c6e591fe870380bd0da0d5", + "rev": "3a57c4e29cb2beb777b2e6ae7309a680585b8b2f", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 2dbf713..c456029 100755 --- a/flake.nix +++ b/flake.nix @@ -16,6 +16,13 @@ local-rust = (pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain).override { extensions = [ "rust-analysis" ]; }; + local-nightly-rust = (pkgs.rust-bin.fromRustupToolchainFile ./plugins/grep/rust-toolchain.toml).override { + extensions = [ "rust-analysis" ]; + }; + nightly-cargo = pkgs.writeShellScriptBin "nightly-cargo" '' + export RUSTC="${local-nightly-rust}/bin/rustc"; + exec "${local-nightly-rust}/bin/cargo" "$@" + ''; fixed-odin = pkgs.odin.overrideAttrs (finalAttrs: prevAttr: rec { src = pkgs.fetchFromGitHub { owner = "pcleavelin"; @@ -58,6 +65,7 @@ buildInputs = with pkgs; (if pkgs.system == "aarch64-darwin" || pkgs.system == "x86_64-darwin" then [ fixed-odin local-rust + nightly-cargo rust-analyzer SDL2 SDL2_ttf diff --git a/plugin-rs-bindings/src/lib.rs b/plugin-rs-bindings/src/lib.rs index 7e4a78a..8b919af 100644 --- a/plugin-rs-bindings/src/lib.rs +++ b/plugin-rs-bindings/src/lib.rs @@ -166,6 +166,137 @@ pub struct IteratorVTable { pub until_end_of_word: *const c_void, } +#[repr(C)] +pub struct UiInteraction { + pub hovering: bool, + pub clicked: bool, +} + +#[repr(C)] +struct InternalUiSemanticSize { + kind: isize, + value: isize, +} + +#[repr(isize)] +pub enum UiAxis { + Horizontal = 0, + Vertical, +} + +pub enum UiSemanticSize { + FitText, + Exact(isize), + ChildrenSum, + Fill, + PercentOfParent(isize), +} + +impl From for InternalUiSemanticSize { + fn from(value: UiSemanticSize) -> Self { + let (kind, value) = match value { + UiSemanticSize::FitText => (0, 0), + UiSemanticSize::Exact(value) => (1, value), + UiSemanticSize::ChildrenSum => (2, 0), + UiSemanticSize::Fill => (3, 0), + UiSemanticSize::PercentOfParent(value) => (4, value), + }; + + Self { kind, value } + } +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct UiContext(*const c_void); + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct UiBox(*const c_void); + +type UiPushParentProc = extern "C" fn(ui_context: UiContext, ui_box: UiBox); +type UiPopParentProc = extern "C" fn(ui_context: UiContext); +type UiFloatingProc = + extern "C" fn(ui_context: UiContext, label: *const i8, pos: [isize; 2]) -> UiBox; +type UiRectProc = extern "C" fn( + ui_context: UiContext, + label: *const i8, + border: bool, + axis: UiAxis, + size: [InternalUiSemanticSize; 2], +) -> UiBox; +type UiSimpleProc = extern "C" fn(ui_context: UiContext, label: *const i8) -> UiInteraction; +type UiBufferProc = extern "C" fn(ui_context: UiContext, buffer: Buffer, show_line_numbers: bool); + +#[repr(C)] +pub struct UiVTable { + ui_context: UiContext, + + push_parent: UiPushParentProc, + pop_parent: UiPopParentProc, + + floating: UiFloatingProc, + rect: UiRectProc, + + button: UiSimpleProc, + label: UiSimpleProc, + + buffer: UiBufferProc, + buffer_from_index: UiBufferProc, +} + +impl UiVTable { + pub fn push_parent(&self, ui_box: UiBox) { + (self.push_parent)(self.ui_context, ui_box); + } + pub fn pop_parent(&self) { + (self.pop_parent)(self.ui_context); + } + + pub fn push_rect( + &self, + label: &CStr, + show_border: bool, + axis: UiAxis, + horizontal_size: UiSemanticSize, + vertical_size: UiSemanticSize, + inner: impl FnOnce(&UiVTable), + ) { + let rect = (self.rect)( + self.ui_context, + label.as_ptr(), + show_border, + axis, + [horizontal_size.into(), vertical_size.into()], + ); + self.push_parent(rect); + + inner(self); + + self.pop_parent(); + } + + pub fn push_floating(&self, label: &CStr, x: isize, y: isize, inner: impl FnOnce(&UiVTable)) { + let floating = (self.floating)(self.ui_context, label.as_ptr(), [x, y]); + self.push_parent(floating); + + inner(self); + + self.pop_parent(); + } + + pub fn label(&self, label: &CStr) -> UiInteraction { + (self.label)(self.ui_context, label.as_ptr()) + } + pub fn button(&self, label: &CStr) -> UiInteraction { + (self.button)(self.ui_context, label.as_ptr()) + } + + pub fn buffer(&self, buffer: Buffer, show_line_numbers: bool) { + (self.buffer)(self.ui_context, buffer, show_line_numbers) + } +} + type OnColorBufferProc = extern "C" fn(plugin: Plugin, buffer: *const c_void); type OnHookProc = extern "C" fn(plugin: Plugin, buffer: Buffer); type InputGroupProc = extern "C" fn(plugin: Plugin, input_map: InputMap); @@ -176,8 +307,10 @@ type WindowGetBufferProc = extern "C" fn(plugin: Plugin, window: *const c_void) #[repr(C)] pub struct Plugin { state: *const c_void, + pub iter_table: IteratorVTable, pub buffer_table: BufferVTable, + pub ui_table: UiVTable, pub register_hook: extern "C" fn(hook: Hook, on_hook: OnHookProc), pub register_highlighter: diff --git a/plugins/buffer_search/plugin.odin b/plugins/buffer_search/plugin.odin index 3bc992c..b0f7f9a 100644 --- a/plugins/buffer_search/plugin.odin +++ b/plugins/buffer_search/plugin.odin @@ -98,11 +98,58 @@ buffer_list_iter :: proc(plugin: Plugin, buffer_index: ^int) -> (int, int, bool) draw_buffer_window :: proc "c" (plugin: Plugin, win: rawptr) { context = runtime.default_context(); + runtime.free_all(context.temp_allocator); + win := cast(^BufferListWindow)win; if win == nil { return; } + screen_width := plugin.get_screen_width(); + screen_height := plugin.get_screen_height(); + directory := string(plugin.get_current_directory()); + + canvas := plugin.ui.floating(plugin.ui.ui_context, "buffer search canvas", {screen_width/8, screen_height/8}); + + plugin.ui.push_parent(plugin.ui.ui_context, canvas); + { + defer plugin.ui.pop_parent(plugin.ui.ui_context); + + ui_window := plugin.ui.rect(plugin.ui.ui_context, "buffer search window", true, .Horizontal, {{4, 75}, {4, 75}}); + plugin.ui.push_parent(plugin.ui.ui_context, ui_window); + { + defer plugin.ui.pop_parent(plugin.ui.ui_context); + + buffer_list_view := plugin.ui.rect(plugin.ui.ui_context, "buffer list view", false, .Vertical, {{4, 60}, {3, 0}}); + plugin.ui.push_parent(plugin.ui.ui_context, buffer_list_view); + { + defer plugin.ui.pop_parent(plugin.ui.ui_context); + + _buffer_index := 0; + for index in buffer_list_iter(plugin, &_buffer_index) { + buffer := plugin.buffer.get_buffer_info_from_index(index); + relative_file_path, _ := filepath.rel(directory, string(buffer.file_path), context.temp_allocator) + text := fmt.ctprintf("%s:%d", relative_file_path, buffer.cursor.line+1); + + if index == win.selected_index { + plugin.ui.button(plugin.ui.ui_context, text); + } else { + plugin.ui.label(plugin.ui.ui_context, text); + } + } + } + + buffer_preview := plugin.ui.rect(plugin.ui.ui_context, "buffer preview", false, .Horizontal, {{3, 0}, {3, 0}}); + plugin.ui.push_parent(plugin.ui.ui_context, buffer_preview); + { + defer plugin.ui.pop_parent(plugin.ui.ui_context); + + plugin.ui.buffer_from_index(plugin.ui.ui_context, win.selected_index, false); + } + } + } + + /* screen_width := plugin.get_screen_width(); screen_height := plugin.get_screen_height(); source_font_width := plugin.get_font_width(); @@ -173,4 +220,5 @@ draw_buffer_window :: proc "c" (plugin: Plugin, win: rawptr) { runtime.free_all(context.temp_allocator); } + */ } diff --git a/plugins/grep/rust-toolchain.toml b/plugins/grep/rust-toolchain.toml new file mode 100644 index 0000000..98d1e3f --- /dev/null +++ b/plugins/grep/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2024-01-24" + diff --git a/plugins/grep/src/lib.rs b/plugins/grep/src/lib.rs index 74e5c3b..d51859d 100644 --- a/plugins/grep/src/lib.rs +++ b/plugins/grep/src/lib.rs @@ -1,6 +1,6 @@ use std::{ error::Error, - ffi::OsString, + ffi::{CString, OsString}, path::Path, str::FromStr, sync::mpsc::{Receiver, Sender}, @@ -11,7 +11,7 @@ use grep::{ regex::RegexMatcherBuilder, searcher::{BinaryDetection, SearcherBuilder, Sink, SinkError}, }; -use plugin_rs_bindings::{Buffer, Closure, Hook, InputMap, Key, PaletteColor, Plugin}; +use plugin_rs_bindings::{Buffer, Closure, Hook, InputMap, Key, Plugin, UiAxis, UiSemanticSize}; use std::sync::mpsc::channel; use walkdir::WalkDir; @@ -273,109 +273,142 @@ extern "C" fn draw_window(plugin: Plugin, window: *const std::ffi::c_void) { let screen_width = (plugin.get_screen_width)() as i32; let screen_height = (plugin.get_screen_height)() as i32; - let font_width = (plugin.get_font_width)() as i32; + let font_height = (plugin.get_font_height)() as i32; - let x = screen_width / 8; - let y = screen_height / 8; - let width = screen_width - screen_width / 4; let height = screen_height - screen_height / 4; - let buffer_prev_width = (width - font_width * 2) / 2; - - let glyph_buffer_width = buffer_prev_width / font_width - 1; - let glyph_buffer_height = 1; - let dir = plugin.get_current_directory(); let directory = Path::new(dir.as_ref()); - (plugin.draw_rect)(x, y, width, height, PaletteColor::Background4); - (plugin.draw_rect)( - x + font_width, - y + font_height, - width - font_width * 2, - height - font_height * 3, - PaletteColor::Background3, - ); + plugin.ui_table.push_floating( + c"grep canvas", + (screen_width as isize) / 8, + (screen_height as isize) / 8, + |ui_table| { + ui_table.push_rect( + c"grep window", + true, + UiAxis::Vertical, + UiSemanticSize::PercentOfParent(75), + UiSemanticSize::PercentOfParent(75), + |ui_table| { + if let Ok(sink) = window.rx.try_recv() { + window.sink = Some(sink); + } - if let Some(buffer) = window.input_buffer { - (plugin.draw_rect)( - x + font_width, - y + height - font_height * 2, - buffer_prev_width, - font_height, - PaletteColor::Background2, - ); - (plugin.draw_buffer)( - buffer, - (x + font_width) as isize, - (y + height - font_height * 2) as isize, - (glyph_buffer_width) as isize, - (glyph_buffer_height) as isize, - false, - ); - } + ui_table.push_rect( + c"results list", + false, + UiAxis::Vertical, + UiSemanticSize::Fill, + UiSemanticSize::Fill, + |ui_table| match &window.sink { + Some(sink) if !sink.matches.is_empty() => { + let num_mats_to_draw = std::cmp::min( + (sink.matches.len() - window.top_index) as i32, + (height - font_height) / (font_height), + ); - if let Ok(sink) = window.rx.try_recv() { - window.sink = Some(sink); - } + for (i, mat) in sink.matches[window.top_index..].iter().enumerate() + { + let index = i + window.top_index; + if i as i32 >= num_mats_to_draw { + break; + } - if let Some(sink) = &window.sink { - if !sink.matches.is_empty() { - let num_mats_to_draw = std::cmp::min( - (sink.matches.len() - window.top_index) as i32, - (height - font_height * 2) / (font_height) - 1, - ); - let max_mat_length = (width - font_width * 2) / font_width; + let path = Path::new(&mat.path); + let relative_file_path = path + .strip_prefix(directory) + .unwrap_or(path) + .to_str() + .unwrap_or(""); - for (i, mat) in sink.matches[window.top_index..].iter().enumerate() { - let index = i + window.top_index; - if i as i32 >= num_mats_to_draw { - break; - } + let matched_text = String::from_utf8_lossy(&mat.text); + let text = match mat.line_number { + Some(line_number) => format!( + "{}:{}:{}: {}", + relative_file_path, + line_number, + mat.column, + matched_text + ), + None => format!( + "{}:{}: {}", + relative_file_path, mat.column, matched_text + ), + }; - let path = Path::new(&mat.path); - let relative_file_path = path - .strip_prefix(directory) - .unwrap_or(path) - .to_str() - .unwrap_or(""); - - let matched_text = String::from_utf8_lossy(&mat.text); - let text = match mat.line_number { - Some(line_number) => format!( - "{}:{}:{}: {}", - relative_file_path, line_number, mat.column, matched_text - ), - None => format!("{}:{}: {}", relative_file_path, mat.column, matched_text), - }; - let text = if text.len() > max_mat_length as usize { - text.as_str().split_at(max_mat_length as usize).0 - } else { - &text - }; - - let text = format!("{text}\0"); - - if index == window.selected_match { - (plugin.draw_rect)( - x + font_width, - y + font_height + ((index - window.top_index) as i32) * font_height, - (text.chars().count() as i32) * font_width, - font_height, - PaletteColor::Background2, + if index == window.selected_match { + ui_table.button(&CString::new(text).expect("valid text")); + } else { + ui_table.label(&CString::new(text).expect("valid text")); + } + } + } + Some(_) | None => { + ui_table.push_rect( + c"top spacer", + false, + UiAxis::Vertical, + UiSemanticSize::Fill, + UiSemanticSize::Fill, + |ui_table| {}, + ); + ui_table.push_rect( + c"centered text container", + false, + UiAxis::Horizontal, + UiSemanticSize::Fill, + UiSemanticSize::Fill, + |ui_table| { + ui_table.push_rect( + c"left spacer", + false, + UiAxis::Vertical, + UiSemanticSize::Fill, + UiSemanticSize::Fill, + |ui_table| {}, + ); + ui_table.label(c"no results"); + ui_table.push_rect( + c"right spacer", + false, + UiAxis::Vertical, + UiSemanticSize::Fill, + UiSemanticSize::Fill, + |ui_table| {}, + ); + }, + ); + ui_table.push_rect( + c"bottom spacer", + false, + UiAxis::Vertical, + UiSemanticSize::Fill, + UiSemanticSize::Fill, + |ui_table| {}, + ); + } + }, ); - } - (plugin.draw_text)( - text.as_ptr() as *const i8, - (x + font_width) as f32, - (y + font_height + ((index - window.top_index) as i32) * font_height) as f32, - PaletteColor::Foreground2, - ); - } - } - } + ui_table.push_rect( + c"grep window", + false, + UiAxis::Vertical, + UiSemanticSize::Fill, + UiSemanticSize::Exact(font_height as isize), + |ui_table| { + if let Some(buffer) = window.input_buffer { + ui_table.buffer(buffer, false); + } + }, + ); + }, + ); + }, + ); } extern "C" fn on_buffer_input(plugin: Plugin, buffer: Buffer) { diff --git a/plugins/highlighter/src/plugin.odin b/plugins/highlighter/src/plugin.odin index 1b50204..fa98094 100644 --- a/plugins/highlighter/src/plugin.odin +++ b/plugins/highlighter/src/plugin.odin @@ -277,6 +277,9 @@ is_rust_keyword :: proc(plugin: Plugin, start: BufferIter, end: BufferIter) -> ( return; } +// TODO: split logic into single line coloring, and multi-line coloring. +// single line coloring can be done directly on the glyph buffer +// (with some edge cases, literally, the edge of the screen) color_buffer_odin :: proc "c" (plugin: Plugin, buffer: rawptr) { context = runtime.default_context(); diff --git a/src/core/gfx.odin b/src/core/gfx.odin index 1cb2a67..e176698 100644 --- a/src/core/gfx.odin +++ b/src/core/gfx.odin @@ -6,7 +6,7 @@ import "vendor:sdl2/ttf" import "../theme" -scale :: 2; +scale :: 1; start_char :: ' '; end_char :: '~'; diff --git a/src/main.odin b/src/main.odin index 266dbfd..650ae39 100644 --- a/src/main.odin +++ b/src/main.odin @@ -162,6 +162,7 @@ register_default_input_actions :: proc(input_map: ^core.InputMap) { // Scale font size { core.register_ctrl_key_action(input_map, .MINUS, proc(state: ^State) { + fmt.print("You pressed -MINUS", state.source_font_height, " "); if state.source_font_height > 16 { state.source_font_height -= 2; state.source_font_width = state.source_font_height / 2; @@ -170,8 +171,11 @@ register_default_input_actions :: proc(input_map: ^core.InputMap) { //state.font = raylib.LoadFontEx("/System/Library/Fonts/Supplemental/Andale Mono.ttf", i32(state.source_font_height*2), nil, 0); //raylib.SetTextureFilter(state.font.texture, .BILINEAR); } + fmt.println(state.source_font_height); }, "increase font size"); core.register_ctrl_key_action(input_map, .EQUAL, proc(state: ^State) { + fmt.println("You pressed -EQUAL"); + state.source_font_height += 2; state.source_font_width = state.source_font_height / 2; @@ -185,6 +189,7 @@ register_default_input_actions :: proc(input_map: ^core.InputMap) { { core.register_key_action(input_map, .I, proc(state: ^State) { state.mode = .Insert; + sdl2.StartTextInput(); }, "enter insert mode"); core.register_key_action(input_map, .A, proc(state: ^State) { core.move_cursor_right(&state.buffers[state.current_buffer], false); @@ -240,19 +245,12 @@ draw :: proc(state_with_ui: ^StateWithUi) { sdl2.SetRenderDrawColor(state_with_ui.state.sdl_renderer, render_color.r, render_color.g, render_color.b, render_color.a); sdl2.RenderClear(state_with_ui.state.sdl_renderer); - // raylib.ClearBackground(theme.get_palette_raylib_color(.Background)); - - // core.draw_file_buffer(state_with_ui.state, buffer, 32, state_with_ui.state.source_font_height); + // if state_with_ui.state.window != nil && state_with_ui.state.window.draw != nil { + // state_with_ui.state.window.draw(state_with_ui.state.plugin_vtable, state_with_ui.state.window.user_data); + // } ui.compute_layout(state_with_ui.ui_context, { state_with_ui.state.screen_width, state_with_ui.state.screen_height }, state_with_ui.state.source_font_width, state_with_ui.state.source_font_height, state_with_ui.ui_context.root); ui.draw(state_with_ui.ui_context, state_with_ui.state, state_with_ui.state.source_font_width, state_with_ui.state.source_font_height, state_with_ui.ui_context.root); - //ui.draw_menu_bar(&state_with_ui.state, &menu_bar_state_with_ui.state, 0, 0, i32(state_with_ui.state.screen_width), i32(state_with_ui.state.screen_height), state_with_ui.state.source_font_height); - - //raylib.DrawRectangle(0, i32(state_with_ui.state.screen_height - state_with_ui.state.source_font_height), i32(state_with_ui.state.screen_width), i32(state_with_ui.state.source_font_height), theme.get_palette_raylib_color(.Background2)); - - if state_with_ui.state.window != nil && state_with_ui.state.window.draw != nil { - state_with_ui.state.window.draw(state_with_ui.state.plugin_vtable, state_with_ui.state.window.user_data); - } if state_with_ui.state.current_input_map != &state_with_ui.state.input_map { longest_description := 0; @@ -375,20 +373,8 @@ ui_file_buffer :: proc(ctx: ^ui.Context, buffer: ^FileBuffer) -> ui.Interaction return interaction; } -main :: proc() { - state = State { - ctx = context, - source_font_width = 8 + 2 * 3, - source_font_height = 16 + 2 * 3, - input_map = core.new_input_map(), - window = nil, - directory = os.get_current_directory(), - plugins = make([dynamic]plugin.Interface), - highlighters = make(map[string]plugin.OnColorBufferProc), - hooks = make(map[plugin.Hook][dynamic]plugin.OnHookProc), - }; - - state.plugin_vtable = plugin.Plugin { +init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { + return plugin.Plugin { state = cast(rawptr)&state, register_hook = proc "c" (hook: plugin.Hook, on_hook: plugin.OnHookProc) { context = state.ctx; @@ -507,6 +493,7 @@ main :: proc() { }, enter_insert_mode = proc "c" () { state.mode = .Insert; + sdl2.StartTextInput(); }, draw_rect = proc "c" (x: i32, y: i32, width: i32, height: i32, color: theme.PaletteColor) { context = state.ctx; @@ -864,8 +851,109 @@ main :: proc() { free(buffer); } }, - } + }, + ui = plugin.Ui { + ui_context = ui_context, + + push_parent = proc "c" (ui_context: rawptr, box: plugin.UiBox) { + context = state.ctx; + ui_context := transmute(^ui.Context)ui_context; + box := transmute(^ui.Box)box; + + ui.push_parent(ui_context, box); + }, + + pop_parent = proc "c" (ui_context: rawptr) { + context = state.ctx; + ui_context := transmute(^ui.Context)ui_context; + + ui.pop_parent(ui_context); + }, + + // TODO: allow this to have more flags sent to it + floating = proc "c" (ui_context: rawptr, label: cstring, pos: [2]int) -> plugin.UiBox { + context = state.ctx; + ui_context := transmute(^ui.Context)ui_context; + label := strings.clone(string(label), context.temp_allocator); + + return ui.push_floating(ui_context, label, pos); + }, + rect = proc "c" (ui_context: rawptr, label: cstring, border: bool, axis: plugin.UiAxis, size: [2]plugin.UiSemanticSize) -> plugin.UiBox { + context = state.ctx; + ui_context := transmute(^ui.Context)ui_context; + label := strings.clone(string(label), context.temp_allocator); + + size := [2]ui.SemanticSize { + ui.SemanticSize { + kind = ui.SemanticSizeKind(size.x.kind), + value = size.x.value, + }, + ui.SemanticSize { + kind = ui.SemanticSizeKind(size.y.kind), + value = size.y.value, + }, + }; + + return ui.push_rect(ui_context, label, border, ui.Axis(axis), size); + }, + + label = proc "c" (ui_context: rawptr, label: cstring) -> plugin.UiInteraction { + context = state.ctx; + ui_context := transmute(^ui.Context)ui_context; + label := strings.clone(string(label), context.temp_allocator); + + interaction := ui.label(ui_context, label); + + return plugin.UiInteraction { + hovering = interaction.hovering, + clicked = interaction.clicked, + }; + }, + button = proc "c" (ui_context: rawptr, label: cstring) -> plugin.UiInteraction { + context = state.ctx; + ui_context := transmute(^ui.Context)ui_context; + label := strings.clone(string(label), context.temp_allocator); + + interaction := ui.button(ui_context, label); + + return plugin.UiInteraction { + hovering = interaction.hovering, + clicked = interaction.clicked, + }; + }, + buffer = proc "c" (ui_context: rawptr, buffer: rawptr, show_line_numbers: bool) { + context = state.ctx; + ui_context := transmute(^ui.Context)ui_context; + buffer := transmute(^FileBuffer)buffer; + + ui_file_buffer(ui_context, buffer); + }, + + buffer_from_index = proc "c" (ui_context: rawptr, buffer: int, show_line_numbers: bool) { + context = state.ctx; + ui_context := transmute(^ui.Context)ui_context; + + buffer := &state.buffers[buffer]; + + ui_file_buffer(ui_context, buffer); + }, + }, }; +} + +main :: proc() { + state = State { + ctx = context, + source_font_width = 8 + 2 * 3, + source_font_height = 16 + 2 * 3, + input_map = core.new_input_map(), + window = nil, + directory = os.get_current_directory(), + plugins = make([dynamic]plugin.Interface), + highlighters = make(map[string]plugin.OnColorBufferProc), + hooks = make(map[plugin.Hook][dynamic]plugin.OnHookProc), + }; + state.current_input_map = &state.input_map; register_default_input_actions(&state.input_map); @@ -879,16 +967,6 @@ main :: proc() { runtime.append(&state.buffers, buffer); } - // Load plugins - // TODO(pcleavelin): Get directory of binary instead of shells current working directory - filepath.walk(filepath.join({ os.get_current_directory(), "bin" }), load_plugin, transmute(rawptr)&state); - - for plugin in state.plugins { - if plugin.on_initialize != nil { - plugin.on_initialize(state.plugin_vtable); - } - } - if sdl2.Init({.VIDEO}) < 0 { fmt.eprintln("SDL failed to initialize:", sdl2.GetError()); return; @@ -936,18 +1014,22 @@ main :: proc() { } } + sdl2.StartTextInput(); + sdl2.StopTextInput(); + ui_context := ui.init(state.sdl_renderer); - sdl2.AddEventWatch(expose_event_watcher, &StateWithUi { &state, &ui_context }); + state.plugin_vtable = init_plugin_vtable(&ui_context); - // raylib.InitWindow(640, 480, "odin_editor - [now with more ui]"); - // raylib.SetWindowState({ .WINDOW_RESIZABLE, .VSYNC_HINT }); - // raylib.SetTargetFPS(144); - // raylib.SetExitKey(.KEY_NULL); + // Load plugins + // TODO(pcleavelin): Get directory of binary instead of shells current working directory + filepath.walk(filepath.join({ os.get_current_directory(), "bin" }), load_plugin, transmute(rawptr)&state); - // TODO: don't just hard code a MacOS font path - // state.font = raylib.LoadFontEx("/System/Library/Fonts/Supplemental/Andale Mono.ttf", i32(state.source_font_height), nil, 0); - // raylib.SetTextureFilter(state.font.texture, .BILINEAR); + for plugin in state.plugins { + if plugin.on_initialize != nil { + plugin.on_initialize(state.plugin_vtable); + } + } control_key_pressed: bool; @@ -958,7 +1040,7 @@ main :: proc() { { buffer := &state.buffers[state.current_buffer]; - ui.push_parent(&ui_context, ui.push_box(&ui_context, "main", {}, .Vertical, semantic_size = {ui.make_semantic_size(.PercentOfParent, 100), ui.make_semantic_size(.PercentOfParent, 100)})); + ui.push_parent(&ui_context, ui.push_box(&ui_context, "main", {}, .Vertical, semantic_size = {ui.make_semantic_size(.Fill, 100), ui.make_semantic_size(.Fill, 100)})); defer ui.pop_parent(&ui_context); { @@ -1012,7 +1094,7 @@ main :: proc() { defer ui.pop_parent(&ui_context); { - if ui_file_buffer(&ui_context, &state.buffers[0+3]).clicked { + if ui_file_buffer(&ui_context, &state.buffers[state.current_buffer]).clicked { state.current_buffer = 3; } } @@ -1065,6 +1147,10 @@ main :: proc() { } } + if state.window != nil && state.window.draw != nil { + state.window.draw(state.plugin_vtable, state.window.user_data); + } + { ui_context.last_mouse_left_down = ui_context.mouse_left_down; ui_context.last_mouse_right_down = ui_context.mouse_right_down; @@ -1091,40 +1177,98 @@ main :: proc() { } } - if sdl_event.type == .KEYDOWN { - key := plugin.Key(sdl_event.key.keysym.sym); - if key == .LCTRL { - control_key_pressed = true; - } else if state.current_input_map != nil { - if control_key_pressed { - if action, exists := state.current_input_map.ctrl_key_actions[key]; exists { - switch value in action.action { - case core.PluginEditorAction: - value(state.plugin_vtable); - case core.EditorAction: - value(&state); - case core.InputMap: - state.current_input_map = &(&state.current_input_map.ctrl_key_actions[key]).action.(core.InputMap) - } + switch state.mode { + case .Normal: { + if sdl_event.type == .KEYDOWN { + key := plugin.Key(sdl_event.key.keysym.sym); + if key == .ESCAPE { + core.request_window_close(&state); } - } else { - if action, exists := state.current_input_map.key_actions[key]; exists { - switch value in action.action { - case core.PluginEditorAction: - value(state.plugin_vtable); - case core.EditorAction: - value(&state); - case core.InputMap: - state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputMap) + + if key == .LCTRL { + control_key_pressed = true; + } else if state.current_input_map != nil { + if control_key_pressed { + if action, exists := state.current_input_map.ctrl_key_actions[key]; exists { + switch value in action.action { + case core.PluginEditorAction: + value(state.plugin_vtable); + case core.EditorAction: + value(&state); + case core.InputMap: + state.current_input_map = &(&state.current_input_map.ctrl_key_actions[key]).action.(core.InputMap) + } + } + } else { + if action, exists := state.current_input_map.key_actions[key]; exists { + switch value in action.action { + case core.PluginEditorAction: + value(state.plugin_vtable); + case core.EditorAction: + value(&state); + case core.InputMap: + state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputMap) + } + } } } } + if sdl_event.type == .KEYUP { + key := plugin.Key(sdl_event.key.keysym.sym); + if key == .LCTRL { + control_key_pressed = false; + } + } } - } - if sdl_event.type == .KEYUP { - key := plugin.Key(sdl_event.key.keysym.sym); - if key == .LCTRL { - control_key_pressed = false; + case .Insert: { + buffer: ^FileBuffer; + + if state.window != nil && state.window.get_buffer != nil { + buffer = transmute(^core.FileBuffer)(state.window.get_buffer(state.plugin_vtable, state.window.user_data)); + } else { + buffer = &state.buffers[state.current_buffer]; + } + + if sdl_event.type == .KEYDOWN { + key := plugin.Key(sdl_event.key.keysym.sym); + + #partial switch key { + case .ESCAPE: { + state.mode = .Normal; + + core.insert_content(buffer, buffer.input_buffer[:]); + runtime.clear(&buffer.input_buffer); + + sdl2.StopTextInput(); + } + case .BACKSPACE: { + core.delete_content(buffer, 1); + + for hook_proc in state.hooks[plugin.Hook.BufferInput] { + hook_proc(state.plugin_vtable, buffer); + } + } + case .ENTER: { + append(&buffer.input_buffer, '\n'); + } + } + } + + if sdl_event.type == .TEXTINPUT { + for char in sdl_event.text.text { + if char < 1 { + break; + } + + if char >= 32 && char <= 125 && len(buffer.input_buffer) < 1024-1 { + append(&buffer.input_buffer, u8(char)); + + for hook_proc in state.hooks[plugin.Hook.BufferInput] { + hook_proc(state.plugin_vtable, buffer); + } + } + } + } } } } diff --git a/src/plugin/plugin.odin b/src/plugin/plugin.odin index d9684b8..1053fcc 100644 --- a/src/plugin/plugin.odin +++ b/src/plugin/plugin.odin @@ -84,6 +84,47 @@ Iterator :: struct { until_end_of_word: rawptr, } +UiInteraction :: struct { + hovering: bool, + clicked: bool +} + +UiAxis :: enum { + Horizontal = 0, + Vertical, +} + +UiSemanticSize :: struct { + kind: int, + value: int, +} + +UiBox :: rawptr; + +UiPushParentProc :: proc "c" (ui_context: rawptr, box: UiBox); +UiPopParentProc :: proc "c" (ui_context: rawptr); +UiFloatingProc :: proc "c" (ui_context: rawptr, label: cstring, pos: [2]int) -> UiBox; +UiCreateBoxProc :: proc "c" (ui_context: rawptr, label: cstring) -> UiBox; +UiRectProc :: proc "c" (ui_context: rawptr, label: cstring, border: bool, axis: UiAxis, size: [2]UiSemanticSize) -> UiBox; +UiSimpleProc :: proc "c" (ui_context: rawptr, label: cstring) -> UiInteraction; +UiBufferProc :: proc "c" (ui_context: rawptr, buffer: rawptr, show_line_numbers: bool); +UiBufferIndexProc :: proc "c" (ui_context: rawptr, buffer: int, show_line_numbers: bool); +Ui :: struct { + ui_context: rawptr, + + push_parent: UiPushParentProc, + pop_parent: UiPopParentProc, + + floating: UiFloatingProc, + rect: UiRectProc, + + button: UiSimpleProc, + label: UiSimpleProc, + + buffer: UiBufferProc, + buffer_from_index: UiBufferIndexProc, +} + OnColorBufferProc :: proc "c" (plugin: Plugin, buffer: rawptr); InputGroupProc :: proc "c" (plugin: Plugin, input_map: rawptr); InputActionProc :: proc "c" (plugin: Plugin); @@ -97,6 +138,7 @@ Plugin :: struct { state: rawptr, iter: Iterator, buffer: Buffer, + ui: Ui, register_hook: proc "c" (hook: Hook, on_hook: OnHookProc), register_highlighter: proc "c" (extension: cstring, on_color_buffer: OnColorBufferProc), diff --git a/src/ui/imm.odin b/src/ui/imm.odin index e1148d7..ffe06b1 100644 --- a/src/ui/imm.odin +++ b/src/ui/imm.odin @@ -49,11 +49,12 @@ Flag :: enum { DrawText, DrawBorder, DrawBackground, + Floating, CustomDrawFunc, } SemanticSizeKind :: enum { - FitText, + FitText = 0, Exact, ChildrenSum, Fill, @@ -126,6 +127,7 @@ gen_key :: proc(ctx: ^Context, label: string, value: int) -> Key { }; } +@(private) make_box :: proc(ctx: ^Context, key: Key, label: string, flags: bit_set[Flag], axis: Axis, semantic_size: [2]SemanticSize) -> ^Box { box: ^Box = nil; @@ -193,6 +195,15 @@ ChildrenSum :[2]SemanticSize: { } }; +Fill :[2]SemanticSize: { + SemanticSize { + kind = .Fill, + }, + SemanticSize { + kind = .Fill, + } +}; + push_box :: proc(ctx: ^Context, label: string, flags: bit_set[Flag], axis: Axis = .Horizontal, semantic_size: [2]SemanticSize = FitText, value: int = 0) -> ^Box { key := gen_key(ctx, label, value); box := make_box(ctx, key, label, flags, axis, semantic_size); @@ -227,10 +238,6 @@ test_box :: proc(ctx: ^Context, box: ^Box) -> Interaction { box.hot = 0; } - if hovering && mouse_is_clicked { - fmt.println("hot", box.hot); - } - return Interaction { hovering = hovering, clicked = hovering && mouse_is_clicked, @@ -265,16 +272,21 @@ prune :: proc(ctx: ^Context) { } } + computed_pos := ctx.root.computed_pos; + computed_size := ctx.root.computed_size; root_key := ctx.root.key; - ctx.root^ = { - key = root_key, - }; + + ctx.root.first = nil; + ctx.root.last = nil; + ctx.root.next = nil; + ctx.root.prev = nil; + ctx.root.parent = nil; ctx.current_parent = ctx.root; } // TODO: consider not using `ctx` here ancestor_size :: proc(ctx: ^Context, box: ^Box, axis: Axis) -> int { - if box == nil || box.parent == nil { + if box == nil || box.parent == nil || .Floating in box.flags { return ctx.root.computed_size[axis]; } @@ -292,17 +304,35 @@ ancestor_size :: proc(ctx: ^Context, box: ^Box, axis: Axis) -> int { return 1337; } +prev_non_floating_sibling :: proc(ctx: ^Context, box: ^Box) -> ^Box { + if box == nil { + return nil; + } else if box.prev == nil { + return nil; + } else if !(.Floating in box.prev.flags) { + return box.prev; + } else { + return prev_non_floating_sibling(ctx, box.prev); + } +} + compute_layout :: proc(ctx: ^Context, canvas_size: [2]int, font_width: int, font_height: int, box: ^Box) { if box == nil { return; } axis := Axis.Horizontal; - if box.parent != nil { + if box.parent != nil && !(.Floating in box.flags) { 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]; + if .Floating in box.flags { + // box.computed_pos = {0,0}; + } else if box.prev != nil { + prev := prev_non_floating_sibling(ctx, box); + + if prev != nil { + box.computed_pos[axis] = prev.computed_pos[axis] + prev.computed_size[axis]; + } } post_compute_size := [2]bool { false, false }; @@ -415,6 +445,8 @@ compute_layout :: proc(ctx: ^Context, canvas_size: [2]int, font_width: int, font our_size := box.computed_size; for child in iterate_box(&iter) { + if .Floating in child.flags { continue; } + compute_layout(ctx, canvas_size, font_width, font_height, child); if child.semantic_size[box.axis].kind == .Fill { number_of_fills[box.axis] += 1; @@ -644,8 +676,19 @@ spacer :: proc(ctx: ^Context, label: string, flags: bit_set[Flag] = {}, semantic return push_box(ctx, label, flags, semantic_size = semantic_size); } +push_floating :: proc(ctx: ^Context, label: string, pos: [2]int, flags: bit_set[Flag] = {.Floating}, axis: Axis = .Vertical, semantic_size: [2]SemanticSize = Fill) -> ^Box { + box := push_box(ctx, label, flags, semantic_size = semantic_size); + box.computed_pos = pos; + + return box; +} + +push_rect :: proc(ctx: ^Context, label: string, border: bool = true, axis: Axis = .Vertical, semantic_size: [2]SemanticSize = Fill) -> ^Box { + return push_box(ctx, label, {.DrawBackground, .DrawBorder if border else nil}, axis, semantic_size = semantic_size); +} + label :: proc(ctx: ^Context, label: string) -> Interaction { - box := push_box(ctx, label, {.DrawText, .Hoverable}); + box := push_box(ctx, label, {.DrawText}); return test_box(ctx, box); } From ccfdc2e325502a873f753eb440d4c45b799fb118 Mon Sep 17 00:00:00 2001 From: Patrick Cleavelin Date: Fri, 26 Jan 2024 02:31:25 -0600 Subject: [PATCH 4/6] finally fixed the ugly font rendering --- src/core/gfx.odin | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/core/gfx.odin b/src/core/gfx.odin index e176698..aa0d9df 100644 --- a/src/core/gfx.odin +++ b/src/core/gfx.odin @@ -6,7 +6,7 @@ import "vendor:sdl2/ttf" import "../theme" -scale :: 1; +scale :: 2; start_char :: ' '; end_char :: '~'; @@ -26,9 +26,10 @@ gen_font_atlas :: proc(state: ^State, path: cstring) -> FontAtlas { // FIXME: check if this failed font = ttf.OpenFont(path, font_height), } - ttf.SetFontKerning(atlas.font, false); ttf.SetFontStyle(atlas.font, ttf.STYLE_NORMAL); - ttf.SetFontOutline(atlas.font, 0); + + // NOTE: not sure if I like the look of this or not yet + // ttf.SetFontHinting(atlas.font, ttf.HINTING_MONO); minx, maxx, miny, maxy: i32; advanced: i32; @@ -45,20 +46,14 @@ gen_font_atlas :: proc(state: ^State, path: cstring) -> FontAtlas { atlas.max_height = int(height); } - // if atlas.max_width%2 != 0 { - // atlas.max_width += 1; - // } } font_width := i32(atlas.max_width); font_height = i32(atlas.max_height); - state.source_font_width = int(font_width/scale);// int(font_width/scale); - state.source_font_height = int(font_height/scale);//int(font_height/scale); - //fmt.println("font_width:", font_width, "font height:", font_height); - //state.source_font_width = int(f32(font_width)/f32(scale)); + state.source_font_width = int(font_width/scale); + state.source_font_height = int(font_height/scale); temp_surface: ^sdl2.Surface; - sdl2.SetHint(sdl2.HINT_RENDER_SCALE_QUALITY, "2"); // FIXME: check if this failed font_surface := sdl2.CreateRGBSurface(0, font_width * (end_char-start_char + 1), font_height, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); @@ -66,21 +61,30 @@ gen_font_atlas :: proc(state: ^State, path: cstring) -> FontAtlas { white := sdl2.Color { 0xff, 0xff, 0xff, 0xff }; for char, index in start_char..=end_char { - // ttf.GlyphMetrics32(atlas.font, char, &minx, &maxx, &miny, &maxy, &advanced); rect.x = i32(index) * font_width; - rect.y = 0;//-font_height/8; + rect.y = 0; // FIXME: check if this failed temp_surface = ttf.RenderGlyph32_Blended(atlas.font, char, white); + // NOTE(pcleavelin): holy schmoley batman, it took hours of researching to find out + // that the way to properly blend the texture atlas WAS TO DISABLE BLENDING! + // and of course it's ONE GUY on a forum that has the answer, but it's for some + // reason not even listed on the first page of Google when you search for: + // "sdl_ttf rendering antialiasing" + // + // But the reason why this is needed (I /think/) is because you want to + // directly write all the RGBA data to `font_surface` without it attempting to + // blend a surface that has pure black + sdl2.SetSurfaceBlendMode(temp_surface, .NONE); + src_rect := sdl2.Rect { 0, 0, temp_surface.w, temp_surface.h }; - //fmt.println("char", char, src_rect.x, src_rect.y, src_rect.w, src_rect.h, atlas.max_width, atlas.max_height); // FIXME: check if this failed sdl2.BlitSurface(temp_surface, &src_rect, font_surface, &rect); @@ -90,8 +94,6 @@ gen_font_atlas :: proc(state: ^State, path: cstring) -> FontAtlas { // FIXME: check if this failed atlas.texture = sdl2.CreateTextureFromSurface(state.sdl_renderer, font_surface); sdl2.SetTextureScaleMode(atlas.texture, .Best); - // sdl2.SetTextureAlphaMod(atlas.texture, 0xff); - // sdl2.SetTextureBlendMode(atlas.texture, .BLEND); return atlas; } From 8d633758387fa59b81061162b9ffb19b5393e30c Mon Sep 17 00:00:00 2001 From: Patrick Cleavelin Date: Sat, 27 Jan 2024 13:26:29 -0600 Subject: [PATCH 5/6] fix issue with grep/buffer window not changing position on window resize --- plugin-rs-bindings/src/lib.rs | 8 ++ plugins/buffer_search/plugin.odin | 50 ++++--- plugins/grep/src/lib.rs | 224 +++++++++++++++--------------- src/core/core.odin | 2 + src/main.odin | 47 +++++-- src/plugin/plugin.odin | 3 +- src/ui/imm.odin | 12 +- 7 files changed, 195 insertions(+), 151 deletions(-) diff --git a/plugin-rs-bindings/src/lib.rs b/plugin-rs-bindings/src/lib.rs index 8b919af..c1d0ecb 100644 --- a/plugin-rs-bindings/src/lib.rs +++ b/plugin-rs-bindings/src/lib.rs @@ -222,6 +222,7 @@ type UiRectProc = extern "C" fn( ui_context: UiContext, label: *const i8, border: bool, + border: bool, axis: UiAxis, size: [InternalUiSemanticSize; 2], ) -> UiBox; @@ -235,6 +236,7 @@ pub struct UiVTable { push_parent: UiPushParentProc, pop_parent: UiPopParentProc, + spacer: UiSimpleProc, floating: UiFloatingProc, rect: UiRectProc, @@ -253,9 +255,14 @@ impl UiVTable { (self.pop_parent)(self.ui_context); } + pub fn spacer(&self, label: &CStr) -> UiInteraction { + (self.spacer)(self.ui_context, label.as_ptr()) + } + pub fn push_rect( &self, label: &CStr, + show_background: bool, show_border: bool, axis: UiAxis, horizontal_size: UiSemanticSize, @@ -265,6 +272,7 @@ impl UiVTable { let rect = (self.rect)( self.ui_context, label.as_ptr(), + show_background, show_border, axis, [horizontal_size.into(), vertical_size.into()], diff --git a/plugins/buffer_search/plugin.odin b/plugins/buffer_search/plugin.odin index b0f7f9a..382a13f 100644 --- a/plugins/buffer_search/plugin.odin +++ b/plugins/buffer_search/plugin.odin @@ -109,44 +109,54 @@ draw_buffer_window :: proc "c" (plugin: Plugin, win: rawptr) { screen_height := plugin.get_screen_height(); directory := string(plugin.get_current_directory()); - canvas := plugin.ui.floating(plugin.ui.ui_context, "buffer search canvas", {screen_width/8, screen_height/8}); + canvas := plugin.ui.floating(plugin.ui.ui_context, "buffer search canvas", {0,0}); plugin.ui.push_parent(plugin.ui.ui_context, canvas); { defer plugin.ui.pop_parent(plugin.ui.ui_context); - ui_window := plugin.ui.rect(plugin.ui.ui_context, "buffer search window", true, .Horizontal, {{4, 75}, {4, 75}}); - plugin.ui.push_parent(plugin.ui.ui_context, ui_window); + plugin.ui.spacer(plugin.ui.ui_context, "left spacer"); + centered_container := plugin.ui.rect(plugin.ui.ui_context, "centered container", false, false, .Vertical, {{4, 75}, {3, 0}}); + plugin.ui.push_parent(plugin.ui.ui_context, centered_container); { defer plugin.ui.pop_parent(plugin.ui.ui_context); - buffer_list_view := plugin.ui.rect(plugin.ui.ui_context, "buffer list view", false, .Vertical, {{4, 60}, {3, 0}}); - plugin.ui.push_parent(plugin.ui.ui_context, buffer_list_view); + plugin.ui.spacer(plugin.ui.ui_context, "top spacer"); + ui_window := plugin.ui.rect(plugin.ui.ui_context, "buffer search window", true, true, .Horizontal, {{3, 0}, {4, 75}}); + plugin.ui.push_parent(plugin.ui.ui_context, ui_window); { defer plugin.ui.pop_parent(plugin.ui.ui_context); - _buffer_index := 0; - for index in buffer_list_iter(plugin, &_buffer_index) { - buffer := plugin.buffer.get_buffer_info_from_index(index); - relative_file_path, _ := filepath.rel(directory, string(buffer.file_path), context.temp_allocator) - text := fmt.ctprintf("%s:%d", relative_file_path, buffer.cursor.line+1); + buffer_list_view := plugin.ui.rect(plugin.ui.ui_context, "buffer list view", false, false, .Vertical, {{4, 60}, {3, 0}}); + plugin.ui.push_parent(plugin.ui.ui_context, buffer_list_view); + { + defer plugin.ui.pop_parent(plugin.ui.ui_context); - if index == win.selected_index { - plugin.ui.button(plugin.ui.ui_context, text); - } else { - plugin.ui.label(plugin.ui.ui_context, text); + _buffer_index := 0; + for index in buffer_list_iter(plugin, &_buffer_index) { + buffer := plugin.buffer.get_buffer_info_from_index(index); + relative_file_path, _ := filepath.rel(directory, string(buffer.file_path), context.temp_allocator) + text := fmt.ctprintf("%s:%d", relative_file_path, buffer.cursor.line+1); + + if index == win.selected_index { + plugin.ui.button(plugin.ui.ui_context, text); + } else { + plugin.ui.label(plugin.ui.ui_context, text); + } } } - } - buffer_preview := plugin.ui.rect(plugin.ui.ui_context, "buffer preview", false, .Horizontal, {{3, 0}, {3, 0}}); - plugin.ui.push_parent(plugin.ui.ui_context, buffer_preview); - { - defer plugin.ui.pop_parent(plugin.ui.ui_context); + buffer_preview := plugin.ui.rect(plugin.ui.ui_context, "buffer preview", true, false, .Horizontal, {{3, 0}, {3, 0}}); + plugin.ui.push_parent(plugin.ui.ui_context, buffer_preview); + { + defer plugin.ui.pop_parent(plugin.ui.ui_context); - plugin.ui.buffer_from_index(plugin.ui.ui_context, win.selected_index, false); + plugin.ui.buffer_from_index(plugin.ui.ui_context, win.selected_index, false); + } } + plugin.ui.spacer(plugin.ui.ui_context, "bottom spacer"); } + plugin.ui.spacer(plugin.ui.ui_context, "right spacer"); } /* diff --git a/plugins/grep/src/lib.rs b/plugins/grep/src/lib.rs index d51859d..6d0c3da 100644 --- a/plugins/grep/src/lib.rs +++ b/plugins/grep/src/lib.rs @@ -281,134 +281,130 @@ extern "C" fn draw_window(plugin: Plugin, window: *const std::ffi::c_void) { let dir = plugin.get_current_directory(); let directory = Path::new(dir.as_ref()); - plugin.ui_table.push_floating( - c"grep canvas", - (screen_width as isize) / 8, - (screen_height as isize) / 8, - |ui_table| { + plugin + .ui_table + .push_floating(c"grep canvas", 0, 0, |ui_table| { + // TODO: make some primitive that centers a Box + ui_table.spacer(c"left spacer"); + ui_table.push_rect( - c"grep window", - true, + c"centered container", + false, + false, UiAxis::Vertical, UiSemanticSize::PercentOfParent(75), - UiSemanticSize::PercentOfParent(75), + UiSemanticSize::Fill, |ui_table| { - if let Ok(sink) = window.rx.try_recv() { - window.sink = Some(sink); - } - - ui_table.push_rect( - c"results list", - false, - UiAxis::Vertical, - UiSemanticSize::Fill, - UiSemanticSize::Fill, - |ui_table| match &window.sink { - Some(sink) if !sink.matches.is_empty() => { - let num_mats_to_draw = std::cmp::min( - (sink.matches.len() - window.top_index) as i32, - (height - font_height) / (font_height), - ); - - for (i, mat) in sink.matches[window.top_index..].iter().enumerate() - { - let index = i + window.top_index; - if i as i32 >= num_mats_to_draw { - break; - } - - let path = Path::new(&mat.path); - let relative_file_path = path - .strip_prefix(directory) - .unwrap_or(path) - .to_str() - .unwrap_or(""); - - let matched_text = String::from_utf8_lossy(&mat.text); - let text = match mat.line_number { - Some(line_number) => format!( - "{}:{}:{}: {}", - relative_file_path, - line_number, - mat.column, - matched_text - ), - None => format!( - "{}:{}: {}", - relative_file_path, mat.column, matched_text - ), - }; - - if index == window.selected_match { - ui_table.button(&CString::new(text).expect("valid text")); - } else { - ui_table.label(&CString::new(text).expect("valid text")); - } - } - } - Some(_) | None => { - ui_table.push_rect( - c"top spacer", - false, - UiAxis::Vertical, - UiSemanticSize::Fill, - UiSemanticSize::Fill, - |ui_table| {}, - ); - ui_table.push_rect( - c"centered text container", - false, - UiAxis::Horizontal, - UiSemanticSize::Fill, - UiSemanticSize::Fill, - |ui_table| { - ui_table.push_rect( - c"left spacer", - false, - UiAxis::Vertical, - UiSemanticSize::Fill, - UiSemanticSize::Fill, - |ui_table| {}, - ); - ui_table.label(c"no results"); - ui_table.push_rect( - c"right spacer", - false, - UiAxis::Vertical, - UiSemanticSize::Fill, - UiSemanticSize::Fill, - |ui_table| {}, - ); - }, - ); - ui_table.push_rect( - c"bottom spacer", - false, - UiAxis::Vertical, - UiSemanticSize::Fill, - UiSemanticSize::Fill, - |ui_table| {}, - ); - } - }, - ); - + ui_table.spacer(c"top spacer"); ui_table.push_rect( c"grep window", - false, + true, + true, UiAxis::Vertical, UiSemanticSize::Fill, - UiSemanticSize::Exact(font_height as isize), + UiSemanticSize::PercentOfParent(75), |ui_table| { - if let Some(buffer) = window.input_buffer { - ui_table.buffer(buffer, false); + if let Ok(sink) = window.rx.try_recv() { + window.sink = Some(sink); } + + ui_table.push_rect( + c"results list", + false, + false, + UiAxis::Vertical, + UiSemanticSize::Fill, + UiSemanticSize::Fill, + |ui_table| match &window.sink { + Some(sink) if !sink.matches.is_empty() => { + let num_mats_to_draw = std::cmp::min( + (sink.matches.len() - window.top_index) as i32, + (height - font_height) / (font_height), + ); + + for (i, mat) in + sink.matches[window.top_index..].iter().enumerate() + { + let index = i + window.top_index; + if i as i32 >= num_mats_to_draw { + break; + } + + let path = Path::new(&mat.path); + let relative_file_path = path + .strip_prefix(directory) + .unwrap_or(path) + .to_str() + .unwrap_or(""); + + let matched_text = String::from_utf8_lossy(&mat.text); + let text = match mat.line_number { + Some(line_number) => format!( + "{}:{}:{}: {}", + relative_file_path, + line_number, + mat.column, + matched_text + ), + None => format!( + "{}:{}: {}", + relative_file_path, mat.column, matched_text + ), + }; + + if index == window.selected_match { + // TODO: don't use button here, but apply a style + // to `label` + ui_table.button( + &CString::new(text).expect("valid text"), + ); + } else { + ui_table.label( + &CString::new(text).expect("valid text"), + ); + } + } + } + Some(_) | None => { + ui_table.spacer(c"top spacer"); + ui_table.push_rect( + c"centered text container", + false, + false, + UiAxis::Horizontal, + UiSemanticSize::Fill, + UiSemanticSize::Fill, + |ui_table| { + ui_table.spacer(c"left spacer"); + ui_table.label(c"no results"); + ui_table.spacer(c"right spacer"); + }, + ); + ui_table.spacer(c"bottom spacer"); + } + }, + ); + + ui_table.push_rect( + c"grep window", + true, + false, + UiAxis::Vertical, + UiSemanticSize::Fill, + UiSemanticSize::Exact(font_height as isize), + |ui_table| { + if let Some(buffer) = window.input_buffer { + ui_table.buffer(buffer, false); + } + }, + ); }, ); + ui_table.spacer(c"bottom spacer"); }, ); - }, - ); + ui_table.spacer(c"right spacer"); + }); } extern "C" fn on_buffer_input(plugin: Plugin, buffer: Buffer) { diff --git a/src/core/core.odin b/src/core/core.odin index d5156ee..1766493 100644 --- a/src/core/core.odin +++ b/src/core/core.odin @@ -49,6 +49,8 @@ State :: struct { should_close: bool, screen_height: int, screen_width: int, + width_dpi_ratio: f32, + height_dpi_ratio: f32, directory: string, diff --git a/src/main.odin b/src/main.odin index 650ae39..0c9e177 100644 --- a/src/main.odin +++ b/src/main.odin @@ -194,6 +194,7 @@ register_default_input_actions :: proc(input_map: ^core.InputMap) { core.register_key_action(input_map, .A, proc(state: ^State) { core.move_cursor_right(&state.buffers[state.current_buffer], false); state.mode = .Insert; + sdl2.StartTextInput(); }, "enter insert mode after character (append)"); } @@ -341,6 +342,9 @@ expose_event_watcher :: proc "c" (state: rawptr, event: ^sdl2.Event) -> i32 { state.state.screen_width = int(w); state.state.screen_height = int(h); + state.state.width_dpi_ratio = f32(w) / f32(event.window.data1); + state.state.height_dpi_ratio = f32(h) / f32(event.window.data2); + draw(state); } } @@ -870,6 +874,18 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { ui.pop_parent(ui_context); }, + spacer = proc "c" (ui_context: rawptr, label: cstring) -> plugin.UiInteraction { + context = state.ctx; + ui_context := transmute(^ui.Context)ui_context; + label := strings.clone(string(label), context.temp_allocator); + + interaction := ui.spacer(ui_context, label); + + return plugin.UiInteraction { + hovering = interaction.hovering, + clicked = interaction.clicked, + }; + }, // TODO: allow this to have more flags sent to it floating = proc "c" (ui_context: rawptr, label: cstring, pos: [2]int) -> plugin.UiBox { context = state.ctx; @@ -878,7 +894,7 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { return ui.push_floating(ui_context, label, pos); }, - rect = proc "c" (ui_context: rawptr, label: cstring, border: bool, axis: plugin.UiAxis, size: [2]plugin.UiSemanticSize) -> plugin.UiBox { + rect = proc "c" (ui_context: rawptr, label: cstring, background: bool, border: bool, axis: plugin.UiAxis, size: [2]plugin.UiSemanticSize) -> plugin.UiBox { context = state.ctx; ui_context := transmute(^ui.Context)ui_context; label := strings.clone(string(label), context.temp_allocator); @@ -894,7 +910,7 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { }, }; - return ui.push_rect(ui_context, label, border, ui.Axis(axis), size); + return ui.push_rect(ui_context, label, background, border, ui.Axis(axis), size); }, label = proc "c" (ui_context: rawptr, label: cstring) -> plugin.UiInteraction { @@ -944,8 +960,10 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { main :: proc() { state = State { ctx = context, - source_font_width = 8 + 2 * 3, - source_font_height = 16 + 2 * 3, + screen_width = 640, + screen_height = 480, + source_font_width = 8, + source_font_height = 16, input_map = core.new_input_map(), window = nil, directory = os.get_current_directory(), @@ -984,7 +1002,7 @@ main :: proc() { 0, 640, 480, - {.SHOWN, .RESIZABLE, .METAL, .ALLOW_HIGHDPI} + {.SHOWN, .RESIZABLE, .ALLOW_HIGHDPI} ); defer if sdl_window != nil { sdl2.DestroyWindow(sdl_window); @@ -1014,6 +1032,17 @@ main :: proc() { } } + { + w,h: i32; + sdl2.GetRendererOutputSize(state.sdl_renderer, &w, &h); + + state.width_dpi_ratio = f32(w) / f32(state.screen_width); + state.height_dpi_ratio = f32(h) / f32(state.screen_height); + state.screen_width = int(w); + state.screen_height = int(h); + } + + // Done to clear the buffer sdl2.StartTextInput(); sdl2.StopTextInput(); @@ -1031,11 +1060,7 @@ main :: proc() { } } - control_key_pressed: bool; - - state.screen_width = 640; //int(raylib.GetScreenWidth()); - state.screen_height = 480; //int(raylib.GetScreenHeight()); for !state.should_close { { buffer := &state.buffers[state.current_buffer]; @@ -1162,8 +1187,8 @@ main :: proc() { } if sdl_event.type == .MOUSEMOTION { - ui_context.mouse_x = int(sdl_event.motion.x); - ui_context.mouse_y = int(sdl_event.motion.y); + ui_context.mouse_x = int(f32(sdl_event.motion.x) * state.width_dpi_ratio); + ui_context.mouse_y = int(f32(sdl_event.motion.y) * state.height_dpi_ratio); } if sdl_event.type == .MOUSEBUTTONDOWN || sdl_event.type == .MOUSEBUTTONUP { diff --git a/src/plugin/plugin.odin b/src/plugin/plugin.odin index 1053fcc..a9786f1 100644 --- a/src/plugin/plugin.odin +++ b/src/plugin/plugin.odin @@ -105,7 +105,7 @@ UiPushParentProc :: proc "c" (ui_context: rawptr, box: UiBox); UiPopParentProc :: proc "c" (ui_context: rawptr); UiFloatingProc :: proc "c" (ui_context: rawptr, label: cstring, pos: [2]int) -> UiBox; UiCreateBoxProc :: proc "c" (ui_context: rawptr, label: cstring) -> UiBox; -UiRectProc :: proc "c" (ui_context: rawptr, label: cstring, border: bool, axis: UiAxis, size: [2]UiSemanticSize) -> UiBox; +UiRectProc :: proc "c" (ui_context: rawptr, label: cstring, background: bool, border: bool, axis: UiAxis, size: [2]UiSemanticSize) -> UiBox; UiSimpleProc :: proc "c" (ui_context: rawptr, label: cstring) -> UiInteraction; UiBufferProc :: proc "c" (ui_context: rawptr, buffer: rawptr, show_line_numbers: bool); UiBufferIndexProc :: proc "c" (ui_context: rawptr, buffer: int, show_line_numbers: bool); @@ -115,6 +115,7 @@ Ui :: struct { push_parent: UiPushParentProc, pop_parent: UiPopParentProc, + spacer: UiSimpleProc, floating: UiFloatingProc, rect: UiRectProc, diff --git a/src/ui/imm.odin b/src/ui/imm.odin index ffe06b1..57e3a20 100644 --- a/src/ui/imm.odin +++ b/src/ui/imm.odin @@ -672,19 +672,21 @@ debug_print :: proc(ctx: ^Context, box: ^Box, depth: int = 0) { } } -spacer :: proc(ctx: ^Context, label: string, flags: bit_set[Flag] = {}, semantic_size: [2]SemanticSize = {{.Fill, 0}, {.Fill,0}}) -> ^Box { - return push_box(ctx, label, flags, semantic_size = semantic_size); +spacer :: proc(ctx: ^Context, label: string, flags: bit_set[Flag] = {}, semantic_size: [2]SemanticSize = {{.Fill, 0}, {.Fill,0}}) -> Interaction { + box := push_box(ctx, label, flags, semantic_size = semantic_size); + + return test_box(ctx, box); } -push_floating :: proc(ctx: ^Context, label: string, pos: [2]int, flags: bit_set[Flag] = {.Floating}, axis: Axis = .Vertical, semantic_size: [2]SemanticSize = Fill) -> ^Box { +push_floating :: proc(ctx: ^Context, label: string, pos: [2]int, flags: bit_set[Flag] = {.Floating}, axis: Axis = .Horizontal, semantic_size: [2]SemanticSize = Fill) -> ^Box { box := push_box(ctx, label, flags, semantic_size = semantic_size); box.computed_pos = pos; return box; } -push_rect :: proc(ctx: ^Context, label: string, border: bool = true, axis: Axis = .Vertical, semantic_size: [2]SemanticSize = Fill) -> ^Box { - return push_box(ctx, label, {.DrawBackground, .DrawBorder if border else nil}, axis, semantic_size = semantic_size); +push_rect :: proc(ctx: ^Context, label: string, background: bool = true, border: bool = true, axis: Axis = .Vertical, semantic_size: [2]SemanticSize = Fill) -> ^Box { + return push_box(ctx, label, {.DrawBackground if background else nil, .DrawBorder if border else nil}, axis, semantic_size = semantic_size); } label :: proc(ctx: ^Context, label: string) -> Interaction { From f6f61d555002bab0b65097ec828c8bad108067dd Mon Sep 17 00:00:00 2001 From: Patrick Cleavelin Date: Tue, 6 Feb 2024 20:54:38 -0600 Subject: [PATCH 6/6] update odin compiler to master --- flake.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index c456029..3ba6dab 100755 --- a/flake.nix +++ b/flake.nix @@ -27,8 +27,8 @@ src = pkgs.fetchFromGitHub { owner = "pcleavelin"; repo = "Odin"; - rev = "59aa05170d54edff75aed220bb1653fc369573d7"; - hash = "sha256-ZMcVugE0uRHba8jmQjTyQ9KKDUdIVSELggKDz9iSiwY="; + rev = "7b9ea9eca02bf5dd295439a46ed6103a0c4a44ff"; + hash = "sha256-pxvU5veB1NEYPfer5roiLp/od2Pv4l1jJah0OHwb5yo="; }; LLVM_CONFIG = "${pkgs.llvmPackages_17.llvm.dev}/bin/llvm-config"; nativeBuildInputs = with pkgs; prevAttr.nativeBuildInputs ++ [ libcxx libcxxabi ];