@@ -2,6 +2,7 @@ package ui
22
33import (
44 "fmt"
5+ "time"
56 "zfs-file-history/internal/logging"
67 "zfs-file-history/internal/ui/dataset_info"
78 "zfs-file-history/internal/ui/file_browser"
@@ -48,6 +49,8 @@ type MainPage struct {
4849 isDragging bool
4950 dragType dragType
5051 hoveredBoundary boundaryType
52+ lastDragRedraw time.Time
53+ dragTimer * time.Timer
5154}
5255
5356func NewMainPage (application * tview.Application , path string ) * MainPage {
@@ -169,7 +172,8 @@ func (mainPage *MainPage) createLayout() *tview.Flex {
169172 mainPage .windowLayout = windowLayout
170173 mainPage .infoLayout = infoLayout
171174
172- windowLayout .SetMouseCapture (func (action tview.MouseAction , event * tcell.EventMouse ) (tview.MouseAction , * tcell.EventMouse ) {
175+ // Set mouse capture on the top-level layout to capture drags anywhere on the screen
176+ mainPageLayout .SetMouseCapture (func (action tview.MouseAction , event * tcell.EventMouse ) (tview.MouseAction , * tcell.EventMouse ) {
173177 mouseX , mouseY := event .Position ()
174178 buttons := event .Buttons ()
175179
@@ -183,42 +187,36 @@ func (mainPage *MainPage) createLayout() *tview.Flex {
183187 mainPage .isDragging = false
184188 mainPage .dragType = dragNone
185189 mainPage .hoveredBoundary = boundaryNone
190+ if mainPage .dragTimer != nil {
191+ mainPage .dragTimer .Stop ()
192+ mainPage .dragTimer = nil
193+ }
186194 mainPage .updateBorderHighlights ()
187195 return tview .MouseConsumed , nil
188196 }
189197
190- if mainPage .dragType == dragVertical {
191- newLeftWidth := mouseX - winX
192- minWidth := 10
193- if newLeftWidth < minWidth {
194- newLeftWidth = minWidth
195- }
196- if newLeftWidth > winW - minWidth {
197- newLeftWidth = winW - minWidth
198+ // Rate limit updates to 30ms to prevent redraw flooding/input lag
199+ now := time .Now ()
200+ if now .Sub (mainPage .lastDragRedraw ) > 30 * time .Millisecond {
201+ mainPage .lastDragRedraw = now
202+ if mainPage .dragTimer != nil {
203+ mainPage .dragTimer .Stop ()
204+ mainPage .dragTimer = nil
198205 }
199- newRightWidth := winW - newLeftWidth
200-
201- windowLayout .ResizeItem (mainPage .fileBrowser .GetLayout (), 0 , newLeftWidth )
202- windowLayout .ResizeItem (infoLayout , 0 , newRightWidth )
203- } else if mainPage .dragType == dragHorizontal {
204- infoH := diH + sbH
205- infoY := diY
206- newTopHeight := mouseY - infoY
207- minTopHeight := 4
208- minBottomHeight := 5
209- if newTopHeight < minTopHeight {
210- newTopHeight = minTopHeight
211- }
212- if newTopHeight > infoH - minBottomHeight {
213- newTopHeight = infoH - minBottomHeight
206+ mainPage .applyResize (mouseX , mouseY , winX , winW , diY , diH , sbY , sbH )
207+ return tview .MouseConsumed , nil
208+ } else {
209+ // Schedule a trailing redraw for the final drag position
210+ if mainPage .dragTimer != nil {
211+ mainPage .dragTimer .Stop ()
214212 }
215- newBottomHeight := infoH - newTopHeight
216-
217- infoLayout .ResizeItem (mainPage .datasetInfo .GetLayout (), 0 , newTopHeight )
218- infoLayout .ResizeItem (mainPage .snapshotBrowser .GetLayout (), 0 , newBottomHeight )
213+ mainPage .dragTimer = time .AfterFunc (30 * time .Millisecond , func () {
214+ mainPage .application .QueueUpdateDraw (func () {
215+ mainPage .applyResize (mouseX , mouseY , winX , winW , diY , diH , sbY , sbH )
216+ })
217+ })
218+ return action , nil // consume event for children but do not trigger immediate screen redraw
219219 }
220-
221- return tview .MouseConsumed , nil
222220 }
223221
224222 // 2. Not dragging: detect hover boundaries
@@ -254,17 +252,57 @@ func (mainPage *MainPage) createLayout() *tview.Flex {
254252 if isOnHorizontal {
255253 mainPage .isDragging = true
256254 mainPage .dragType = dragHorizontal
255+ mainPage .lastDragRedraw = time .Now ()
257256 return tview .MouseConsumed , nil
258257 } else if isOnVertical {
259258 mainPage .isDragging = true
260259 mainPage .dragType = dragVertical
260+ mainPage .lastDragRedraw = time .Now ()
261261 return tview .MouseConsumed , nil
262262 }
263263 }
264264
265265 return action , event
266266 })
267267
268+ // Configure drawing of highlighted adjacent borders after the screen draws
269+ mainPage .application .SetAfterDrawFunc (func (screen tcell.Screen ) {
270+ // Highlight vertical boundary adjacent line segment
271+ if mainPage .hoveredBoundary == boundaryVertical || (mainPage .isDragging && mainPage .dragType == dragVertical ) {
272+ _ , diY , _ , _ := mainPage .datasetInfo .GetLayout ().GetRect ()
273+ diX , _ , diW , _ := mainPage .datasetInfo .GetLayout ().GetRect ()
274+ _ , sbY , _ , sbH := mainPage .snapshotBrowser .GetLayout ().GetRect ()
275+
276+ if diW > 0 && sbH > 0 {
277+ highlightColor := theme .Primary
278+ for y := diY ; y < sbY + sbH ; y ++ {
279+ for _ , x := range []int {diX - 1 , diX } {
280+ primary , combining , style , _ := screen .GetContent (x , y )
281+ newStyle := style .Foreground (highlightColor )
282+ screen .SetContent (x , y , primary , combining , newStyle )
283+ }
284+ }
285+ }
286+ }
287+
288+ // Highlight horizontal boundary adjacent line segment
289+ if mainPage .hoveredBoundary == boundaryHorizontal || (mainPage .isDragging && mainPage .dragType == dragHorizontal ) {
290+ diX , _ , diW , _ := mainPage .datasetInfo .GetLayout ().GetRect ()
291+ _ , sbY , _ , sbH := mainPage .snapshotBrowser .GetLayout ().GetRect ()
292+
293+ if diW > 0 && sbH > 0 {
294+ highlightColor := theme .Primary
295+ for x := diX ; x < diX + diW ; x ++ {
296+ for _ , y := range []int {sbY - 1 , sbY } {
297+ primary , combining , style , _ := screen .GetContent (x , y )
298+ newStyle := style .Foreground (highlightColor )
299+ screen .SetContent (x , y , primary , combining , newStyle )
300+ }
301+ }
302+ }
303+ }
304+ })
305+
268306 mainPage .header = header
269307
270308 shortcutMap := shortcut_helper .NewShortcutMap (mainPage .application )
@@ -341,32 +379,39 @@ func (mainPage *MainPage) updateShortcutMap(component FocusableUiComponent) {
341379 }
342380}
343381
344- func (mainPage * MainPage ) setPanelBorderColor (panel FocusableUiComponent , color tcell.Color ) {
345- if fb , ok := panel .(* file_browser.FileBrowserComponent ); ok {
346- fb .SetBorderColor (color )
347- } else if di , ok := panel .(* dataset_info.DatasetInfoComponent ); ok {
348- di .SetBorderColor (color )
349- } else if sb , ok := panel .(* snapshot_browser.SnapshotBrowserComponent ); ok {
350- sb .SetBorderColor (color )
351- }
382+ func (mainPage * MainPage ) updateBorderHighlights () {
383+ // Redraw logic is handled by SetAfterDrawFunc based on the hoveredBoundary/isDragging states.
352384}
353385
354- func (mainPage * MainPage ) updateBorderHighlights () {
355- defaultColor := theme .Colors .Layout .Border
356- highlightColor := theme .Primary
357-
358- switch mainPage .hoveredBoundary {
359- case boundaryVertical :
360- mainPage .setPanelBorderColor (mainPage .fileBrowser , highlightColor )
361- mainPage .setPanelBorderColor (mainPage .datasetInfo , highlightColor )
362- mainPage .setPanelBorderColor (mainPage .snapshotBrowser , highlightColor )
363- case boundaryHorizontal :
364- mainPage .setPanelBorderColor (mainPage .fileBrowser , defaultColor )
365- mainPage .setPanelBorderColor (mainPage .datasetInfo , highlightColor )
366- mainPage .setPanelBorderColor (mainPage .snapshotBrowser , highlightColor )
367- case boundaryNone :
368- mainPage .setPanelBorderColor (mainPage .fileBrowser , defaultColor )
369- mainPage .setPanelBorderColor (mainPage .datasetInfo , defaultColor )
370- mainPage .setPanelBorderColor (mainPage .snapshotBrowser , defaultColor )
386+ func (mainPage * MainPage ) applyResize (mouseX , mouseY , winX , winW , diY , diH , sbY , sbH int ) {
387+ if mainPage .dragType == dragVertical {
388+ newLeftWidth := mouseX - winX
389+ minWidth := 10
390+ if newLeftWidth < minWidth {
391+ newLeftWidth = minWidth
392+ }
393+ if newLeftWidth > winW - minWidth {
394+ newLeftWidth = winW - minWidth
395+ }
396+ newRightWidth := winW - newLeftWidth
397+
398+ mainPage .windowLayout .ResizeItem (mainPage .fileBrowser .GetLayout (), 0 , newLeftWidth )
399+ mainPage .windowLayout .ResizeItem (mainPage .infoLayout , 0 , newRightWidth )
400+ } else if mainPage .dragType == dragHorizontal {
401+ infoH := diH + sbH
402+ infoY := diY
403+ newTopHeight := mouseY - infoY
404+ minTopHeight := 4
405+ minBottomHeight := 5
406+ if newTopHeight < minTopHeight {
407+ newTopHeight = minTopHeight
408+ }
409+ if newTopHeight > infoH - minBottomHeight {
410+ newTopHeight = infoH - minBottomHeight
411+ }
412+ newBottomHeight := infoH - newTopHeight
413+
414+ mainPage .infoLayout .ResizeItem (mainPage .datasetInfo .GetLayout (), 0 , newTopHeight )
415+ mainPage .infoLayout .ResizeItem (mainPage .snapshotBrowser .GetLayout (), 0 , newBottomHeight )
371416 }
372417}
0 commit comments