diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2023-02-14 16:05:06 +0100 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-01-27 16:43:49 +0100 |
| commit | b85c1a85e3f9a53321495e4d015e7620b29f9929 (patch) | |
| tree | 314469f8bcbe61cea3c055d01e2c961b0e988708 /go/ui | |
| parent | e049500b213c85c9d1556e01717226a191d2dce8 (diff) | |
| download | muhqs-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.go | 2 | ||||
| -rw-r--r-- | go/ui/button.go | 4 | ||||
| -rw-r--r-- | go/ui/cardGrid.go | 183 | ||||
| -rw-r--r-- | go/ui/clickable.go | 40 | ||||
| -rw-r--r-- | go/ui/collection.go | 70 | ||||
| -rw-r--r-- | go/ui/colors.go | 4 | ||||
| -rw-r--r-- | go/ui/hand.go | 100 | ||||
| -rw-r--r-- | go/ui/stateBar.go | 2 | ||||
| -rw-r--r-- | go/ui/textBox.go | 10 | ||||
| -rw-r--r-- | go/ui/textInput.go | 1 |
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 |
