From 6b0d130138dfb11218bf076bf7050ef8cd37c3fd Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Wed, 9 Oct 2024 14:35:26 -0700 Subject: [PATCH] Update list offset when selection overflows --- README.md | 1 + game.go | 23 +++++++++++++++++++++++ list.go | 33 +++++++++++++++++++++++++++------ spite.go => sprite.go | 0 4 files changed, 51 insertions(+), 6 deletions(-) rename spite.go => sprite.go (100%) diff --git a/README.md b/README.md index bfc719b..90d793a 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ - Grid: Highly customizable cell-based layout. Each widget added to the Grid may span multiple cells. - Input: Text input widget. The Input widget is simply a Text widget that also accepts user input. - List: List of widgets as selectable items. + - Sprite: Resizable image. - Text: Text display widget. - Window: Widget paging mechanism. Only one widget added to a window is displayed at a time. diff --git a/game.go b/game.go index 49be244..a210655 100644 --- a/game.go +++ b/game.go @@ -463,3 +463,26 @@ func rectAtOrigin(r image.Rectangle) image.Rectangle { r.Min.X, r.Min.Y = 0, 0 return r } + +func _clamp(x int, y int, boundary image.Rectangle) (int, int) { + if x < boundary.Min.X { + x = boundary.Min.X + } else if y > boundary.Max.X { + x = boundary.Max.X + } + if y < boundary.Min.Y { + y = boundary.Min.Y + } else if y > boundary.Max.Y { + y = boundary.Max.Y + } + return x, y +} + +func clampRect(r image.Rectangle, boundary image.Rectangle) image.Rectangle { + r.Min.X, r.Min.Y = _clamp(r.Min.X, r.Min.Y, boundary) + r.Max.X, r.Max.Y = _clamp(r.Max.X, r.Max.Y, boundary) + if r.Min.X == r.Max.X || r.Min.Y == r.Max.Y { + return image.Rectangle{} + } + return r +} diff --git a/list.go b/list.go index f1b7971..a4e467f 100644 --- a/list.go +++ b/list.go @@ -106,6 +106,7 @@ func (l *List) SetRect(r image.Rectangle) { r.Max.X -= l.scrollWidth } l.grid.SetRect(r) + l.selectionUpdated() l.recreateGrid = true } @@ -220,6 +221,7 @@ func (l *List) SetSelectedItem(x int, y int) { defer l.Unlock() l.selectedX, l.selectedY = x, y + l.selectionUpdated() } // SetScrollBarWidth sets the width of the scroll bar. @@ -337,6 +339,22 @@ func (l *List) clampOffset(offset int) int { return offset } +func (l *List) selectionUpdated() { + if l.selectedY < l.offset { + l.offset = l.selectedY + l.recreateGrid = true + return + } + visible := l.grid.rect.Dy()/l.itemHeight - 1 + if visible < 1 { + visible = 1 + } + if l.selectedY > l.offset+visible { + l.offset = l.selectedY - visible + l.recreateGrid = true + } +} + // Cursor returns the cursor shape shown when a mouse cursor hovers over the // widget, or -1 to let widgets beneath determine the cursor shape. func (l *List) Cursor() ebiten.CursorShapeType { @@ -367,6 +385,7 @@ func (l *List) HandleKeyboard(key ebiten.Key, r rune) (handled bool, err error) y = l.selectedY + y if y >= 0 && y <= l.maxY { l.selectedY = y + l.selectionUpdated() } } for _, leftKey := range Bindings.MoveLeftKeyboard { @@ -450,7 +469,7 @@ func (l *List) HandleMouse(cursor image.Point, pressed bool, clicked bool) (hand if !clicked || (cursor.X == 0 && cursor.Y == 0) { return true, nil } - selected := (cursor.Y - l.grid.rect.Min.Y) / l.itemHeight + selected := l.offset + (cursor.Y-l.grid.rect.Min.Y)/l.itemHeight if selected >= 0 && selected <= l.maxY { lastSelected := l.selectedY l.selectedY = selected @@ -466,6 +485,8 @@ func (l *List) HandleMouse(cursor image.Point, pressed bool, clicked bool) (hand } } + l.selectionUpdated() + if selected == lastSelected && time.Since(l.selectedTime) <= Bindings.DoubleClickThreshold { confirmedFunc := l.confirmedFunc if confirmedFunc != nil { @@ -488,7 +509,7 @@ func (l *List) Draw(screen *ebiten.Image) error { defer l.Unlock() if l.recreateGrid { - maxY := l.grid.rect.Dy() / l.itemHeight + maxY := l.grid.rect.Dy()/l.itemHeight + 1 l.offset = l.clampOffset(l.offset) l.grid.Clear() rowSizes := make([]int, l.maxY+1) @@ -529,10 +550,10 @@ func (l *List) Draw(screen *ebiten.Image) error { // Highlight selection. drawHighlight := l.selectionMode != SelectNone && l.selectedY >= 0 if drawHighlight { - { - x, y := l.grid.rect.Min.X, l.grid.rect.Min.Y+l.selectedY*l.itemHeight - w, h := l.grid.rect.Dx(), l.itemHeight - r := image.Rect(x, y, x+w, y+h) + x, y := l.grid.rect.Min.X, l.grid.rect.Min.Y+(l.selectedY-l.offset)*l.itemHeight + w, h := l.grid.rect.Dx(), l.itemHeight + r := clampRect(image.Rect(x, y, x+w, y+h), l.rect) + if r.Dx() > 0 && r.Dy() > 0 { screen.SubImage(r).(*ebiten.Image).Fill(l.highlightColor) } } diff --git a/spite.go b/sprite.go similarity index 100% rename from spite.go rename to sprite.go