support for scrolling regions in the UI
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 2 Mar 2022 00:33:29 +0000 (18:33 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 2 Mar 2022 00:33:29 +0000 (18:33 -0600)
run_tree/assets/shaders/font.glsl
run_tree/assets/shaders/imgui.glsl
src/entity/editor.onyx
src/gfx/font.onyx
src/gfx/immediate.onyx
src/gfx/ui.onyx
src/utils/input.onyx

index db0265d5ea4c11edd4334d928d079cadf1f69b20..8fdf5c979ad5d3a2b68023c1308903ec58c586c6 100644 (file)
@@ -14,8 +14,13 @@ layout(std140) uniform u_window_matrix_block {
     mat4 u_window;
 };
 
+layout(std140) uniform u_world_matrix_block {
+    mat4 u_world;
+    mat4 u_model;
+};
+
 void main() {
-    gl_Position = u_window * vec4(mix(a_pos_top_left, a_pos_bottom_right, a_interp), 0, 1);
+    gl_Position = u_window * u_world * u_model * vec4(mix(a_pos_top_left, a_pos_bottom_right, a_interp), 0, 1);
     v_texture = mix(a_tex_top_left, a_tex_bottom_right, a_interp);
 }
 
index 16b6129b16248a2cdfce8caa6434e570848d3914..26c0260e394d4ba94071bd6a42a9c899ac508709 100644 (file)
@@ -13,8 +13,13 @@ layout(std140) uniform u_window_matrix_block {
     mat4 u_window;
 };
 
+layout(std140) uniform u_world_matrix_block {
+    mat4 u_world;
+    mat4 u_model;
+};
+
 void main() {
-    gl_Position = u_window * vec4(a_pos, 0, 1);
+    gl_Position = u_window * u_world * u_model * vec4(a_pos, 0, 1);
     v_tex = a_tex;
     v_col = a_col;
 }
index 17cb769cdf1234c19baf7ce57006174aa0fdba36..570f4dd3db6e3b904839485219a5b4f6d80cf91a 100644 (file)
@@ -271,6 +271,13 @@ editor_draw :: () {
 #local render_entity_fields :: (entity: ^Entity, x, y, w, h: f32) {
     assert(entity != null, "entity is null");
 
+    if active_index >= 0 {
+        scrolling_region_start(.{x - sidebar_width, y, w + sidebar_width, h});
+    } else {
+        scrolling_region_start(.{x, y, w, h});
+    }
+    defer scrolling_region_stop();
+
     info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(entity.schematic);
     if info != null {
         font_print(editor_big_font, x + 2, y + 24, info.name);
@@ -372,7 +379,6 @@ editor_draw :: () {
     w := sidebar_width / 2;
     h := 200.0f;
     x := ~~ window_width - sidebar_width;
-    y = math.min(y, ~~ window_height - h);
     immediate_set_color(.{.3,.3,.3});
     immediate_rectangle(x, y, w, h);
 
index 094760ead05dc9e891dde9cab0c0963f7d3e0e48..35994bbb11d3719cb11e7cbb7db6e1cfd404a41e 100644 (file)
@@ -19,6 +19,7 @@ fonts_init :: () {
     font_shader = shader_make("./assets/shaders/font.glsl");
     shader_use(font_shader);
     shader_link_window_matrix_block(font_shader);
+    shader_link_world_matrix_block(font_shader);
 
     glGenVertexArrays(1, ^font_vao);
     glBindVertexArray(font_vao);
index 2d9d67a69e36dd9f14f440f5203e60a09116f577..487e5bd872ddf42c33e1e8ca0454d4df671e6471 100644 (file)
@@ -10,6 +10,7 @@ immediate_init :: () {
     imgui_shader = shader_make(Shader_Path);
     shader_use(imgui_shader);
     shader_link_window_matrix_block(imgui_shader);
+    shader_link_world_matrix_block(imgui_shader);
     shader_set_uniform(imgui_shader, #cstr "u_texture_enabled", 0.0f);
     shader_set_uniform(imgui_shader, #cstr "u_texture", 0);
 
@@ -145,6 +146,13 @@ immediate_pop_scissor :: () {
     }
 }
 
+immediate_set_scroll :: (scroll_x, scroll_y: f32) {
+    offset = .{ scroll_x, scroll_y };
+    update_model_matrix(offset);
+}
+
+immediate_get_scroll :: () => offset;
+
 Color :: struct {
     r, g, b :  f32;
     a       := 1.0f;
@@ -180,6 +188,8 @@ Immediate_Vertex :: struct {
     }
     scissors: [..] Scissor;
 
+    offset: Vector2;
+
     set_rendering_type :: (new_type: typeof rendering_type) {
         if rendering_type != new_type {
             immediate_flush();
index 6d74b1001df96984b49bc7318a16df8eddcaf39d..b1b0f965865b517fffee4f9aff5a838aaf208200 100644 (file)
@@ -343,6 +343,38 @@ draw_checkbox :: (use r: Rect, value: ^bool, text: str, theme := ^default_checkb
 }
 
 
+
+scrolling_region_start :: (r: Rect, max_y_scroll := 10000.0f, site := #callsite, increment := 0) {
+    hash := get_site_hash(site, increment);
+    mx, my := mouse_get_position();
+    state := map.get_ptr(^scroll_states, hash);
+    if state == null {
+        animation_states[hash] = .{};
+        state = ^scroll_states[hash];
+    }
+
+    contains := Rect.contains(r, .{~~mx, ~~my});
+    if contains {
+        scroll_delta := mouse_get_scroll_vector();
+        state.xscroll -= scroll_delta.x * 20;
+        state.yscroll -= scroll_delta.y * 20;
+
+        state.yscroll = math.clamp(state.yscroll, 0, max_y_scroll);
+    }
+
+    immediate_flush();
+    immediate_push_scissor(r.x, r.y, r.w, r.h);
+    immediate_set_scroll(-state.xscroll, -state.yscroll);
+}
+
+scrolling_region_stop :: () {
+    immediate_flush();
+    immediate_pop_scissor();
+    immediate_set_scroll(0, 0);
+}
+
+
+
 #local {
     hot_item    : UI_Id = 0
     active_item : UI_Id = 0
@@ -423,6 +455,14 @@ draw_checkbox :: (use r: Rect, value: ^bool, text: str, theme := ^default_checkb
         return false;
     }
 
+
+    Scroll_State :: struct {
+        xscroll: f32;
+        yscroll: f32;
+    }
+
+    scroll_states : Map(UI_Id, Scroll_State);
+
     get_site_hash :: macro (site: CallSite, increment := 0) -> UI_Id {
         hash :: package core.hash
         file_hash   := hash.to_u32(site.file);
index 2a5f217cf407d12573050905c3a7b3bcfc4f81b3..5273c5076f7e623a58cd9363dcdab80cc29566b8 100644 (file)
@@ -1,6 +1,13 @@
 use package core
 use package glfw3
 
+// If you are offseting the mouse coordinate for world space
+// or UI scrolling etc., set this function to be the function
+// to ask for the current offset. Currently scaling the mouse
+// coordinate is not supported, only linear translations.
+Mouse_Offset_Function :: immediate_get_scroll
+
+
 #local {
        keys_this_frame  : [..] u32   // Keys currently being pressed this frame
        keys_pulse_frame : [..] u32   // Keys pressed during this frame, only set once per keypress
@@ -8,6 +15,9 @@ use package glfw3
 
        buttons_this_frame: [8] bool  // Mouse buttons being pressed this frame
        buttons_last_frame: [8] bool  // Mouse buttons being pressed last frame
+
+       scroll_x: f64
+       scroll_y: f64
 }
 
 input_update :: () {
@@ -30,6 +40,8 @@ input_post_update :: () {
 
        last_mouse_x = mouse_x;
        last_mouse_y = mouse_y;
+       scroll_x = 0;
+       scroll_y = 0;
 }
 
 input_get_keys_this_frame :: () -> [] u32 {
@@ -62,22 +74,43 @@ mouse_get_delta_vector :: () -> Vector2 {
 }
 
 mouse_get_position :: () -> (f64, f64) {
-       return mouse_x, mouse_y;
+       mx, my := mouse_x, mouse_y;     
+       #if #defined(Mouse_Offset_Function) {
+               scroll := Mouse_Offset_Function();
+               mx -= ~~ scroll.x;
+               my -= ~~ scroll.y;
+       }
+       return mx, my;
 }
 
 mouse_get_position_vector :: () -> Vector2 {
-       return .{ ~~mouse_x, ~~mouse_y };
+       mx, my := mouse_x, mouse_y;     
+       #if #defined(Mouse_Offset_Function) {
+               scroll := Mouse_Offset_Function();
+               mx -= ~~ scroll.x;
+               my -= ~~ scroll.y;
+       }
+       return .{ ~~mx, ~~my };
 }
 
+mouse_get_scroll :: () -> (f64, f64) {
+       return scroll_x, scroll_y;
+}
+
+mouse_get_scroll_vector :: () -> Vector2 {
+       return .{ ~~scroll_x, ~~scroll_y };
+}
 
 input_bind_glfw_events :: (window: GLFWwindow_p) {
        glfwSetKeyCallback(window, INPUT_KEY_EVENT);
        glfwSetMouseButtonCallback(window, INPUT_BUTTON_EVENT);
+       glfwSetScrollCallback(window, INPUT_SCROLL_EVENT);
 }
 
 #local {
        INPUT_BUTTON_EVENT :: "__input_button_event"
        INPUT_KEY_EVENT    :: "__input_key_event"
+       INPUT_SCROLL_EVENT :: "__input_scroll_event"
 }
 
 #export INPUT_BUTTON_EVENT (window: GLFWwindow_p, button, action, mod: u32) {
@@ -99,3 +132,8 @@ input_bind_glfw_events :: (window: GLFWwindow_p) {
                array.remove(^keys_this_frame, key);
        }
 }
+
+#export INPUT_SCROLL_EVENT (window: GLFWwindow_p, xoff, yoff: f64) {
+       scroll_x = xoff;
+       scroll_y = yoff;
+}