package browser import ( "pad/internal/ui" "path/filepath" ) // HandleScroll updates the browser scroll index by the given delta (in entries). // Clamps to valid bounds and triggers prefetch/eviction as needed. func HandleScroll(s *BrowserState, delta int) { deltaPx := int(float64(delta) * s.EntryHeight) HandlePixelScroll(s, deltaPx) } // HandlePixelScroll updates the browser scroll offset by the given delta (in pixels). // Clamps to valid bounds and triggers eviction as needed. func HandlePixelScroll(s *BrowserState, delta int) { s.ScrollOffset += float64(delta) // Clamp to valid bounds clampScrollOffset(s) // Trigger eviction of distant pages s.EvictPages() } // clampScrollOffset ensures the scroll offset stays within valid bounds. func clampScrollOffset(s *BrowserState) { if s.ScrollOffset < 0 { s.ScrollOffset = 0 } // Use search results count if active, otherwise use TotalEntries entryCount := s.TotalEntries if s.Query != "" { entryCount = len(s.SearchResults) } // Calculate maximum scroll offset maxScroll := 0.0 if entryCount > s.VisibleCount { maxScroll = float64(entryCount-s.VisibleCount) * s.EntryHeight } if s.ScrollOffset > maxScroll { s.ScrollOffset = maxScroll } } // resolveTapIndex maps a ListView row index to the actual sorted entry index. // When search is active, the row index is into SearchResults, so we must // look through SearchResults to find the real entry index. func resolveTapIndex(s *BrowserState, index int) int { if s.Query != "" && len(s.SearchResults) > 0 { if index < 0 || index >= len(s.SearchResults) { return -1 } return s.SearchResults[index] } return index } // HandleBrowserTap processes a tap on the ListView at the given index. func HandleBrowserTap(bm *BrowserManager, s *BrowserState, index int) { // Resolve the ListView row index to the actual sorted entry index. // When search is active, the row is an index into SearchResults. entryIndex := resolveTapIndex(s, index) if entryIndex < 0 || entryIndex >= s.TotalEntries { return } entry, ok := getEntryByIndex(s, entryIndex) if !ok { return // Page not loaded } if entry.IsDir { // Navigate into directory or up var newPath string if entry.Name == ".." { // If we are at the root (".") stay there if s.CurrentPath == "." || s.CurrentPath == "" { newPath = "." } else { // Get parent directory parent := filepath.Dir(s.CurrentPath) if parent == "." || parent == "/" { newPath = "." } else { newPath = parent } } } else { // Join child entry name if s.CurrentPath == "." { newPath = entry.Name } else { newPath = filepath.Join(s.CurrentPath, entry.Name) } } bm.NavigateTo(newPath) } else { // Open file s.SelectedIndex = entryIndex if ui.OpenFile != nil { ui.OpenFile(entry.Path) } } }