Skip to content

Commit b06eaf5

Browse files
committed
1. Added custom border-highlight handlers:
• Added SetBorderColor(tcell.Color) to FileBrowserComponent in file_browser.go. • Added SetBorderColor(tcell.Color) to DatasetInfoComponent in dataset_info.go. • Added SetBorderColor(tcell.Color) to SnapshotBrowserComponent in snapshot_browser.go. • Added SetBorderColor(tcell.Color) wrapper inside LoadingContainer in data_loader.go. 2. Implemented coordinate tracking and resize interaction in main_page.go: • Configured SetMouseCapture on the parent windowLayout . • Used absolute screen coordinates from event.Position() and checked them against the widget rectangles returned by GetRect() . • Enabled interactive boundary hover detection: highlighting the border of adjacent windows with theme.Primary (accent orange) when the mouse is positioned on the boundaries. • Handled MouseLeftDown click drags to dynamically scale flex row/column proportions using tview.Flex.ResizeItem , clamping panels to safe minimum boundaries (minimum 10 columns for browser width, 4 rows for dataset info height, 5 rows for snapshot browser height). • Released state tracking on MouseLeftUp or when buttons are released.
1 parent 07f23d3 commit b06eaf5

5 files changed

Lines changed: 188 additions & 0 deletions

File tree

internal/ui/dataset_info/dataset_info.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,13 @@ func (datasetInfo *DatasetInfoComponent) GetLayout() *uiutil.LoadingContainer {
212212
return datasetInfo.container
213213
}
214214

215+
func (datasetInfo *DatasetInfoComponent) SetBorderColor(color tcell.Color) {
216+
datasetInfo.textView.SetBorderColor(color)
217+
if datasetInfo.container != nil {
218+
datasetInfo.container.SetBorderColor(color)
219+
}
220+
}
221+
215222
func (datasetInfo *DatasetInfoComponent) CreateSnapshot(name string) error {
216223
if datasetInfo.dataset == nil {
217224
return fmt.Errorf("no dataset selected")

internal/ui/file_browser/file_browser.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,12 @@ func (fileBrowser *FileBrowserComponent) GetLayout() tview.Primitive {
888888
return fileBrowser.layout
889889
}
890890

891+
func (fileBrowser *FileBrowserComponent) SetBorderColor(color tcell.Color) {
892+
if flex, ok := fileBrowser.tableContainer.GetLayout().(*tview.Flex); ok {
893+
flex.SetBorderColor(color)
894+
}
895+
}
896+
891897
func (fileBrowser *FileBrowserComponent) SelectHeader() {
892898
fileBrowser.tableContainer.SelectHeader()
893899
}

internal/ui/main_page.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,30 @@ import (
88
"zfs-file-history/internal/ui/shortcut_helper"
99
"zfs-file-history/internal/ui/snapshot_browser"
1010
"zfs-file-history/internal/ui/status_message"
11+
"zfs-file-history/internal/ui/theme"
1112
uiutil "zfs-file-history/internal/ui/util"
1213
"zfs-file-history/internal/zfs"
1314

1415
"github.com/gdamore/tcell/v2"
1516
"github.com/rivo/tview"
1617
)
1718

19+
type dragType int
20+
21+
const (
22+
dragNone dragType = iota
23+
dragVertical
24+
dragHorizontal
25+
)
26+
27+
type boundaryType int
28+
29+
const (
30+
boundaryNone boundaryType = iota
31+
boundaryVertical
32+
boundaryHorizontal
33+
)
34+
1835
type MainPage struct {
1936
application *tview.Application
2037
header *ApplicationHeaderComponent
@@ -23,8 +40,14 @@ type MainPage struct {
2340
datasetInfo *dataset_info.DatasetInfoComponent
2441
snapshotBrowser *snapshot_browser.SnapshotBrowserComponent
2542
layout *tview.Flex
43+
windowLayout *tview.Flex
44+
infoLayout *tview.Flex
2645

2746
wasInitialized bool
47+
48+
isDragging bool
49+
dragType dragType
50+
hoveredBoundary boundaryType
2851
}
2952

3053
func NewMainPage(application *tview.Application, path string) *MainPage {
@@ -143,6 +166,105 @@ func (mainPage *MainPage) createLayout() *tview.Flex {
143166

144167
mainPageLayout.AddItem(windowLayout, 0, 1, true)
145168

169+
mainPage.windowLayout = windowLayout
170+
mainPage.infoLayout = infoLayout
171+
172+
windowLayout.SetMouseCapture(func(action tview.MouseAction, event *tcell.EventMouse) (tview.MouseAction, *tcell.EventMouse) {
173+
mouseX, mouseY := event.Position()
174+
buttons := event.Buttons()
175+
176+
diX, diY, diW, diH := mainPage.datasetInfo.GetLayout().GetRect()
177+
_, sbY, _, sbH := mainPage.snapshotBrowser.GetLayout().GetRect()
178+
winX, _, winW, _ := windowLayout.GetRect()
179+
180+
// 1. If currently dragging
181+
if mainPage.isDragging {
182+
if buttons == tcell.ButtonNone || action == tview.MouseLeftUp {
183+
mainPage.isDragging = false
184+
mainPage.dragType = dragNone
185+
mainPage.hoveredBoundary = boundaryNone
186+
mainPage.updateBorderHighlights()
187+
return tview.MouseConsumed, nil
188+
}
189+
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+
}
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
214+
}
215+
newBottomHeight := infoH - newTopHeight
216+
217+
infoLayout.ResizeItem(mainPage.datasetInfo.GetLayout(), 0, newTopHeight)
218+
infoLayout.ResizeItem(mainPage.snapshotBrowser.GetLayout(), 0, newBottomHeight)
219+
}
220+
221+
return tview.MouseConsumed, nil
222+
}
223+
224+
// 2. Not dragging: detect hover boundaries
225+
isOnVertical := false
226+
if mouseY >= diY && mouseY < sbY+sbH {
227+
if mouseX == diX || mouseX == diX-1 {
228+
isOnVertical = true
229+
}
230+
}
231+
232+
isOnHorizontal := false
233+
if mouseX >= diX && mouseX < diX+diW {
234+
if mouseY == sbY || mouseY == sbY-1 {
235+
isOnHorizontal = true
236+
}
237+
}
238+
239+
newHover := boundaryNone
240+
if isOnHorizontal {
241+
newHover = boundaryHorizontal
242+
} else if isOnVertical {
243+
newHover = boundaryVertical
244+
}
245+
246+
if newHover != mainPage.hoveredBoundary {
247+
mainPage.hoveredBoundary = newHover
248+
mainPage.updateBorderHighlights()
249+
return tview.MouseConsumed, nil
250+
}
251+
252+
// 3. Initiate dragging
253+
if buttons&tcell.Button1 != 0 && action == tview.MouseLeftDown {
254+
if isOnHorizontal {
255+
mainPage.isDragging = true
256+
mainPage.dragType = dragHorizontal
257+
return tview.MouseConsumed, nil
258+
} else if isOnVertical {
259+
mainPage.isDragging = true
260+
mainPage.dragType = dragVertical
261+
return tview.MouseConsumed, nil
262+
}
263+
}
264+
265+
return action, event
266+
})
267+
146268
mainPage.header = header
147269

148270
shortcutMap := shortcut_helper.NewShortcutMap(mainPage.application)
@@ -218,3 +340,33 @@ func (mainPage *MainPage) updateShortcutMap(component FocusableUiComponent) {
218340
mainPage.clearShortcutMap()
219341
}
220342
}
343+
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+
}
352+
}
353+
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)
371+
}
372+
}

internal/ui/snapshot_browser/snapshot_browser.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,15 @@ func (snapshotBrowser *SnapshotBrowserComponent) GetLayout() *uiutil.LoadingCont
202202
return snapshotBrowser.container
203203
}
204204

205+
func (snapshotBrowser *SnapshotBrowserComponent) SetBorderColor(color tcell.Color) {
206+
if flex, ok := snapshotBrowser.tableContainer.GetLayout().(*tview.Flex); ok {
207+
flex.SetBorderColor(color)
208+
}
209+
if snapshotBrowser.container != nil {
210+
snapshotBrowser.container.SetBorderColor(color)
211+
}
212+
}
213+
205214
func (snapshotBrowser *SnapshotBrowserComponent) SetPath(path string, force bool) {
206215
if !force && snapshotBrowser.path == path {
207216
return

internal/ui/util/data_loader.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"sync"
66
"sync/atomic"
77

8+
"github.com/gdamore/tcell/v2"
89
"github.com/rivo/tview"
910
)
1011

@@ -112,3 +113,16 @@ func (c *LoadingContainer) SetIsLoading(isLoading bool) {
112113
func (c *LoadingContainer) GetFrontPage() (string, tview.Primitive) {
113114
return c.Pages.GetFrontPage()
114115
}
116+
117+
func (c *LoadingContainer) SetBorderColor(color tcell.Color) {
118+
if c.loadingView != nil {
119+
c.loadingView.SetBorderColor(color)
120+
}
121+
if c.content != nil {
122+
if box, ok := c.content.(interface{ SetBorderColor(tcell.Color) *tview.Box }); ok {
123+
box.SetBorderColor(color)
124+
} else if flex, ok := c.content.(*tview.Flex); ok {
125+
flex.SetBorderColor(color)
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)