markdown · 15570 bytes Raw Blame History

improving the breadcrumb

right now the breadcrumb interactively is not functional. as advertised we can click portions of the breadcrumb to jump to but every click is just registered as the active dir and so no jump is performed I think we ought to think like web devs and make a breadcrumb component

here's my vision for the

improved breadcrumb

  • is color coded:
    • gray/dim for inactive dirs in the path
    • black for "/"
    • bold red for active dir
    • root should be green to indicate we should try to make it clickable
      • though I imagine that's hard with the pixel adjustments
  • is back/forwards aware
    • if we previously navigated from a path say we went from ~/Downloads to ~/
      • we expect the path to be /home/matthewwolffe/Downloads
        • with Downlaods and home greyed out.
  • should look as similar to the current breadcrumb as possible. though maybe we should add hover state to dirs to indcate they can be clicked
    • hover ideas
      • darker gray for inactive dirs
      • lose bold effect on active dir

clear!? it may be that the vision is unattainable within a single text string, as in we can't detect fine tuned clicks like that let me know if this vision is impossible!


FEASIBILITY ASSESSMENT (2025-11-11)

Verdict:TOTALLY FEASIBLE - Vision is achievable with custom Cairo rendering

Approaches Considered:

Option 1: Separate GTK Buttons (Standard)

Pros:

  • ✅ Easy (~100 lines of code)
  • ✅ Automatic hover/click detection
  • ✅ Built-in accessibility
  • ✅ CSS styling support
  • ✅ 3-4 hours implementation time

Cons:

  • ❌ Button borders/padding may look less seamless
  • ❌ Less control over exact appearance

Option 2: GtkLabel + Pango Hit-Testing (Hybrid)

Pros:

  • ⚠️ No button visual artifacts
  • ⚠️ Single text widget

Cons:

  • ❌ Pango xy_to_index() gives character indices, not semantic segments
  • ❌ Must manually parse / delimiter positions
  • ❌ Complex hover state management (~250 lines)
  • ❌ Poor accessibility

Option 3: Custom Cairo Rendering (Chosen) ✅

Pros:

  • Complete control over appearance (exact vision match)
  • ✅ Seamless floating text aesthetic
  • ✅ Smooth hover effects (darker gray)
  • ✅ Color coding exactly as specified
  • ✅ Back/forward awareness (dim previously-visited segments)
  • ✅ No button borders or padding artifacts

Cons:

  • ⚠️ More code (~350 lines)
  • ⚠️ Manual hit-testing required
  • ⚠️ 2-3 days implementation time

WHY CAIRO?

Decision rationale:

  1. Achieves the vision exactly - No compromises on appearance
  2. Performance is negligible - Breadcrumb is tiny compared to treemap (5-10 text segments vs 100s-1000s of rectangles)
  3. State safety is already solved - GTK event loop serialization + cached path segments
  4. You're already using Cairo - Treemap widget proves you know the APIs
  5. Seamless look - Floating text with precise hover zones, no visual artifacts

Performance Considerations:

Impact: <0.1% CPU overhead

  • Breadcrumb renders 5-10 text segments (trivial)
  • Treemap renders 100s-1000s of rectangles (heavy) - breadcrumb is nothing by comparison
  • Pango text rendering is GPU-accelerated
  • Optimization: Only redraw on hover state change, not every mouse move

During scans:

  • Zero overhead - breadcrumb only updates on navigation events
  • Same thread-safety as current implementation (GTK main loop serialization)
  • Cache path segments on main thread, draw function reads cached data

State Corruption Risk:

Assessment: Same as current implementation (safe with caching)

  • Current breadcrumb_callback() already accesses current_view pointer
  • Protection via GTK event loop (all widget access on main thread)
  • Progressive scanner uses g_idle_add() for thread-safe callbacks
  • Strategy: Cache path segments when breadcrumb_callback() fires, draw function reads cache only

IMPLEMENTATION PLAN

Phase 1: Create Custom Breadcrumb Widget Module

File: src/gui/breadcrumb_widget.f90

Tasks:

  1. Create module skeleton with Cairo drawing area

  2. Define cached state variables:

    ! Path segment cache (updated on main thread only)
    character(len=256), dimension(50), save :: cached_segments
    integer, save :: cached_segment_count = 0
    
    ! Hover state
    integer, save :: hovered_segment = 0  ! 0 = none, 1+ = segment index
    integer, save :: last_hovered_segment = -1
    
    ! Click bounds for each segment
    type :: segment_bounds
      integer :: x, y, width, height
    end type
    type(segment_bounds), dimension(50), save :: segment_rects
    
  3. Create widget initialization function:

    function create_breadcrumb_widget() result(widget)
      type(c_ptr) :: widget
      ! Create GtkDrawingArea
      ! Set draw function
      ! Attach motion/click controllers
    end function
    

Estimated time: 2-3 hours


Phase 2: Path Parsing Logic

Tasks:

  1. Split path into segments:

    ! Input: "/Users/matt/Documents/sniffly"
    ! Output:
    !   segments(1) = "/"
    !   segments(2) = "/Users"
    !   segments(3) = "/Users/matt"
    !   segments(4) = "/Users/matt/Documents"
    !   segments(5) = "/Users/matt/Documents/sniffly"
    ! (Full paths for click navigation, but display only last component)
    
  2. Handle ~ abbreviation:

    ! Replace /Users/matt with ~ for display
    ! But keep full path for navigation
    
  3. Extract display names:

    ! From "/Users/matt", display "matt"
    ! From "~/Documents", display "Documents"
    ! From "/", display "/"
    
  4. Create update_breadcrumb_cache() function:

    subroutine update_breadcrumb_cache(full_path)
      character(len=*), intent(in) :: full_path
      ! Parse path into cached_segments
      ! Store full paths for navigation
      ! Trigger redraw
    end subroutine
    

Estimated time: 3-4 hours


Phase 3: Cairo Draw Function

Tasks:

  1. Render each segment with Pango:

    subroutine draw_breadcrumb(area, cr, width, height, user_data) bind(c)
      ! For each segment:
      !   1. Determine color based on state:
      !      - Root (i==1): green
      !      - Active (i==count): bold red
      !      - Inactive: gray
      !      - Hovered inactive: darker gray
      !   2. Set Cairo color
      !   3. Draw text with Pango
      !   4. Store bounds in segment_rects(i)
      !   5. Draw separator " / "
      !   6. Update x_offset
    end subroutine
    
  2. Color logic:

    ! Root segment (/)
    if (i == 1) then
      call cairo_set_source_rgb(cr, 0.0_c_double, 0.6_c_double, 0.0_c_double)  ! Green
    
    ! Active segment (last in path)
    else if (i == cached_segment_count) then
      call cairo_set_source_rgb(cr, 0.8_c_double, 0.0_c_double, 0.0_c_double)  ! Bold red
      font_desc = pango_font_description_from_string("Sans Bold 11"//c_null_char)
    
    ! Inactive segment (hovered)
    else if (i == hovered_segment) then
      call cairo_set_source_rgb(cr, 0.3_c_double, 0.3_c_double, 0.3_c_double)  ! Darker gray
    
    ! Inactive segment (not hovered)
    else
      call cairo_set_source_rgb(cr, 0.5_c_double, 0.5_c_double, 0.5_c_double)  ! Gray
    end if
    
  3. Back/forward awareness integration:

    ! Check if segment is in backwards history
    logical function is_backwards_segment(segment_path)
      ! Compare with nav_history and nav_history_pos
      ! If segment_path appears before current position, return true
    end function
    
    ! Apply extra dimming to backwards segments
    if (is_backwards_segment(cached_segments(i))) then
      call cairo_set_source_rgba(cr, 0.5_c_double, 0.5_c_double, 0.5_c_double, 0.6_c_double)  ! Gray + transparency
    end if
    

Estimated time: 5-6 hours


Phase 4: Mouse Interaction

Tasks:

  1. Hit-testing function:

    function find_segment_at_position(x, y) result(segment_index)
      real(c_double), intent(in) :: x, y
      integer :: segment_index
      integer :: i
    
      segment_index = 0
      do i = 1, cached_segment_count
        if (x >= segment_rects(i)%x .and. &
            x <= segment_rects(i)%x + segment_rects(i)%width .and. &
            y >= segment_rects(i)%y .and. &
            y <= segment_rects(i)%y + segment_rects(i)%height) then
          segment_index = i
          return
        end if
      end do
    end function
    
  2. Motion callback (hover):

    subroutine on_breadcrumb_motion(controller, x, y, user_data) bind(c)
      integer :: new_hovered
    
      new_hovered = find_segment_at_position(x, y)
    
      ! Only redraw if hover state CHANGED
      if (new_hovered /= last_hovered_segment) then
        last_hovered_segment = new_hovered
        hovered_segment = new_hovered
        call gtk_widget_queue_draw(breadcrumb_widget_ptr)
      end if
    end subroutine
    
  3. Click callback (navigation):

    subroutine on_breadcrumb_click(gesture, n_press, x, y, user_data) bind(c)
      integer :: clicked_segment, levels_up
    
      clicked_segment = find_segment_at_position(x, y)
      if (clicked_segment > 0 .and. clicked_segment < cached_segment_count) then
        ! Navigate to clicked segment
        levels_up = cached_segment_count - clicked_segment
        call navigate_up(levels_up)
    
        ! Trigger navigation callback to update history/UI
        if (associated(nav_callback)) call nav_callback()
    
        ! Redraw
        call gtk_widget_queue_draw(breadcrumb_widget_ptr)
      end if
    end subroutine
    

Estimated time: 3-4 hours


Phase 5: Integration with gtk_app.f90

Tasks:

  1. Replace current breadcrumb bar:

    • REMOVE lines 356-364 (old breadcrumb_bar creation)
    • REMOVE breadcrumb_box_ptr global variable
    • REMOVE breadcrumb_buttons array
    • REMOVE breadcrumb_count tracking
  2. Add new breadcrumb widget:

    ! In on_activate(), after toolbar creation:
    use breadcrumb_widget, only: create_breadcrumb_widget, update_breadcrumb_cache
    
    type(c_ptr) :: breadcrumb_area
    
    ! Create custom breadcrumb widget
    breadcrumb_area = create_breadcrumb_widget()
    call gtk_widget_set_size_request(breadcrumb_area, -1_c_int, 30_c_int)  ! Height: 30px
    call gtk_box_append(main_box, breadcrumb_area)
    
  3. Update breadcrumb_callback():

    subroutine breadcrumb_callback()
      use breadcrumb_widget, only: update_breadcrumb_cache
      use treemap_renderer, only: get_current_view_node
    
      current_view => get_current_view_node()
      if (associated(current_view) .and. allocated(current_view%path)) then
        ! Update custom breadcrumb cache
        call update_breadcrumb_cache(current_view%path)
      end if
    
      ! Existing history/status logic...
    end subroutine
    
  4. CRITICAL: Unhook old click behavior:

    • REMOVE on_breadcrumb_clicked() function (lines 1328-1356)
    • REMOVE sniffly_update_breadcrumbs() function (lines 1222-1325)
    • Navigation will now be handled by breadcrumb_widget's click callback
  5. Update meson.build:

    sources += [
      'src/gui/breadcrumb_widget.f90',
      # ... existing sources
    ]
    

Estimated time: 2-3 hours


Phase 6: Back/Forward Awareness (Advanced Feature)

Tasks:

  1. Expose navigation history to breadcrumb:

    ! In gtk_app.f90, add public getter:
    public :: get_navigation_history, get_navigation_position
    
    function get_navigation_history() result(history)
      character(len=512), dimension(:), allocatable :: history
      allocate(history(nav_history_count))
      history = nav_history(1:nav_history_count)
    end function
    
  2. Check in breadcrumb draw function:

    ! In breadcrumb_widget.f90 draw function:
    use gtk_app, only: get_navigation_history, get_navigation_position
    
    character(len=512), dimension(:), allocatable :: history
    integer :: history_pos, j
    logical :: is_backwards
    
    history = get_navigation_history()
    history_pos = get_navigation_position()
    
    ! For each segment, check if it was in earlier history
    is_backwards = .false.
    do j = 1, history_pos - 1  ! Check all history before current
      if (index(trim(history(j)), trim(cached_segments(i))) > 0) then
        is_backwards = .true.
        exit
      end if
    end do
    
    ! Apply dimming if backwards
    if (is_backwards) then
      alpha = 0.6_c_double  ! More transparent
    end if
    

Estimated time: 2-3 hours


Phase 7: Testing & Polish

Tasks:

  1. Test scenarios:

    • Navigate into nested directories - breadcrumb updates correctly
    • Click middle segment - navigates up correct number of levels
    • Hover over segments - color changes smoothly
    • Click active (red) segment - no navigation (already there)
    • Back/Forward navigation - backwards segments dim appropriately
    • Rapid mouse movement - no excessive redraws
    • Long paths - breadcrumb doesn't overflow window width
  2. Handle edge cases:

    • Root path / - shows only one segment
    • Home path ~ - abbreviates correctly
    • Very long paths - add ellipsis or scrolling?
    • Window resize - breadcrumb reflows
  3. Performance validation:

    • Run profiler during hover - verify <0.1% CPU
    • Check redraw frequency - only on state changes
    • Test during active scans - no interference
  4. Visual polish:

    • Adjust colors to match GTK theme
    • Fine-tune separator spacing
    • Ensure alignment with toolbar

Estimated time: 3-4 hours


TOTAL EFFORT ESTIMATE

  • Phase 1: 2-3 hours (module setup)
  • Phase 2: 3-4 hours (path parsing)
  • Phase 3: 5-6 hours (Cairo drawing)
  • Phase 4: 3-4 hours (mouse interaction)
  • Phase 5: 2-3 hours (integration)
  • Phase 6: 2-3 hours (back/forward awareness)
  • Phase 7: 3-4 hours (testing & polish)

Total: 20-27 hours (~2-3 days of focused work)


CRITICAL NOTES

Don't Forget:

  1. Unhook old breadcrumb click behavior - Remove on_breadcrumb_clicked() and related button logic
  2. Remove old breadcrumb widgets - Clean up breadcrumb_box_ptr, breadcrumb_buttons, etc.
  3. Navigation callback integration - Breadcrumb widget needs access to navigate_up() from treemap_renderer
  4. Thread safety - Always update cache on main thread via callbacks, never in draw function
  5. Redraw optimization - Only queue redraw when hover STATE changes, not on every mouse move

Performance Gotchas:

  • Cache path segments when navigation happens, not on every draw
  • Use last_hovered_segment to detect state changes
  • Pango layout creation can be expensive - consider caching layouts between redraws if profiling shows issues

Future Enhancements:

  • Tooltip on hover showing full path + size stats
  • Keyboard navigation (Tab through segments, Enter to navigate)
  • Context menu on right-click (same as treemap)
  • Animated transitions when segments update

SUCCESS CRITERIA

Vision achieved when:

  • Breadcrumb shows path as seamless floating text (no button borders)
  • Root segment is green
  • Active (last) segment is bold red
  • Inactive segments are gray
  • Hovered segments darken
  • Clicking any segment navigates to that level
  • Back/forward navigation dims previously-visited segments
  • Performance <0.1% CPU overhead
  • No state corruption during scans
  • Looks visually seamless and polished
View source
1 # improving the breadcrumb
2 right now the breadcrumb interactively is not functional.
3 as advertised we can click portions of the breadcrumb to jump to
4 but every click is just registered as the active dir and so no jump is performed
5 I think we ought to think like web devs and make a breadcrumb component
6
7 here's my vision for the
8
9 ## improved breadcrumb
10 - is color coded:
11 - gray/dim for inactive dirs in the path
12 - black for "/"
13 - bold red for active dir
14 - root should be green to indicate we should try to make it clickable
15 - though I imagine that's hard with the pixel adjustments
16 - is back/forwards aware
17 - if we previously navigated from a path say we went from ~/Downloads to ~/
18 - we expect the path to be /home/matthewwolffe/Downloads
19 - with Downlaods and home greyed out.
20 - should look as similar to the current breadcrumb as possible. though maybe we should add hover state to dirs to indcate they can be clicked
21 - hover ideas
22 - darker gray for inactive dirs
23 - lose bold effect on active dir
24
25 clear!?
26 it may be that the vision is unattainable within a single text string, as in we can't detect fine tuned clicks like that
27 let me know if this vision is impossible!
28
29 ---
30
31 ## FEASIBILITY ASSESSMENT (2025-11-11)
32
33 **Verdict:****TOTALLY FEASIBLE** - Vision is achievable with custom Cairo rendering
34
35 ### Approaches Considered:
36
37 #### Option 1: Separate GTK Buttons (Standard)
38 **Pros:**
39 - ✅ Easy (~100 lines of code)
40 - ✅ Automatic hover/click detection
41 - ✅ Built-in accessibility
42 - ✅ CSS styling support
43 - ✅ 3-4 hours implementation time
44
45 **Cons:**
46 - ❌ Button borders/padding may look less seamless
47 - ❌ Less control over exact appearance
48
49 #### Option 2: GtkLabel + Pango Hit-Testing (Hybrid)
50 **Pros:**
51 - ⚠️ No button visual artifacts
52 - ⚠️ Single text widget
53
54 **Cons:**
55 - ❌ Pango `xy_to_index()` gives character indices, not semantic segments
56 - ❌ Must manually parse `/` delimiter positions
57 - ❌ Complex hover state management (~250 lines)
58 - ❌ Poor accessibility
59
60 #### Option 3: Custom Cairo Rendering (Chosen) ✅
61 **Pros:**
62 -**Complete control** over appearance (exact vision match)
63 - ✅ Seamless floating text aesthetic
64 - ✅ Smooth hover effects (darker gray)
65 - ✅ Color coding exactly as specified
66 - ✅ Back/forward awareness (dim previously-visited segments)
67 - ✅ No button borders or padding artifacts
68
69 **Cons:**
70 - ⚠️ More code (~350 lines)
71 - ⚠️ Manual hit-testing required
72 - ⚠️ 2-3 days implementation time
73
74 ---
75
76 ## WHY CAIRO?
77
78 **Decision rationale:**
79 1. **Achieves the vision exactly** - No compromises on appearance
80 2. **Performance is negligible** - Breadcrumb is tiny compared to treemap (5-10 text segments vs 100s-1000s of rectangles)
81 3. **State safety is already solved** - GTK event loop serialization + cached path segments
82 4. **You're already using Cairo** - Treemap widget proves you know the APIs
83 5. **Seamless look** - Floating text with precise hover zones, no visual artifacts
84
85 ### Performance Considerations:
86
87 **Impact:** <0.1% CPU overhead
88 - Breadcrumb renders 5-10 text segments (trivial)
89 - Treemap renders 100s-1000s of rectangles (heavy) - breadcrumb is nothing by comparison
90 - Pango text rendering is GPU-accelerated
91 - Optimization: Only redraw on hover **state change**, not every mouse move
92
93 **During scans:**
94 - Zero overhead - breadcrumb only updates on navigation events
95 - Same thread-safety as current implementation (GTK main loop serialization)
96 - Cache path segments on main thread, draw function reads cached data
97
98 ### State Corruption Risk:
99
100 **Assessment:** Same as current implementation (safe with caching)
101 - Current `breadcrumb_callback()` already accesses `current_view` pointer
102 - Protection via GTK event loop (all widget access on main thread)
103 - Progressive scanner uses `g_idle_add()` for thread-safe callbacks
104 - **Strategy:** Cache path segments when `breadcrumb_callback()` fires, draw function reads cache only
105
106 ---
107
108 ## IMPLEMENTATION PLAN
109
110 ### Phase 1: Create Custom Breadcrumb Widget Module
111
112 **File:** `src/gui/breadcrumb_widget.f90`
113
114 **Tasks:**
115 1. Create module skeleton with Cairo drawing area
116 2. Define cached state variables:
117 ```fortran
118 ! Path segment cache (updated on main thread only)
119 character(len=256), dimension(50), save :: cached_segments
120 integer, save :: cached_segment_count = 0
121
122 ! Hover state
123 integer, save :: hovered_segment = 0 ! 0 = none, 1+ = segment index
124 integer, save :: last_hovered_segment = -1
125
126 ! Click bounds for each segment
127 type :: segment_bounds
128 integer :: x, y, width, height
129 end type
130 type(segment_bounds), dimension(50), save :: segment_rects
131 ```
132
133 3. Create widget initialization function:
134 ```fortran
135 function create_breadcrumb_widget() result(widget)
136 type(c_ptr) :: widget
137 ! Create GtkDrawingArea
138 ! Set draw function
139 ! Attach motion/click controllers
140 end function
141 ```
142
143 **Estimated time:** 2-3 hours
144
145 ---
146
147 ### Phase 2: Path Parsing Logic
148
149 **Tasks:**
150 1. **Split path into segments:**
151 ```fortran
152 ! Input: "/Users/matt/Documents/sniffly"
153 ! Output:
154 ! segments(1) = "/"
155 ! segments(2) = "/Users"
156 ! segments(3) = "/Users/matt"
157 ! segments(4) = "/Users/matt/Documents"
158 ! segments(5) = "/Users/matt/Documents/sniffly"
159 ! (Full paths for click navigation, but display only last component)
160 ```
161
162 2. **Handle ~ abbreviation:**
163 ```fortran
164 ! Replace /Users/matt with ~ for display
165 ! But keep full path for navigation
166 ```
167
168 3. **Extract display names:**
169 ```fortran
170 ! From "/Users/matt", display "matt"
171 ! From "~/Documents", display "Documents"
172 ! From "/", display "/"
173 ```
174
175 4. **Create `update_breadcrumb_cache()` function:**
176 ```fortran
177 subroutine update_breadcrumb_cache(full_path)
178 character(len=*), intent(in) :: full_path
179 ! Parse path into cached_segments
180 ! Store full paths for navigation
181 ! Trigger redraw
182 end subroutine
183 ```
184
185 **Estimated time:** 3-4 hours
186
187 ---
188
189 ### Phase 3: Cairo Draw Function
190
191 **Tasks:**
192 1. **Render each segment with Pango:**
193 ```fortran
194 subroutine draw_breadcrumb(area, cr, width, height, user_data) bind(c)
195 ! For each segment:
196 ! 1. Determine color based on state:
197 ! - Root (i==1): green
198 ! - Active (i==count): bold red
199 ! - Inactive: gray
200 ! - Hovered inactive: darker gray
201 ! 2. Set Cairo color
202 ! 3. Draw text with Pango
203 ! 4. Store bounds in segment_rects(i)
204 ! 5. Draw separator " / "
205 ! 6. Update x_offset
206 end subroutine
207 ```
208
209 2. **Color logic:**
210 ```fortran
211 ! Root segment (/)
212 if (i == 1) then
213 call cairo_set_source_rgb(cr, 0.0_c_double, 0.6_c_double, 0.0_c_double) ! Green
214
215 ! Active segment (last in path)
216 else if (i == cached_segment_count) then
217 call cairo_set_source_rgb(cr, 0.8_c_double, 0.0_c_double, 0.0_c_double) ! Bold red
218 font_desc = pango_font_description_from_string("Sans Bold 11"//c_null_char)
219
220 ! Inactive segment (hovered)
221 else if (i == hovered_segment) then
222 call cairo_set_source_rgb(cr, 0.3_c_double, 0.3_c_double, 0.3_c_double) ! Darker gray
223
224 ! Inactive segment (not hovered)
225 else
226 call cairo_set_source_rgb(cr, 0.5_c_double, 0.5_c_double, 0.5_c_double) ! Gray
227 end if
228 ```
229
230 3. **Back/forward awareness integration:**
231 ```fortran
232 ! Check if segment is in backwards history
233 logical function is_backwards_segment(segment_path)
234 ! Compare with nav_history and nav_history_pos
235 ! If segment_path appears before current position, return true
236 end function
237
238 ! Apply extra dimming to backwards segments
239 if (is_backwards_segment(cached_segments(i))) then
240 call cairo_set_source_rgba(cr, 0.5_c_double, 0.5_c_double, 0.5_c_double, 0.6_c_double) ! Gray + transparency
241 end if
242 ```
243
244 **Estimated time:** 5-6 hours
245
246 ---
247
248 ### Phase 4: Mouse Interaction
249
250 **Tasks:**
251 1. **Hit-testing function:**
252 ```fortran
253 function find_segment_at_position(x, y) result(segment_index)
254 real(c_double), intent(in) :: x, y
255 integer :: segment_index
256 integer :: i
257
258 segment_index = 0
259 do i = 1, cached_segment_count
260 if (x >= segment_rects(i)%x .and. &
261 x <= segment_rects(i)%x + segment_rects(i)%width .and. &
262 y >= segment_rects(i)%y .and. &
263 y <= segment_rects(i)%y + segment_rects(i)%height) then
264 segment_index = i
265 return
266 end if
267 end do
268 end function
269 ```
270
271 2. **Motion callback (hover):**
272 ```fortran
273 subroutine on_breadcrumb_motion(controller, x, y, user_data) bind(c)
274 integer :: new_hovered
275
276 new_hovered = find_segment_at_position(x, y)
277
278 ! Only redraw if hover state CHANGED
279 if (new_hovered /= last_hovered_segment) then
280 last_hovered_segment = new_hovered
281 hovered_segment = new_hovered
282 call gtk_widget_queue_draw(breadcrumb_widget_ptr)
283 end if
284 end subroutine
285 ```
286
287 3. **Click callback (navigation):**
288 ```fortran
289 subroutine on_breadcrumb_click(gesture, n_press, x, y, user_data) bind(c)
290 integer :: clicked_segment, levels_up
291
292 clicked_segment = find_segment_at_position(x, y)
293 if (clicked_segment > 0 .and. clicked_segment < cached_segment_count) then
294 ! Navigate to clicked segment
295 levels_up = cached_segment_count - clicked_segment
296 call navigate_up(levels_up)
297
298 ! Trigger navigation callback to update history/UI
299 if (associated(nav_callback)) call nav_callback()
300
301 ! Redraw
302 call gtk_widget_queue_draw(breadcrumb_widget_ptr)
303 end if
304 end subroutine
305 ```
306
307 **Estimated time:** 3-4 hours
308
309 ---
310
311 ### Phase 5: Integration with gtk_app.f90
312
313 **Tasks:**
314 1. **Replace current breadcrumb bar:**
315 - REMOVE lines 356-364 (old breadcrumb_bar creation)
316 - REMOVE `breadcrumb_box_ptr` global variable
317 - REMOVE `breadcrumb_buttons` array
318 - REMOVE `breadcrumb_count` tracking
319
320 2. **Add new breadcrumb widget:**
321 ```fortran
322 ! In on_activate(), after toolbar creation:
323 use breadcrumb_widget, only: create_breadcrumb_widget, update_breadcrumb_cache
324
325 type(c_ptr) :: breadcrumb_area
326
327 ! Create custom breadcrumb widget
328 breadcrumb_area = create_breadcrumb_widget()
329 call gtk_widget_set_size_request(breadcrumb_area, -1_c_int, 30_c_int) ! Height: 30px
330 call gtk_box_append(main_box, breadcrumb_area)
331 ```
332
333 3. **Update breadcrumb_callback():**
334 ```fortran
335 subroutine breadcrumb_callback()
336 use breadcrumb_widget, only: update_breadcrumb_cache
337 use treemap_renderer, only: get_current_view_node
338
339 current_view => get_current_view_node()
340 if (associated(current_view) .and. allocated(current_view%path)) then
341 ! Update custom breadcrumb cache
342 call update_breadcrumb_cache(current_view%path)
343 end if
344
345 ! Existing history/status logic...
346 end subroutine
347 ```
348
349 4. **CRITICAL: Unhook old click behavior:**
350 - REMOVE `on_breadcrumb_clicked()` function (lines 1328-1356)
351 - REMOVE `sniffly_update_breadcrumbs()` function (lines 1222-1325)
352 - Navigation will now be handled by breadcrumb_widget's click callback
353
354 5. **Update meson.build:**
355 ```meson
356 sources += [
357 'src/gui/breadcrumb_widget.f90',
358 # ... existing sources
359 ]
360 ```
361
362 **Estimated time:** 2-3 hours
363
364 ---
365
366 ### Phase 6: Back/Forward Awareness (Advanced Feature)
367
368 **Tasks:**
369 1. **Expose navigation history to breadcrumb:**
370 ```fortran
371 ! In gtk_app.f90, add public getter:
372 public :: get_navigation_history, get_navigation_position
373
374 function get_navigation_history() result(history)
375 character(len=512), dimension(:), allocatable :: history
376 allocate(history(nav_history_count))
377 history = nav_history(1:nav_history_count)
378 end function
379 ```
380
381 2. **Check in breadcrumb draw function:**
382 ```fortran
383 ! In breadcrumb_widget.f90 draw function:
384 use gtk_app, only: get_navigation_history, get_navigation_position
385
386 character(len=512), dimension(:), allocatable :: history
387 integer :: history_pos, j
388 logical :: is_backwards
389
390 history = get_navigation_history()
391 history_pos = get_navigation_position()
392
393 ! For each segment, check if it was in earlier history
394 is_backwards = .false.
395 do j = 1, history_pos - 1 ! Check all history before current
396 if (index(trim(history(j)), trim(cached_segments(i))) > 0) then
397 is_backwards = .true.
398 exit
399 end if
400 end do
401
402 ! Apply dimming if backwards
403 if (is_backwards) then
404 alpha = 0.6_c_double ! More transparent
405 end if
406 ```
407
408 **Estimated time:** 2-3 hours
409
410 ---
411
412 ### Phase 7: Testing & Polish
413
414 **Tasks:**
415 1. **Test scenarios:**
416 - [ ] Navigate into nested directories - breadcrumb updates correctly
417 - [ ] Click middle segment - navigates up correct number of levels
418 - [ ] Hover over segments - color changes smoothly
419 - [ ] Click active (red) segment - no navigation (already there)
420 - [ ] Back/Forward navigation - backwards segments dim appropriately
421 - [ ] Rapid mouse movement - no excessive redraws
422 - [ ] Long paths - breadcrumb doesn't overflow window width
423
424 2. **Handle edge cases:**
425 - [ ] Root path `/` - shows only one segment
426 - [ ] Home path `~` - abbreviates correctly
427 - [ ] Very long paths - add ellipsis or scrolling?
428 - [ ] Window resize - breadcrumb reflows
429
430 3. **Performance validation:**
431 - [ ] Run profiler during hover - verify <0.1% CPU
432 - [ ] Check redraw frequency - only on state changes
433 - [ ] Test during active scans - no interference
434
435 4. **Visual polish:**
436 - [ ] Adjust colors to match GTK theme
437 - [ ] Fine-tune separator spacing
438 - [ ] Ensure alignment with toolbar
439
440 **Estimated time:** 3-4 hours
441
442 ---
443
444 ## TOTAL EFFORT ESTIMATE
445
446 - **Phase 1:** 2-3 hours (module setup)
447 - **Phase 2:** 3-4 hours (path parsing)
448 - **Phase 3:** 5-6 hours (Cairo drawing)
449 - **Phase 4:** 3-4 hours (mouse interaction)
450 - **Phase 5:** 2-3 hours (integration)
451 - **Phase 6:** 2-3 hours (back/forward awareness)
452 - **Phase 7:** 3-4 hours (testing & polish)
453
454 **Total:** 20-27 hours (~2-3 days of focused work)
455
456 ---
457
458 ## CRITICAL NOTES
459
460 ### Don't Forget:
461 1. **Unhook old breadcrumb click behavior** - Remove `on_breadcrumb_clicked()` and related button logic
462 2. **Remove old breadcrumb widgets** - Clean up `breadcrumb_box_ptr`, `breadcrumb_buttons`, etc.
463 3. **Navigation callback integration** - Breadcrumb widget needs access to `navigate_up()` from treemap_renderer
464 4. **Thread safety** - Always update cache on main thread via callbacks, never in draw function
465 5. **Redraw optimization** - Only queue redraw when hover STATE changes, not on every mouse move
466
467 ### Performance Gotchas:
468 - Cache path segments when navigation happens, not on every draw
469 - Use `last_hovered_segment` to detect state changes
470 - Pango layout creation can be expensive - consider caching layouts between redraws if profiling shows issues
471
472 ### Future Enhancements:
473 - Tooltip on hover showing full path + size stats
474 - Keyboard navigation (Tab through segments, Enter to navigate)
475 - Context menu on right-click (same as treemap)
476 - Animated transitions when segments update
477
478 ---
479
480 ## SUCCESS CRITERIA
481
482 **Vision achieved when:**
483 - [x] Breadcrumb shows path as seamless floating text (no button borders)
484 - [x] Root segment is green
485 - [x] Active (last) segment is bold red
486 - [x] Inactive segments are gray
487 - [x] Hovered segments darken
488 - [x] Clicking any segment navigates to that level
489 - [x] Back/forward navigation dims previously-visited segments
490 - [x] Performance <0.1% CPU overhead
491 - [x] No state corruption during scans
492 - [x] Looks visually seamless and polished