fortrangoingonforty/sniffly / 3bf0b4e

Browse files

start seamless mouse/kb input

Authored by espadonne
SHA
3bf0b4e5f7266ee9cd9f9eb4c64311df96c78cb2
Parents
a5a87a0
Tree
ccad51c

3 changed files

StatusFile+-
M src/gui/gtk_app.f90 5 1
M src/gui/treemap_widget.f90 33 25
M src/rendering/treemap_renderer.f90 68 1
src/gui/gtk_app.f90modified
@@ -13,7 +13,8 @@ module gtk_app
1313
                  gtk_label_new, gtk_label_set_text, gtk_widget_set_halign, &
1414
                  GTK_ALIGN_START
1515
   use g, only: g_application_run
16
-  use treemap_widget, only: create_treemap_widget, set_scan_path, register_navigation_callback
16
+  use treemap_widget, only: create_treemap_widget, set_scan_path, register_navigation_callback, &
17
+                             register_key_handler
1718
   implicit none
1819
   private
1920
 
@@ -174,6 +175,9 @@ contains
174175
     ! Add main box to window
175176
     call gtk_window_set_child(main_window_ptr, main_box)
176177
 
178
+    ! Register keyboard handler on window (not widget) for global keyboard capture
179
+    call register_key_handler(main_window_ptr)
180
+
177181
     ! Show the window
178182
     call gtk_window_present(main_window_ptr)
179183
 
src/gui/treemap_widget.f90modified
@@ -11,7 +11,8 @@ module treemap_widget
1111
   implicit none
1212
   private
1313
 
14
-  public :: create_treemap_widget, set_scan_path, get_widget_ptr, register_navigation_callback
14
+  public :: create_treemap_widget, set_scan_path, get_widget_ptr, register_navigation_callback, &
15
+            register_key_handler
1516
 
1617
   ! Callback interface for navigation events
1718
   abstract interface
@@ -87,14 +88,23 @@ contains
8788
                            c_funloc(on_click), c_null_ptr)
8889
     call gtk_widget_add_controller(widget, click_controller)
8990
 
90
-    ! Add keyboard event controller for navigation
91
+    print *, "Treemap widget created successfully"
92
+  end function create_treemap_widget
93
+
94
+  ! Register keyboard handler (called from gtk_app after window is created)
95
+  subroutine register_key_handler(window)
96
+    use gtk, only: gtk_event_controller_key_new, g_signal_connect, gtk_widget_add_controller
97
+    type(c_ptr), value :: window
98
+    type(c_ptr) :: key_controller
99
+
100
+    ! Add keyboard event controller to window for navigation
91101
     key_controller = gtk_event_controller_key_new()
92102
     call g_signal_connect(key_controller, "key-pressed"//c_null_char, &
93103
                            c_funloc(on_key_press), c_null_ptr)
94
-    call gtk_widget_add_controller(widget, key_controller)
104
+    call gtk_widget_add_controller(window, key_controller)
95105
 
96
-    print *, "Treemap widget created successfully"
97
-  end function create_treemap_widget
106
+    print *, "Key handler registered on window"
107
+  end subroutine register_key_handler
98108
 
99109
   ! Get widget pointer (for triggering redraws)
100110
   function get_widget_ptr() result(ptr)
@@ -196,7 +206,8 @@ contains
196206
 
197207
   ! Keyboard callback - handle all keyboard navigation
198208
   function on_key_press(controller, keyval, keycode, state, user_data) bind(c) result(handled)
199
-    use treemap_renderer, only: navigate_up, navigate_into_node, get_node_count, get_node_center_by_index
209
+    use treemap_renderer, only: navigate_up, navigate_into_node, get_node_count, &
210
+                                get_node_center_by_index, find_node_in_direction
200211
     type(c_ptr), value :: controller, user_data
201212
     integer(c_int), value :: keyval, keycode, state
202213
     integer(c_int) :: handled
@@ -205,7 +216,7 @@ contains
205216
 
206217
     handled = 0_c_int  ! Default: not handled
207218
 
208
-    ! Arrow keys: Navigate through nodes
219
+    ! Arrow keys: Navigate through nodes using spatial navigation
209220
     if (keyval == GDK_KEY_Left .or. keyval == GDK_KEY_Right .or. &
210221
         keyval == GDK_KEY_Up .or. keyval == GDK_KEY_Down) then
211222
 
@@ -215,26 +226,23 @@ contains
215226
       node_count = get_node_count()
216227
 
217228
       if (node_count > 0) then
218
-        ! Initialize keyboard hover if needed
219
-        if (keyboard_hover_index == 0) then
220
-          keyboard_hover_index = 1
221
-        else
222
-          ! Move keyboard hover based on arrow key
223
-          if (keyval == GDK_KEY_Right .or. keyval == GDK_KEY_Down) then
224
-            keyboard_hover_index = keyboard_hover_index + 1
225
-            if (keyboard_hover_index > node_count) keyboard_hover_index = 1  ! Wrap around
226
-          else if (keyval == GDK_KEY_Left .or. keyval == GDK_KEY_Up) then
227
-            keyboard_hover_index = keyboard_hover_index - 1
228
-            if (keyboard_hover_index < 1) keyboard_hover_index = node_count  ! Wrap around
229
-          end if
229
+        ! Determine direction: 1=up, 2=down, 3=left, 4=right
230
+        if (keyval == GDK_KEY_Up) then
231
+          keyboard_hover_index = find_node_in_direction(mouse_x, mouse_y, 1)
232
+        else if (keyval == GDK_KEY_Down) then
233
+          keyboard_hover_index = find_node_in_direction(mouse_x, mouse_y, 2)
234
+        else if (keyval == GDK_KEY_Left) then
235
+          keyboard_hover_index = find_node_in_direction(mouse_x, mouse_y, 3)
236
+        else if (keyval == GDK_KEY_Right) then
237
+          keyboard_hover_index = find_node_in_direction(mouse_x, mouse_y, 4)
230238
         end if
231239
 
232
-        ! Update mouse position to match keyboard hover for seamless transition
233
-        call get_node_center_by_index(keyboard_hover_index, mouse_x, mouse_y, success)
234
-        if (success) then
235
-          print *, "Arrow key - keyboard hover index: ", keyboard_hover_index, " at (", mouse_x, ",", mouse_y, ")"
236
-        else
237
-          print *, "Arrow key - failed to get node center for index: ", keyboard_hover_index
240
+        ! Update mouse position to center of selected node for seamless transition
241
+        if (keyboard_hover_index > 0) then
242
+          call get_node_center_by_index(keyboard_hover_index, mouse_x, mouse_y, success)
243
+          if (success) then
244
+            print *, "Arrow key - moved to node ", keyboard_hover_index, " at (", mouse_x, ",", mouse_y, ")"
245
+          end if
238246
         end if
239247
       end if
240248
 
src/rendering/treemap_renderer.f90modified
@@ -16,7 +16,7 @@ module treemap_renderer
1616
   public :: scan_and_render, init_renderer, get_root_node, scan_and_render_with_hover, &
1717
             scan_and_render_with_interaction, find_node_at_position, navigate_into_node, &
1818
             navigate_up, get_breadcrumb_path, get_path_depth, get_node_count, &
19
-            get_node_center_by_index
19
+            get_node_center_by_index, find_node_in_direction
2020
 
2121
   ! Global state
2222
   type(file_node), save, target :: root_node
@@ -395,6 +395,73 @@ contains
395395
     success = .true.
396396
   end subroutine get_node_center_by_index
397397
 
398
+  ! Find the best node in a given direction from current position
399
+  ! direction: 1=up, 2=down, 3=left, 4=right
400
+  function find_node_in_direction(from_x, from_y, direction) result(best_index)
401
+    use iso_fortran_env, only: real64
402
+    real(c_double), intent(in) :: from_x, from_y
403
+    integer, intent(in) :: direction
404
+    integer :: best_index
405
+    integer :: i
406
+    real(real64) :: cx, cy, dx, dy, dist, score, best_score
407
+    real(real64) :: directional_component, perpendicular_component
408
+
409
+    best_index = 0
410
+    best_score = 1.0d20  ! Large number
411
+
412
+    if (.not. associated(current_view_node)) return
413
+    if (current_view_node%num_children == 0) return
414
+
415
+    ! For each child node, calculate score based on direction
416
+    do i = 1, current_view_node%num_children
417
+      ! Get center of this node
418
+      cx = real(current_view_node%children(i)%bounds%x, real64) + &
419
+           real(current_view_node%children(i)%bounds%width, real64) / 2.0d0
420
+      cy = real(current_view_node%children(i)%bounds%y, real64) + &
421
+           real(current_view_node%children(i)%bounds%height, real64) / 2.0d0
422
+
423
+      dx = cx - real(from_x, real64)
424
+      dy = cy - real(from_y, real64)
425
+
426
+      ! Check if node is in the correct direction
427
+      select case (direction)
428
+      case (1)  ! Up
429
+        if (dy >= 0.0d0) cycle  ! Skip nodes below or at same level
430
+        directional_component = abs(dy)  ! Distance upward
431
+        perpendicular_component = abs(dx)  ! Horizontal offset
432
+      case (2)  ! Down
433
+        if (dy <= 0.0d0) cycle  ! Skip nodes above or at same level
434
+        directional_component = abs(dy)  ! Distance downward
435
+        perpendicular_component = abs(dx)  ! Horizontal offset
436
+      case (3)  ! Left
437
+        if (dx >= 0.0d0) cycle  ! Skip nodes to right or at same position
438
+        directional_component = abs(dx)  ! Distance leftward
439
+        perpendicular_component = abs(dy)  ! Vertical offset
440
+      case (4)  ! Right
441
+        if (dx <= 0.0d0) cycle  ! Skip nodes to left or at same position
442
+        directional_component = abs(dx)  ! Distance rightward
443
+        perpendicular_component = abs(dy)  ! Vertical offset
444
+      case default
445
+        cycle
446
+      end select
447
+
448
+      ! Score: prioritize alignment (low perpendicular) and closeness (low directional)
449
+      ! Weight perpendicular offset more heavily to prefer aligned nodes
450
+      score = directional_component + perpendicular_component * 2.0d0
451
+
452
+      if (score < best_score) then
453
+        best_score = score
454
+        best_index = i
455
+      end if
456
+    end do
457
+
458
+    ! If no node found in direction, wrap to closest node in any direction
459
+    if (best_index == 0 .and. current_view_node%num_children > 0) then
460
+      best_index = 1  ! Default to first node
461
+    end if
462
+
463
+  end function find_node_in_direction
464
+
398465
   ! Render hover highlight overlay
399466
   subroutine render_hover_highlight(cr, node)
400467
     type(c_ptr), intent(in) :: cr