diff --git a/config/default.focus-config b/config/default.focus-config index a8066a2c6..cfb782188 100644 --- a/config/default.focus-config +++ b/config/default.focus-config @@ -64,6 +64,8 @@ show_cursors_off_screen: true persist_local_search_results: false # if true, search results will stay highlighted and you have to dismiss them using the `escape` action load_most_recent_project_on_start: false projects_sorting_order: most_recent_first +grid_navigation_display_duration: 2.0 +grid_navigation_quadrant_mode: false build_panel_stays_in_one_place: false # if false, the build panel will flip to the inactive pane in two pane layouts build_panel_line_wrap_always_on: true @@ -203,6 +205,17 @@ NumpadEnter break_line Ctrl-NumpadEnter new_line_below_without_breaking Ctrl-Shift-NumpadEnter new_line_above_without_breaking +{Shift}-Ctrl-Numpad0 grid_navigation_reset +{Shift}-Ctrl-Numpad1 grid_navigation_southwest +{Shift}-Ctrl-Numpad2 grid_navigation_south +{Shift}-Ctrl-Numpad3 grid_navigation_southeast +{Shift}-Ctrl-Numpad4 grid_navigation_west +{Shift}-Ctrl-Numpad5 grid_navigation_center +{Shift}-Ctrl-Numpad6 grid_navigation_east +{Shift}-Ctrl-Numpad7 grid_navigation_northwest +{Shift}-Ctrl-Numpad8 grid_navigation_north +{Shift}-Ctrl-Numpad9 grid_navigation_northeast + Ctrl-1 switch_to_left_editor Ctrl-2 switch_to_right_editor Ctrl-, switch_to_other_editor diff --git a/config/default_macos.focus-config b/config/default_macos.focus-config index 1ee896033..848655387 100644 --- a/config/default_macos.focus-config +++ b/config/default_macos.focus-config @@ -61,6 +61,8 @@ show_cursors_off_screen: true persist_local_search_results: false # if true, search results will stay highlighted and you have to dismiss them using the `escape` action load_most_recent_project_on_start: false projects_sorting_order: most_recent_first +grid_navigation_display_duration: 2.0 +grid_navigation_quadrant_mode: false build_panel_stays_in_one_place: false # if true, the build panel will flip to the inactive pane in two pane layouts build_panel_line_wrap_always_on: true @@ -181,6 +183,17 @@ NumpadEnter break_line Cmd-NumpadEnter new_line_below_without_breaking Cmd-Shift-NumpadEnter new_line_above_without_breaking +{Shift}-Cmd-Numpad0 grid_navigation_reset +{Shift}-Cmd-Numpad1 grid_navigation_southwest +{Shift}-Cmd-Numpad2 grid_navigation_south +{Shift}-Cmd-Numpad3 grid_navigation_southeast +{Shift}-Cmd-Numpad4 grid_navigation_west +{Shift}-Cmd-Numpad5 grid_navigation_center +{Shift}-Cmd-Numpad6 grid_navigation_east +{Shift}-Cmd-Numpad7 grid_navigation_northwest +{Shift}-Cmd-Numpad8 grid_navigation_north +{Shift}-Cmd-Numpad9 grid_navigation_northeast + Cmd-1 switch_to_left_editor Cmd-2 switch_to_right_editor Cmd-, switch_to_other_editor diff --git a/modules/Simp/backend/gl.jai b/modules/Simp/backend/gl.jai index c2e96a1f9..142aca5e0 100644 --- a/modules/Simp/backend/gl.jai +++ b/modules/Simp/backend/gl.jai @@ -347,8 +347,8 @@ vec3 hsl2rgb( in vec3 c ) void main () { vec2 uv = vec2((gl_FragCoord.x - rect.x) / rect.z, (gl_FragCoord.y - rect.y) / rect.w); - // This is line 44 - switch (mode) { + float a = iterated_color.a; + switch (mode) { // mode values come from Gradient_Mode in color_picker.jai case 0x00: // 2d controlled by red color.r = value; color.gb = uv.xy; @@ -443,13 +443,21 @@ void main () { case 0x26: // 1d horizontal displaying alpha color.rgb = vec3(uv.x); break; + + case 0x30: // Checkboard alpha + int i = int((gl_FragCoord.x - rect.x) / value); + int j = int((gl_FragCoord.y - rect.y) / value); + i = (i ^ j) & 1; + a = iterated_color.a * float(i); + color = iterated_color; + break; } // When color is between two displayable values dither it to avoid banding; round it up or down randomly, // proportionate to which it is nearer to. vec4 remainder = fract(color * 255.0); color = color - (remainder / 255.0) + (1.0 / 255.0) - (step(remainder, random4(uv)) / 255.0); - color.a = iterated_color.a; + color.a = a; } #endif // FRAGMENT_SHADER GLSL diff --git a/src/config.jai b/src/config.jai index 15e5935f8..d8e028383 100644 --- a/src/config.jai +++ b/src/config.jai @@ -609,6 +609,8 @@ Settings :: struct { search_is_case_sensitive_when_uppercase_present := false; show_selected_text_length := false; load_most_recent_project_on_start := false; + grid_navigation_display_duration := 2.0; + grid_navigation_quadrant_mode := false; build_panel_width_percent := 50; build_panel_height_percent := 50; diff --git a/src/config_parser.jai b/src/config_parser.jai index aa6847df1..c795c670b 100644 --- a/src/config_parser.jai +++ b/src/config_parser.jai @@ -945,6 +945,19 @@ parse_settings_line :: (using parser: *Config_Parser, line: string) -> success: END; print_to_builder(*b, code, setting.name); + case .FLOAT; + code := #string END + float_val, success := parse_float(*value); + if success { + add_highlight(parser, cast(s64)(setting_value.data - line.data), setting_value.count, .value); + result.config.settings.%1 = float_val; + } else { + add_highlight(parser, cast(s64)(setting_value.data - line.data), setting_value.count, .error); + return true, tprint("Couldn't parse '%1' - expected a valid float, got '%%'", setting_value); + } + END; + print_to_builder(*b, code, setting.name); + case .ENUM; code := #string END enum_info := type_info(type_of(Settings.%1)); diff --git a/src/draw.jai b/src/draw.jai index cf7a1a9b5..cd5774613 100644 --- a/src/draw.jai +++ b/src/draw.jai @@ -736,14 +736,18 @@ get_text_offset :: inline () -> Vector2 { return Vector2.{char_x_advance, -char_x_advance}; } -coords_from_mouse_position :: (using editor: Editor, rect: Rect) -> Coords { - pointer := snap_to_rect(mouse.pointer, rect); // when we drag outside the editor we still want to interact +coords_from_screen_position :: (using editor: Editor, rect: Rect, position: Vector2) -> Coords { + pointer := snap_to_rect(position, rect); // when we drag outside the editor we still want to interact mouse_pos := pointer - bottom_left(rect) - get_text_offset(); mouse_pos.x += viewport.left; mouse_pos.y = rect.h - mouse_pos.y + viewport.top - ifx dpi_scale > 1.0 then 1 else 0; return .{ line = xx (mouse_pos.y / line_height), col = xx ((mouse_pos.x / char_x_advance) + 0.5) }; } +coords_from_mouse_position :: (using editor: Editor, rect: Rect) -> Coords { + return coords_from_screen_position(editor, rect, mouse.pointer); +} + draw_editor :: (editor_id: s64, main_area: Rect, status_bar_height: float, ui_id: Ui_Id, total_editor_area := Rect.{}) { as_build_panel := is_build_panel(editor_id); @@ -1524,6 +1528,83 @@ draw_editor :: (editor_id: s64, main_area: Rect, status_bar_height: float, ui_id draw_rect_raw(cursor_mark_rect, Color.CURSOR); } } + + if cursors_active && grid_navigation.editor_id_should_jump == editor_id { + // Performing a grid jump on the cursor: move it to the cursor position which is nearest to the center of our grid. + // This depends on the lenghts of the surrounding lines. + grid_navigation.editor_id_should_jump = -1; + position: Vector2; + position.x = rect.x + rect.w * (grid_navigation.uv.x + grid_navigation.size / 2); + position.y = rect.y + rect.h * (grid_navigation.uv.y + grid_navigation.size / 2); + desired_coords := coords_from_screen_position(editor, rect, position); + + // Loop over nearby lines to find the closest possible cursor. + cursor := leave_only_original_cursor(editor); + cursor.pos = coords_to_offset(editor, buffer, desired_coords); + best_pos := cursor.pos; + actual_coords := get_cursor_coords(editor, buffer, cursor); + cursor_screen_pos := get_cursor_screen_pos(text_origin, actual_coords.pos); + cursor_screen_pos.y += line_height / 2; // get_cursor_screen_pos returns the bottom of the cursor. + min_distance := distance_squared(position, cursor_screen_pos); + for line_offset : -5 .. +5 { + if !line_offset continue; + cursor.pos = coords_to_offset(editor, buffer, .{desired_coords.line + cast(s32) line_offset, desired_coords.col}); + actual_coords := get_cursor_coords(editor, buffer, cursor); + cursor_screen_pos := get_cursor_screen_pos(text_origin, actual_coords.pos); + cursor_screen_pos.y += line_height / 2; + distance := distance_squared(position, cursor_screen_pos); + if distance < min_distance { + min_distance = distance; + best_pos = cursor.pos; + } + } + cursor.pos = best_pos; + if !grid_navigation.extend_selection cursor.sel = cursor.pos; + cursor.col_wanted = -1; + cursor_moved = .jumped; + cursors_start_blinking(); + + if grid_navigation.size < grid_navigation.finish_when_smaller_than reset_grid_navigation(); + } + + if cursors_active && frame_time64 < grid_navigation.last_press_time + config.settings.grid_navigation_display_duration { + remaining_duration : float64 = config.settings.grid_navigation_display_duration - (frame_time64 - grid_navigation.last_press_time); + pulse_rate :: CURSOR_BLINK_SPEED_MS / 1000.0; + pulse_t := fmod_cycling(cast(float) frame_time64, pulse_rate * 2) / pulse_rate; + if pulse_t > 1.0 pulse_t = 2.0 - pulse_t; + color := lerp(map_color_to_vec4(.CURSOR), .{1, 1, 1, 1}, pulse_t * 0.5); + color.w = ifx remaining_duration > grid_navigation.fade_out_duration then 1.0 else cast(float) (remaining_duration / grid_navigation.fade_out_duration); + + Simp.set_shader_for_gradient(cast,force(Vector4) rect, cast(u8) Gradient_Mode.checkered_alpha, char_x_advance / 2); + + grid := Vector2.{rect.w * grid_navigation.size, rect.h * grid_navigation.size}; + r := Rect.{rect.x + rect.w * grid_navigation.uv.x, rect.y + rect.h * grid_navigation.uv.y, grid.x, grid.y}; + if !config.settings.grid_navigation_quadrant_mode { + grid /= 3; + + horizontal := r; + horizontal.h = 1; + horizontal.y += grid.y; draw_rect_with_raw_color(horizontal, color); + horizontal.y += grid.y; draw_rect_with_raw_color(horizontal, color); + + vertical := r; + vertical.w = 1; + vertical.x += grid.x; draw_rect_with_raw_color(vertical, color); + vertical.x += grid.x; draw_rect_with_raw_color(vertical, color); + } else { + grid /= 2; + + horizontal := r; + horizontal.h = 1; + horizontal.y += grid.y; draw_rect_with_raw_color(horizontal, color); + + vertical := r; + vertical.w = 1; + vertical.x += grid.x; draw_rect_with_raw_color(vertical, color); + } + + Simp.immediate_flush(); + } } // Draw line numbers diff --git a/src/editors.jai b/src/editors.jai index fb9aa9b9e..2fd992d08 100644 --- a/src/editors.jai +++ b/src/editors.jai @@ -48,6 +48,71 @@ editors_handle_event :: (event: Input.Event) -> handled: bool { return false; } +grid_navigation : struct { + last_press_time: float64; + editor_id_should_jump: int; + extend_selection: bool; + uv: Vector2; + size: float; + + finish_when_smaller_than :: 1.0 / 3.0 / 3.0; + fade_out_duration : float64 : 0.2; +} + +handle_grid_navigate :: (grid_action : enum { northwest; north; northeast; west; center; east; southwest; south; southeast; reset; }, keep_selection: bool) { + using grid_navigation; + + if last_press_time + config.settings.grid_navigation_display_duration < frame_time64 || grid_action == .reset { + reset_grid_navigation(); + } + + divisor, small_step, big_step: float; + if !config.settings.grid_navigation_quadrant_mode { + divisor = 3; + small_step = 1; + big_step = 2; + } + else { + divisor = 2; + small_step = 0.5; + big_step = 1; + } + + if grid_action != .reset size /= divisor; + + u, v: float; + if grid_action == { + case .reset; #through; + case .southwest; u = size * 0; v = size * 0; + case .south; u = size * small_step; v = size * 0; + case .southeast; u = size * big_step; v = size * 0; + case .west; u = size * 0; v = size * small_step; + case .center; u = size * small_step; v = size * small_step; + case .east; u = size * big_step; v = size * small_step; + case .northwest; u = size * 0; v = size * big_step; + case .north; u = size * small_step; v = size * big_step; + case .northeast; u = size * big_step; v = size * big_step; + } + + uv.x += u; + uv.y += v; + editor_id_should_jump = editors.active; + extend_selection = keep_selection; + last_press_time = frame_time64; +} + +soft_reset_grid_navigation :: () { // Will cause the drawn grid to fade out rather than instantly vanish. + using grid_navigation; + last_press_time = min(last_press_time, frame_time64 - config.settings.grid_navigation_display_duration + grid_navigation.fade_out_duration); +} + +reset_grid_navigation :: () { + using grid_navigation; + last_press_time = 0; + size = 1.0; + uv = .{}; +} + activate_editors :: () { active_global_widget = .editors; cursors_start_blinking(); @@ -241,6 +306,17 @@ active_editor_handle_event :: (editor: *Editor, buffer: *Buffer, event: Input.Ev case .jump_to_file_end; move_to_file_end (editor, buffer); case .jump_to_matching_bracket; move_to_matching_bracket (editor, buffer, shift_pressed); keep_selection = true; + case .grid_navigation_northwest; handle_grid_navigate (.northwest, shift_pressed); + case .grid_navigation_north; handle_grid_navigate (.north, shift_pressed); + case .grid_navigation_northeast; handle_grid_navigate (.northeast, shift_pressed); + case .grid_navigation_west; handle_grid_navigate (.west, shift_pressed); + case .grid_navigation_center; handle_grid_navigate (.center, shift_pressed); + case .grid_navigation_east; handle_grid_navigate (.east, shift_pressed); + case .grid_navigation_southwest; handle_grid_navigate (.southwest, shift_pressed); + case .grid_navigation_south; handle_grid_navigate (.south, shift_pressed); + case .grid_navigation_southeast; handle_grid_navigate (.southeast, shift_pressed); + case .grid_navigation_reset; handle_grid_navigate (.reset, shift_pressed); + case .select_line; select_lines (editor, buffer); keep_selection = true; case .copy; copy_selection_to_clipboard (editor, buffer); @@ -301,6 +377,7 @@ active_editor_handle_event :: (editor: *Editor, buffer: *Buffer, event: Input.Ev if new_group then new_edit_group(buffer, editor); // do it after action + if !is_a_grid_navigation_action(action) soft_reset_grid_navigation(); if is_a_move_action(action) && shift_pressed then keep_selection = true; if editor.cursor_moved && !keep_selection { @@ -341,6 +418,8 @@ active_editor_type_char :: (char: u32) { return; } + soft_reset_grid_navigation(); + if buffer.readonly return; utf8_char := convert_utf32_to_utf8(char); @@ -2094,6 +2173,25 @@ is_a_move_action :: (action: Action_Editors) -> bool { case .jump_to_file_start; return true; case .jump_to_file_end; return true; } + + if is_a_grid_navigation_action(action) return true; + + return false; +} + +is_a_grid_navigation_action :: (action: Action_Editors) -> bool { + if action == { + case .grid_navigation_northwest; return true; + case .grid_navigation_north; return true; + case .grid_navigation_northeast; return true; + case .grid_navigation_west; return true; + case .grid_navigation_center; return true; + case .grid_navigation_east; return true; + case .grid_navigation_southwest; return true; + case .grid_navigation_south; return true; + case .grid_navigation_southeast; return true; + case .grid_navigation_reset; return true; + } return false; } @@ -4053,6 +4151,8 @@ move_to_editor_history_frame :: (frame: Editor_History_Frame, old_frame: Editor_ put_cursor_in_valid_spot(cursor, open_buffers[editor.buffer_id]); } +CURSOR_BLINK_SPEED_MS :: 500; + #scope_file cursor_blink_start: Apollo_Time; @@ -4061,6 +4161,5 @@ cursors_blinking := false; global_config_buffer_id := -1; global_troubleshooting_buffer_id := -1; -CURSOR_BLINK_SPEED_MS :: 500; #import "Clipboard"; diff --git a/src/keymap.jai b/src/keymap.jai index 2db646878..af1ab7156 100644 --- a/src/keymap.jai +++ b/src/keymap.jai @@ -417,6 +417,17 @@ ACTIONS_EDITORS :: #run arrays_concat(ACTIONS_COMMON, string.[ "toggle_line_wrap", "toggle_line_numbers", "copy_current_line_info", + + "grid_navigation_northwest", + "grid_navigation_north", + "grid_navigation_northeast", + "grid_navigation_west", + "grid_navigation_center", + "grid_navigation_east", + "grid_navigation_southwest", + "grid_navigation_south", + "grid_navigation_southeast", + "grid_navigation_reset", ]); ACTIONS_OPEN_FILE_DIALOG :: #run arrays_concat(ACTIONS_COMMON, string.[ diff --git a/src/widgets/color_picker.jai b/src/widgets/color_picker.jai index 9310d7cb6..af27644ca 100644 --- a/src/widgets/color_picker.jai +++ b/src/widgets/color_picker.jai @@ -36,6 +36,8 @@ Gradient_Mode :: enum u8 { saturation_horizontal_1d :: 0x24; lightness_horizontal_1d :: 0x25; alpha_horizontal_1d :: 0x26; + + checkered_alpha :: 0x30; } color_picker_set_mode :: (mode: Gradient_Mode) {