Skip to content

Commit 06bc04c

Browse files
committed
### 1. Eliminating Drag Lag (Rate-Limiting & High-Level Capture)
• Moved the mouse capture handler from windowLayout up to the top-level mainPageLayout . Because this spans the entire screen, fast mouse movements will never escape the captured region. • Added a 30ms rate-limiter (approx 33 FPS) that suppresses redundant intermediate layout calculations and screen redraws. • Integrated a trailing timer ( dragTimer via time.AfterFunc calling QueueUpdateDraw ) which guarantees that the final drag position is always visually captured and exact even if it fell within a rate-limited window. • Normal mouse events return action, nil (consumed but no redraw) during rate-limiting periods, drastically reducing CPU/network terminal redraw load. ### 2. Precise Boundary Highlighting (Adjacent Border Lines Only) • Reverted full panel border color changes to prevent the outer/non-adjacent sides of the windows from turning orange. • Utilized tview 's SetAfterDrawFunc hook to draw highlights. In this hook, we query the character cells directly from screen. GetContent(x, y) at the active boundary column/row and dynamically override their foreground color style to theme.Primary (accent orange) while leaving the characters, corners, junction lines, and background styles completely intact.
1 parent b06eaf5 commit 06bc04c

1 file changed

Lines changed: 100 additions & 55 deletions

File tree

internal/ui/main_page.go

Lines changed: 100 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ui
22

33
import (
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

5356
func 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

Comments
 (0)