aboutsummaryrefslogtreecommitdiff
path: root/go/ui
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-02-14 16:05:06 +0100
committerFlorian Fischer <florian.fischer@muhq.space>2025-01-27 16:43:49 +0100
commitb85c1a85e3f9a53321495e4d015e7620b29f9929 (patch)
tree314469f8bcbe61cea3c055d01e2c961b0e988708 /go/ui
parente049500b213c85c9d1556e01717226a191d2dce8 (diff)
downloadmuhqs-game-b85c1a85e3f9a53321495e4d015e7620b29f9929.tar.gz
muhqs-game-b85c1a85e3f9a53321495e4d015e7620b29f9929.zip
add sealed activity to select a starting deck
Diffstat (limited to 'go/ui')
-rw-r--r--go/ui/buffer.go2
-rw-r--r--go/ui/button.go4
-rw-r--r--go/ui/cardGrid.go183
-rw-r--r--go/ui/clickable.go40
-rw-r--r--go/ui/collection.go70
-rw-r--r--go/ui/colors.go4
-rw-r--r--go/ui/hand.go100
-rw-r--r--go/ui/stateBar.go2
-rw-r--r--go/ui/textBox.go10
-rw-r--r--go/ui/textInput.go1
10 files changed, 317 insertions, 99 deletions
diff --git a/go/ui/buffer.go b/go/ui/buffer.go
index e95102fe..201273b8 100644
--- a/go/ui/buffer.go
+++ b/go/ui/buffer.go
@@ -10,7 +10,7 @@ import (
)
var (
- BUFFER_BACKGROUND = gray
+ BUFFER_BACKGROUND = Gray
BUFFER_FOREGROUND = color.White
)
diff --git a/go/ui/button.go b/go/ui/button.go
index 0fd26936..78ab459f 100644
--- a/go/ui/button.go
+++ b/go/ui/button.go
@@ -12,7 +12,7 @@ import (
type Button interface {
Widget
- Click(x, y int)
+ Clickable
}
type SimpleButton struct {
@@ -24,7 +24,7 @@ type SimpleButton struct {
}
func NewSimpleButton(x, y, w, h int, label string, onClick func(b *SimpleButton)) *SimpleButton {
- sb := &SimpleButton{NewWidgetBase(x, y, w, h), label, onClick, gray, color.White}
+ sb := &SimpleButton{NewWidgetBase(x, y, w, h), label, onClick, Gray, color.White}
sb.renderImpl = func() *ebiten.Image { return sb.render() }
return sb
}
diff --git a/go/ui/cardGrid.go b/go/ui/cardGrid.go
new file mode 100644
index 00000000..9ed92aa4
--- /dev/null
+++ b/go/ui/cardGrid.go
@@ -0,0 +1,183 @@
+package ui
+
+import (
+ "log"
+ "math"
+
+ "github.com/hajimehoshi/ebiten/v2"
+ "muhq.space/muhqs-game/go/assets"
+ "muhq.space/muhqs-game/go/game"
+
+ "golang.org/x/exp/slices"
+)
+
+type CardGrid struct {
+ WidgetBase
+ cards game.PileOfCards
+ columns, rows int
+ scale float64
+ cardWidth, cardHeight float64
+ xOffset, yOffset int
+ highlights []*game.Card
+ grid *ebiten.Image
+ gridCards int
+}
+
+func NewCardGrid(x, y, width, height int, scale float64, cards game.PileOfCards) *CardGrid {
+ w := &CardGrid{
+ WidgetBase: NewWidgetBase(x, y, width, height),
+ cards: cards,
+ scale: scale,
+ }
+ w.renderImpl = func() *ebiten.Image { return w.render() }
+ return w
+}
+
+func (w *CardGrid) Columns(columns int) *CardGrid {
+ if w.rows != 0 {
+ log.Fatalf("CardGrid can not have fixed columns and rows")
+ }
+ w.columns = columns
+ return w
+}
+
+func (w *CardGrid) Rows(rows int) *CardGrid {
+ if w.columns != 0 {
+ log.Fatalf("CardGrid can not have fixed columns and rows")
+ }
+ w.rows = rows
+ return w
+}
+
+func (w *CardGrid) render() *ebiten.Image {
+ img := ebiten.NewImage(w.Width, w.Height)
+
+ cards := w.cards.Cards()
+ nCards := len(cards)
+
+ if nCards == 0 {
+ return img
+ }
+
+ if nCards != w.gridCards {
+ if w.cardWidth == 0 && w.cardHeight == 0 {
+ cardImg := assets.GetCard(w.cards.Cards()[0].Path(), "en")
+ b := cardImg.Bounds()
+
+ // Fit the row height to the widgets height
+ if w.rows == 1 && w.scale == -1 {
+ w.scale = float64(w.Height) / float64(b.Dy())
+ }
+
+ w.cardWidth = float64(b.Dx()) * w.scale
+ w.cardHeight = float64(b.Dy()) * w.scale
+ }
+
+ var width, height int
+ if w.rows != 0 {
+ width = int(math.Ceil(float64(nCards)/float64(w.rows)) * w.cardWidth)
+ height = int(float64(w.rows) * w.cardHeight)
+ } else if w.columns != 0 {
+ width = int(float64(w.columns) * w.cardWidth)
+ height = int(math.Ceil(float64(nCards)/float64(w.columns)) * w.cardHeight)
+ } else {
+ log.Fatalf("CardGrid must have have fixed columns or rows")
+ }
+ w.gridCards = nCards
+ w.grid = ebiten.NewImage(width, height)
+
+ xOffset, yOffset := 0.0, 0.0
+ for i, card := range cards {
+ cardImg := assets.GetCard(card.Path(), "en")
+ if cardImg == nil {
+ log.Fatalf("No image found for %s", card.Path())
+ }
+
+ op := &ebiten.DrawImageOptions{}
+ op.GeoM.Scale(w.scale, w.scale)
+ op.GeoM.Translate(xOffset, yOffset)
+
+ if slices.Contains(w.highlights, card) {
+ op.ColorM.Scale(1, 0, 0, 0.5)
+ }
+
+ w.grid.DrawImage(cardImg, op)
+
+ columns := w.columns
+ if columns == 0 {
+ columns = nCards / w.rows
+ }
+
+ if i != 0 && i%columns == columns-1 {
+ xOffset = 0.0
+ yOffset += w.cardHeight
+ } else {
+ xOffset += w.cardWidth
+ }
+ }
+ }
+
+ op := &ebiten.DrawImageOptions{}
+ op.GeoM.Translate(float64(w.xOffset), float64(w.yOffset))
+ img.DrawImage(w.grid, op)
+ return img
+}
+
+func (w *CardGrid) FindObjectAt(_x, _y int) interface{} {
+ if !w.Contains(_x, _y) || w.grid == nil {
+ return nil
+ }
+
+ x, y := _x-w.X-w.xOffset, _y-w.Y-w.yOffset
+
+ gridBounds := w.grid.Bounds()
+ if x > gridBounds.Dx() || y > gridBounds.Dy() {
+ return nil
+ }
+
+ column := int(float64(x) / w.cardWidth)
+ row := int(float64(y) / w.cardHeight)
+ var i int
+ if w.rows != 0 {
+ i = row*w.cards.Size()/w.rows + column
+ } else {
+ i = row*w.columns + column
+ }
+
+ if i >= w.cards.Size() {
+ return nil
+ }
+
+ return w.cards.Cards()[i]
+}
+
+func (w *CardGrid) setHighlights(cards []*game.Card) {
+ w.highlights = cards
+ w.ForceRedraw()
+}
+
+func (w *CardGrid) HighlightCard(card *game.Card) {
+ w.setHighlights([]*game.Card{card})
+}
+
+func (w *CardGrid) AddHighlightCard(card *game.Card) {
+ w.setHighlights(append(w.highlights, card))
+}
+
+func (w *CardGrid) ClearHighlights() {
+ w.setHighlights([]*game.Card{})
+}
+
+func (w *CardGrid) Scroll(x, y int) {
+ w.xOffset += x * 10
+ if w.xOffset > 0 {
+ w.xOffset = 0
+ }
+
+ w.yOffset += y * 10
+ if w.yOffset > 0 {
+ w.yOffset = 0
+ }
+
+ w.ForceRedraw()
+}
diff --git a/go/ui/clickable.go b/go/ui/clickable.go
new file mode 100644
index 00000000..761b7534
--- /dev/null
+++ b/go/ui/clickable.go
@@ -0,0 +1,40 @@
+package ui
+
+import (
+ "github.com/hajimehoshi/ebiten/v2"
+)
+
+type Clickable interface {
+ Click(x, y int)
+}
+
+type ClickWrapper struct {
+ w Widget
+ onClick func(x, y int)
+}
+
+func NewClickWrapper(w Widget, onClick func(x, y int)) *ClickWrapper {
+ return &ClickWrapper{w, onClick}
+}
+
+func (w *ClickWrapper) Draw(screen *ebiten.Image) {
+ w.w.Draw(screen)
+}
+
+func (w *ClickWrapper) FindObjectAt(x, y int) interface{} {
+ return w.w.FindObjectAt(x, y)
+}
+
+func (w *ClickWrapper) Contains(x, y int) bool {
+ return w.w.Contains(x, y)
+}
+
+func (w *ClickWrapper) Click(x, y int) {
+ w.onClick(x, y)
+}
+
+func (w *ClickWrapper) Scroll(x, y int) {
+ if w, ok := w.w.(Scrollable); ok {
+ w.Scroll(x, y)
+ }
+}
diff --git a/go/ui/collection.go b/go/ui/collection.go
new file mode 100644
index 00000000..ac53ba8c
--- /dev/null
+++ b/go/ui/collection.go
@@ -0,0 +1,70 @@
+package ui
+
+import (
+ "github.com/hajimehoshi/ebiten/v2"
+ "github.com/hajimehoshi/ebiten/v2/inpututil"
+)
+
+type Collection struct {
+ widgets []Widget
+}
+
+func (c *Collection) Widgets() []Widget {
+ return c.widgets
+}
+
+func (c *Collection) Clear() {
+ c.widgets = []Widget{}
+}
+
+func (c *Collection) AddWidget(w Widget) {
+ c.widgets = append(c.widgets, w)
+}
+
+func (c *Collection) Draw(screen *ebiten.Image) {
+ for _, w := range c.widgets {
+ w.Draw(screen)
+ }
+}
+
+func (c *Collection) Update() error {
+ x, y := ebiten.CursorPosition()
+
+ var activeWidget Widget
+ for _, w := range c.widgets {
+ if !w.Contains(x, y) {
+ continue
+ }
+ activeWidget = w
+ break
+ }
+
+ if activeWidget == nil {
+ return nil
+ }
+
+ scrollX, scrollY := ebiten.Wheel()
+ if scrollX != 0 || scrollY != 0 {
+ if scrollable, ok := activeWidget.(Scrollable); ok {
+ scrollable.Scroll(int(scrollX), int(scrollY))
+ }
+ }
+
+ if textInput, ok := activeWidget.(*TextInput); ok {
+ // var input []rune
+ // ebiten.AppendInputChars(input)
+ input := ebiten.InputChars()
+ if len(input) > 0 {
+ textInput.AddInput(input)
+ }
+ textInput.HandleKey()
+ }
+
+ if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
+ if btn, ok := activeWidget.(Button); ok {
+ btn.Click(x, y)
+ }
+ }
+
+ return nil
+}
diff --git a/go/ui/colors.go b/go/ui/colors.go
index 3998e640..d8422479 100644
--- a/go/ui/colors.go
+++ b/go/ui/colors.go
@@ -5,6 +5,6 @@ import (
)
var (
- gray = color.RGBA{0x80, 0x80, 0x80, 0xff}
- resourceBg = color.RGBA{230, 180, 128, 0xff}
+ Gray = color.RGBA{0x80, 0x80, 0x80, 0xff}
+ ResourceBg = color.RGBA{230, 180, 128, 0xff}
)
diff --git a/go/ui/hand.go b/go/ui/hand.go
index f7157232..7c8feab8 100644
--- a/go/ui/hand.go
+++ b/go/ui/hand.go
@@ -1,13 +1,8 @@
package ui
import (
- "log"
-
"github.com/hajimehoshi/ebiten/v2"
- "muhq.space/muhqs-game/go/assets"
"muhq.space/muhqs-game/go/game"
-
- "golang.org/x/exp/slices"
)
type HandCard struct {
@@ -15,99 +10,24 @@ type HandCard struct {
}
type HandView struct {
- WidgetBase
- hand *game.Hand
- cardWidth float64
- cardHeight float64
- scale float64
- bottomAligned bool
- highlights []*game.Card
+ CardGrid
}
func NewHandView(x, y, width, height int, h *game.Hand, bottomAligned bool) *HandView {
- hv := &HandView{
- WidgetBase: NewWidgetBase(x, y, width, height),
- hand: h,
- cardWidth: 0,
- cardHeight: 0,
- scale: 0,
- bottomAligned: bottomAligned,
- highlights: []*game.Card{},
- }
+ hv := &HandView{CardGrid{
+ WidgetBase: NewWidgetBase(x, y, width, height),
+ cards: h,
+ rows: 1,
+ scale: -1,
+ }}
hv.renderImpl = func() *ebiten.Image { return hv.render() }
return hv
}
-func (hv *HandView) render() *ebiten.Image {
- img := ebiten.NewImage(hv.Width, hv.Height)
- handCards := hv.hand.Cards()
- nHandCards := len(handCards)
- for i, card := range handCards {
- cardImg := assets.GetCard(card.Path(), "en")
- if cardImg == nil {
- log.Fatalf("No image found for %s", card.Path())
- }
-
- if hv.scale == 0 {
- b := cardImg.Bounds()
- x_scale := (float64(hv.Width) / float64(nHandCards)) / float64(b.Dx())
- y_scale := float64(hv.Height) / float64(b.Dy())
- hv.scale = x_scale
- if y_scale < x_scale {
- hv.scale = y_scale
- }
- hv.cardWidth = float64(b.Dx()) * hv.scale
- hv.cardHeight = float64(b.Dy()) * hv.scale
- }
-
- yOffset := 0.0
- if hv.bottomAligned {
- yOffset = float64(hv.Height) - hv.cardHeight
- }
-
- op := &ebiten.DrawImageOptions{}
- op.GeoM.Scale(hv.scale, hv.scale)
- op.GeoM.Translate(float64(i)*hv.cardWidth, yOffset)
-
- if slices.Contains(hv.highlights, card) {
- op.ColorM.Scale(1, 0, 0, 0.5)
- }
-
- img.DrawImage(cardImg, op)
- }
- return img
-}
-
func (hv *HandView) FindObjectAt(x, y int) interface{} {
- if !hv.Contains(x, y) {
- return nil
+ if obj := hv.CardGrid.FindObjectAt(x, y); obj != nil {
+ return HandCard{obj.(*game.Card)}
}
- if hv.bottomAligned && float64(y-hv.Y) < float64(hv.Height)-hv.cardHeight {
- return nil
- }
-
- i := int(float64(x) / hv.cardWidth)
- if i >= hv.hand.Size() {
- return nil
- }
-
- return HandCard{hv.hand.Cards()[i]}
-}
-
-func (hv *HandView) setHighlights(cards []*game.Card) {
- hv.highlights = cards
- hv.ForceRedraw()
-}
-
-func (hv *HandView) HighlightCard(card *game.Card) {
- hv.setHighlights([]*game.Card{card})
-}
-
-func (hv *HandView) AddHighlightCard(card *game.Card) {
- hv.setHighlights(append(hv.highlights, card))
-}
-
-func (hv *HandView) ClearHighlights() {
- hv.setHighlights([]*game.Card{})
+ return nil
}
diff --git a/go/ui/stateBar.go b/go/ui/stateBar.go
index b698100a..93de9af9 100644
--- a/go/ui/stateBar.go
+++ b/go/ui/stateBar.go
@@ -33,7 +33,7 @@ func (sb *StateBar) phaseLabel() string {
func (sb *StateBar) Draw(screen *ebiten.Image) {
if sb.resourceView == nil {
sb.resourceView = NewFixedTextBox(sb.X, sb.Y,
- sb.Height*2, sb.Height, sb.resourceLabel()).Centering(true).Bg(resourceBg)
+ sb.Height*2, sb.Height, sb.resourceLabel()).Centering(true).Bg(ResourceBg)
}
sb.resourceView.Draw(screen)
diff --git a/go/ui/textBox.go b/go/ui/textBox.go
index 1c82a307..cc813a4e 100644
--- a/go/ui/textBox.go
+++ b/go/ui/textBox.go
@@ -127,6 +127,7 @@ func NewPocList(centerX, bottomY int, poc game.PileOfCards) *PocList {
bottomY: bottomY,
poc: poc,
}
+
w.renderImpl = func() *ebiten.Image {
w.setText()
return w.render()
@@ -135,6 +136,7 @@ func NewPocList(centerX, bottomY int, poc game.PileOfCards) *PocList {
}
func (w *PocList) setText() {
+ w.Width, w.Height = -1, -1
t := ""
for _, c := range w.poc.Cards() {
t = fmt.Sprintf("%s%s\n", t, c.Name)
@@ -188,11 +190,15 @@ func NewTextInput(x, y int, width, height int, label string) *TextInput {
return w
}
-func (ti *TextInput) AddInput(input []rune) {
- ti.input += string(input)
+func (ti *TextInput) SetInput(input string) {
+ ti.input = input
ti.ForceRedraw()
}
+func (ti *TextInput) AddInput(input []rune) {
+ ti.SetInput(ti.input + string(input))
+}
+
func (ti *TextInput) HandleKey() {
if inpututil.IsKeyJustPressed(ebiten.KeyBackspace) {
if len(ti.input) > 0 {
diff --git a/go/ui/textInput.go b/go/ui/textInput.go
deleted file mode 100644
index 5b1faa29..00000000
--- a/go/ui/textInput.go
+++ /dev/null
@@ -1 +0,0 @@
-package ui