diff --git a/src/core/core.odin b/src/core/core.odin index 2540bc7..7ed99a2 100644 --- a/src/core/core.odin +++ b/src/core/core.odin @@ -29,15 +29,19 @@ State :: struct { EditorAction :: proc(state: ^State); InputGroup :: union {EditorAction, InputMap} +Action :: struct { + action: InputGroup, + description: string, +} InputMap :: struct { - key_actions: map[raylib.KeyboardKey]InputGroup, - ctrl_key_actions: map[raylib.KeyboardKey]InputGroup, + key_actions: map[raylib.KeyboardKey]Action, + ctrl_key_actions: map[raylib.KeyboardKey]Action, } new_input_map :: proc() -> InputMap { input_map := InputMap { - key_actions = make(map[raylib.KeyboardKey]InputGroup), - ctrl_key_actions = make(map[raylib.KeyboardKey]InputGroup), + key_actions = make(map[raylib.KeyboardKey]Action), + ctrl_key_actions = make(map[raylib.KeyboardKey]Action), } return input_map; @@ -46,40 +50,52 @@ new_input_map :: proc() -> 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_key_action_single :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, action: EditorAction) { +register_key_action_single :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, action: EditorAction, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered - fmt.println("key already registered with single action", key); + fmt.eprintln("key already registered with single action", key); } - input_map.key_actions[key] = action; + input_map.key_actions[key] = Action { + action = action, + description = description, + }; } -register_key_action_group :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, input_group: InputGroup) { +register_key_action_group :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, input_group: InputGroup, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered - fmt.println("key already registered with single action", key); + fmt.eprintln("key already registered with single action", key); } - input_map.key_actions[key] = input_group; + input_map.key_actions[key] = Action { + action = input_group, + description = description, + }; } -register_ctrl_key_action_single :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, action: EditorAction) { +register_ctrl_key_action_single :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, action: EditorAction, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered - fmt.println("key already registered with single action", key); + fmt.eprintln("key already registered with single action", key); } - input_map.ctrl_key_actions[key] = action; + input_map.ctrl_key_actions[key] = Action { + action = action, + description = description, + }; } -register_ctrl_key_action_group :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, input_group: InputGroup) { +register_ctrl_key_action_group :: proc(input_map: ^InputMap, key: raylib.KeyboardKey, input_group: InputGroup, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered - fmt.println("key already registered with single action", key); + fmt.eprintln("key already registered with single action", key); } - input_map.ctrl_key_actions[key] = input_group; + input_map.ctrl_key_actions[key] = Action { + action = input_group, + description = description, + }; } register_key_action :: proc{register_key_action_single, register_key_action_group}; diff --git a/src/main.odin b/src/main.odin index 61c8338..bc36e88 100644 --- a/src/main.odin +++ b/src/main.odin @@ -20,24 +20,24 @@ FileBuffer :: core.FileBuffer; do_normal_mode :: proc(state: ^State, buffer: ^FileBuffer) { if state.current_input_map != nil { if raylib.IsKeyDown(.LEFT_CONTROL) { - for key, action in state.current_input_map.ctrl_key_actions { + for key, action in &state.current_input_map.ctrl_key_actions { if raylib.IsKeyPressed(key) { - switch value in action { + switch value in action.action { case core.EditorAction: - value(state); + value(state); case core.InputMap: - // TODO: make this the current input map and display it + 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 { + switch value in action.action { case core.EditorAction: - value(state); + value(state); case core.InputMap: - // TODO: make this the current input map and display it + state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputMap) } } } @@ -83,39 +83,44 @@ switch_to_buffer :: proc(state: ^State, item: ^ui.MenuBarItem) { } } -register_default_input_actions :: proc(input_map: ^core.InputMap) { - core.register_key_action(input_map, .W, proc(state: ^State) { - core.move_cursor_forward_start_of_word(&state.buffers[state.current_buffer]); - }); - - core.register_key_action(input_map, .K, proc(state: ^State) { - core.move_cursor_up(&state.buffers[state.current_buffer]); - }); - core.register_key_action(input_map, .J, proc(state: ^State) { - core.move_cursor_down(&state.buffers[state.current_buffer]); - }); - core.register_key_action(input_map, .H, proc(state: ^State) { - core.move_cursor_left(&state.buffers[state.current_buffer]); - }); - core.register_key_action(input_map, .L, proc(state: ^State) { - core.move_cursor_right(&state.buffers[state.current_buffer]); - }); - - core.register_ctrl_key_action(input_map, .U, proc(state: ^State) { - core.scroll_file_buffer(&state.buffers[state.current_buffer], .Up); - }); - core.register_ctrl_key_action(input_map, .D, proc(state: ^State) { - core.scroll_file_buffer(&state.buffers[state.current_buffer], .Down); - }); - +register_default_leader_actions :: proc(input_map: ^core.InputMap) { core.register_key_action(input_map, .B, proc(state: ^State) { state.buffer_list_window_is_visible = true; state.current_input_map = &state.buffer_list_window_input_map; - }); + }, "show list of open buffers"); +} + +register_default_input_actions :: proc(input_map: ^core.InputMap) { + core.register_key_action(input_map, .W, proc(state: ^State) { + core.move_cursor_forward_start_of_word(&state.buffers[state.current_buffer]); + }, "move forward one word"); + + core.register_key_action(input_map, .K, proc(state: ^State) { + core.move_cursor_up(&state.buffers[state.current_buffer]); + }, "move up one line"); + core.register_key_action(input_map, .J, proc(state: ^State) { + core.move_cursor_down(&state.buffers[state.current_buffer]); + }, "move down one line"); + core.register_key_action(input_map, .H, proc(state: ^State) { + core.move_cursor_left(&state.buffers[state.current_buffer]); + }, "move left one char"); + core.register_key_action(input_map, .L, proc(state: ^State) { + core.move_cursor_right(&state.buffers[state.current_buffer]); + }, "move right one char"); + + core.register_ctrl_key_action(input_map, .U, proc(state: ^State) { + core.scroll_file_buffer(&state.buffers[state.current_buffer], .Up); + }, "scroll buffer up"); + core.register_ctrl_key_action(input_map, .D, proc(state: ^State) { + core.scroll_file_buffer(&state.buffers[state.current_buffer], .Down); + }, "scroll buffer up"); core.register_key_action(input_map, .I, proc(state: ^State) { state.mode = .Insert; - }); + }, "enter insert mode"); + + core.register_key_action(input_map, .SPACE, core.new_input_map(), "leader commands"); + register_default_leader_actions(&(&input_map.key_actions[.SPACE]).action.(core.InputMap)); } register_buffer_list_input_actions :: proc(input_map: ^core.InputMap) { @@ -125,25 +130,25 @@ register_buffer_list_input_actions :: proc(input_map: ^core.InputMap) { } else { state.buffer_list_window_selected_buffer = len(state.buffers)-1; } - }); + }, "move selection up"); core.register_key_action(input_map, .J, proc(state: ^State) { if state.buffer_list_window_selected_buffer >= len(state.buffers)-1 { state.buffer_list_window_selected_buffer = 0; } else { state.buffer_list_window_selected_buffer += 1; } - }); + }, "move selection down"); core.register_key_action(input_map, .ENTER, proc(state: ^State) { state.current_buffer = state.buffer_list_window_selected_buffer; state.buffer_list_window_is_visible = false; state.current_input_map = &state.input_map; - }); + }, "switch to file"); core.register_key_action(input_map, .Q, proc(state: ^State) { state.buffer_list_window_is_visible = false; state.current_input_map = &state.input_map; - }); + }, "close window"); } main :: proc() { @@ -210,27 +215,95 @@ main :: proc() { raylib.DrawRectangle(0, state.screen_height - core.source_font_height, state.screen_width, core.source_font_height, theme.get_palette_raylib_color(.Background2)); - line_info_text := raylib.TextFormat("Line: %d, Col: %d --- Slice Index: %d, Content Index: %d", buffer.cursor.line + 1, buffer.cursor.col + 1, buffer.cursor.index.slice_index, buffer.cursor.index.content_index); + line_info_text := raylib.TextFormat( + "Line: %d, Col: %d --- Slice Index: %d, Content Index: %d", + buffer.cursor.line + 1, + buffer.cursor.col + 1, + buffer.cursor.index.slice_index, + buffer.cursor.index.content_index); line_info_width := raylib.MeasureTextEx(font, line_info_text, core.source_font_height, 0).x; switch state.mode { case .Normal: - raylib.DrawRectangle(0, state.screen_height - core.source_font_height, 8 + len("NORMAL")*core.source_font_width, core.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 - core.source_font_height) }, raylib.Vector2 { 8 + line_info_width, f32(core.source_font_height) }, theme.get_palette_raylib_color(.Foreground4)); - - raylib.DrawTextEx(font, "NORMAL", raylib.Vector2 { 4, f32(state.screen_height - core.source_font_height) }, core.source_font_height, 0, theme.get_palette_raylib_color(.Background1)); + raylib.DrawRectangle( + 0, + state.screen_height - core.source_font_height, + 8 + len("NORMAL")*core.source_font_width, + core.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 - core.source_font_height) }, + raylib.Vector2 { 8 + line_info_width, f32(core.source_font_height) }, + theme.get_palette_raylib_color(.Foreground4)); + raylib.DrawTextEx( + font, + "NORMAL", + raylib.Vector2 { 4, f32(state.screen_height - core.source_font_height) }, + core.source_font_height, + 0, + theme.get_palette_raylib_color(.Background1)); case .Insert: - raylib.DrawRectangle(0, state.screen_height - core.source_font_height, 8 + len("INSERT")*core.source_font_width, core.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 - core.source_font_height) }, raylib.Vector2 { 8 + line_info_width, f32(core.source_font_height) }, theme.get_palette_raylib_color(.Foreground2)); - - raylib.DrawTextEx(font, "INSERT", raylib.Vector2 { 4, f32(state.screen_height - core.source_font_height) }, core.source_font_height, 0, theme.get_palette_raylib_color(.Background1)); + raylib.DrawRectangle( + 0, + state.screen_height - core.source_font_height, + 8 + len("INSERT")*core.source_font_width, + core.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 - core.source_font_height) }, + raylib.Vector2 { 8 + line_info_width, f32(core.source_font_height) }, + theme.get_palette_raylib_color(.Foreground2)); + raylib.DrawTextEx( + font, + "INSERT", + raylib.Vector2 { 4, f32(state.screen_height - core.source_font_height) }, + core.source_font_height, + 0, + theme.get_palette_raylib_color(.Background1)); } - raylib.DrawTextEx(font, line_info_text, raylib.Vector2 { f32(state.screen_width) - line_info_width - 4, f32(state.screen_height - core.source_font_height) }, core.source_font_height, 0, theme.get_palette_raylib_color(.Background1)); + raylib.DrawTextEx( + font, + line_info_text, + raylib.Vector2 { f32(state.screen_width) - line_info_width - 4, f32(state.screen_height - core.source_font_height) }, + core.source_font_height, + 0, + theme.get_palette_raylib_color(.Background1)); if state.buffer_list_window_is_visible { ui.draw_buffer_list_window(&state); } + + 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); + } + } + longest_description += 4; + + helper_height := i32(core.source_font_height * len(state.current_input_map.key_actions)); + + raylib.DrawRectangle( + state.screen_width - i32(longest_description * core.source_font_width), + state.screen_height - helper_height - 20, + i32(longest_description*core.source_font_width), + helper_height, + theme.get_palette_raylib_color(.Background2)); + + index := 0; + for key, action in state.current_input_map.key_actions { + raylib.DrawTextEx( + font, + raylib.TextFormat("%s - %s", key, action.description), + raylib.Vector2 { f32(state.screen_width - i32(longest_description * core.source_font_width)), f32(state.screen_height - helper_height + i32((index) * core.source_font_height) - 20) }, + core.source_font_height, + 0, + theme.get_palette_raylib_color(.Foreground1)); + index += 1; + } + } } switch state.mode { diff --git a/src/ui/ui.odin b/src/ui/ui.odin index f107462..fc9ede1 100644 --- a/src/ui/ui.odin +++ b/src/ui/ui.odin @@ -37,8 +37,6 @@ draw_menu_bar_item :: proc(item: ^MenuBarItem, x, y: i32, parent_width, parent_h raylib.DrawRectangle(x, y, parent_width, i32(font_height), theme.get_palette_raylib_color(foreground_color)); raylib.DrawTextEx(font, item_text, raylib.Vector2 { f32(x + text_padding), f32(y) }, f32(font_height), 0, theme.get_palette_raylib_color(.Background1)); - //raylib.DrawRectangle(x, y, i32(item_width) + text_padding*2, i32(1 * font_height), theme.get_palette_raylib_color(.Foreground3)); - if item.selected { // TODO: change to parent_width largest_sub_item: int @@ -49,7 +47,6 @@ draw_menu_bar_item :: proc(item: ^MenuBarItem, x, y: i32, parent_width, parent_h this_width := i32(largest_sub_item) * 8 + text_padding*2; sub_list_x := x; if horizontal { - //sub_list_x += i32(largest_sub_item) * 8; sub_list_x += parent_width; } for _, index in item.sub_items {