OpenDiablo2/d2core/d2ui/button.go

310 lines
9.1 KiB
Go
Raw Normal View History

2019-11-10 13:51:02 +00:00
package d2ui
import (
2020-07-23 16:56:50 +00:00
"fmt"
2019-10-26 03:41:54 +00:00
"image"
"image/color"
2020-07-23 16:56:50 +00:00
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
2020-06-30 13:58:53 +00:00
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
)
2019-10-26 03:41:54 +00:00
// ButtonType defines the type of button
type ButtonType int
2020-07-23 16:56:50 +00:00
// ButtonType constants
2019-10-26 03:41:54 +00:00
const (
ButtonTypeWide ButtonType = 1
ButtonTypeMedium ButtonType = 2
ButtonTypeNarrow ButtonType = 3
ButtonTypeCancel ButtonType = 4
ButtonTypeTall ButtonType = 5
ButtonTypeShort ButtonType = 6
ButtonTypeOkCancel ButtonType = 7
2019-10-26 03:41:54 +00:00
// Game UI
ButtonTypeSkill ButtonType = 7
ButtonTypeRun ButtonType = 8
ButtonTypeMenu ButtonType = 9
ButtonTypeGoldCoin ButtonType = 10
ButtonTypeClose ButtonType = 11
ButtonTypeSecondaryInvHand ButtonType = 12
ButtonTypeMinipanelCharacter ButtonType = 13
ButtonTypeMinipanelInventory ButtonType = 14
ButtonTypeMinipanelSkill ButtonType = 15
ButtonTypeMinipanelAutomap ButtonType = 16
ButtonTypeMinipanelMessage ButtonType = 17
ButtonTypeMinipanelQuest ButtonType = 18
ButtonTypeMinipanelMen ButtonType = 19
)
// ButtonLayout defines the type of buttons
type ButtonLayout struct {
2020-07-23 16:56:50 +00:00
ResourceName string
PaletteName string
FontPath string
XSegments int
YSegments int
BaseFrame int
DisabledFrame int
ClickableRect *image.Rectangle
TextOffset int
Toggleable bool
AllowFrameChange bool
2019-10-26 03:41:54 +00:00
}
2020-07-23 16:56:50 +00:00
func getButtonLayouts() map[ButtonType]ButtonLayout {
return map[ButtonType]ButtonLayout{
ButtonTypeWide: {
XSegments: 2, YSegments: 1, ResourceName: d2resource.WideButtonBlank, PaletteName: d2resource.PaletteUnits,
DisabledFrame: -1, FontPath: d2resource.FontExocet10, AllowFrameChange: true, TextOffset: 1},
ButtonTypeShort: {
XSegments: 1, YSegments: 1, ResourceName: d2resource.ShortButtonBlank, PaletteName: d2resource.PaletteUnits,
DisabledFrame: -1, FontPath: d2resource.FontRediculous, AllowFrameChange: true, TextOffset: -1},
ButtonTypeMedium: {
XSegments: 1, YSegments: 1, ResourceName: d2resource.MediumButtonBlank, PaletteName: d2resource.PaletteUnits,
FontPath: d2resource.FontExocet10, AllowFrameChange: true},
ButtonTypeTall: {
XSegments: 1, YSegments: 1, ResourceName: d2resource.TallButtonBlank, PaletteName: d2resource.PaletteUnits,
FontPath: d2resource.FontExocet10, AllowFrameChange: true, TextOffset: 5},
ButtonTypeOkCancel: {
XSegments: 1, YSegments: 1, ResourceName: d2resource.CancelButton, PaletteName: d2resource.PaletteUnits,
DisabledFrame: -1, FontPath: d2resource.FontRediculous, AllowFrameChange: true},
ButtonTypeRun: {
XSegments: 1, YSegments: 1, ResourceName: d2resource.RunButton, PaletteName: d2resource.PaletteSky,
Toggleable: true, DisabledFrame: -1, FontPath: d2resource.FontRediculous, AllowFrameChange: true},
}
2019-10-26 03:41:54 +00:00
}
// Button defines a standard wide UI button
type Button struct {
2020-07-23 16:56:50 +00:00
buttonLayout ButtonLayout
normalSurface d2interface.Surface
pressedSurface d2interface.Surface
toggledSurface d2interface.Surface
pressedToggledSurface d2interface.Surface
disabledSurface d2interface.Surface
2020-07-23 16:56:50 +00:00
x int
y int
width int
height int
onClick func()
2020-07-23 16:56:50 +00:00
enabled bool
visible bool
pressed bool
toggled bool
}
// CreateButton creates an instance of Button
func CreateButton(renderer d2interface.Renderer, buttonType ButtonType, text string) Button {
result := Button{
width: 0,
height: 0,
visible: true,
enabled: true,
pressed: false,
}
2020-07-23 16:56:50 +00:00
buttonLayout := getButtonLayouts()[buttonType]
result.buttonLayout = buttonLayout
2020-07-23 16:56:50 +00:00
lbl := CreateLabel(buttonLayout.FontPath, d2resource.PaletteUnits)
lbl.SetText(text)
lbl.Color = color.RGBA{R: 100, G: 100, B: 100, A: 255}
lbl.Alignment = d2gui.HorizontalAlignCenter
2019-11-10 08:36:53 +00:00
animation, _ := d2asset.LoadAnimation(buttonLayout.ResourceName, buttonLayout.PaletteName)
buttonSprite, _ := LoadSprite(animation)
2020-07-23 16:56:50 +00:00
2019-10-26 03:41:54 +00:00
for i := 0; i < buttonLayout.XSegments; i++ {
w, _, _ := buttonSprite.GetFrameSize(i)
2019-10-26 03:41:54 +00:00
result.width += w
}
2020-07-23 16:56:50 +00:00
2019-10-26 03:41:54 +00:00
for i := 0; i < buttonLayout.YSegments; i++ {
_, h, _ := buttonSprite.GetFrameSize(i * buttonLayout.YSegments)
2019-10-26 03:41:54 +00:00
result.height += h
}
result.normalSurface, _ = renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
2019-10-27 06:58:37 +00:00
buttonSprite.SetPosition(0, 0)
buttonSprite.SetEffect(d2enum.DrawEffectModulate)
2020-07-23 16:56:50 +00:00
result.renderFrames(renderer, buttonSprite, &buttonLayout, &lbl)
return result
}
func (v *Button) renderFrames(renderer d2interface.Renderer, buttonSprite *Sprite, buttonLayout *ButtonLayout, label *Label) {
totalButtonTypes := buttonSprite.GetFrameCount() / (buttonLayout.XSegments * buttonLayout.YSegments)
var err error
err = buttonSprite.RenderSegmented(v.normalSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame)
if err != nil {
fmt.Printf("failed to render button normalSurface, err: %v\n", err)
}
_, labelHeight := label.GetSize()
textY := v.height/2 - labelHeight/2
xOffset := v.width / 2
label.SetPosition(xOffset, textY)
label.Render(v.normalSurface)
2019-10-26 03:41:54 +00:00
if buttonLayout.AllowFrameChange {
if totalButtonTypes > 1 {
2020-07-23 16:56:50 +00:00
v.pressedSurface, _ = renderer.NewSurface(v.width, v.height, d2enum.FilterNearest)
err = buttonSprite.RenderSegmented(v.pressedSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+1)
if err != nil {
fmt.Printf("failed to render button pressedSurface, err: %v\n", err)
}
2020-07-23 16:56:50 +00:00
label.SetPosition(xOffset-2, textY+2)
label.Render(v.pressedSurface)
2019-10-26 03:41:54 +00:00
}
2020-07-23 16:56:50 +00:00
2019-10-26 03:41:54 +00:00
if totalButtonTypes > 2 {
2020-07-23 16:56:50 +00:00
v.toggledSurface, _ = renderer.NewSurface(v.width, v.height, d2enum.FilterNearest)
err = buttonSprite.RenderSegmented(v.toggledSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+2)
if err != nil {
fmt.Printf("failed to render button toggledSurface, err: %v\n", err)
}
2020-07-23 16:56:50 +00:00
label.SetPosition(xOffset, textY)
label.Render(v.toggledSurface)
2019-10-26 03:41:54 +00:00
}
2020-07-23 16:56:50 +00:00
2019-10-26 03:41:54 +00:00
if totalButtonTypes > 3 {
2020-07-23 16:56:50 +00:00
v.pressedToggledSurface, _ = renderer.NewSurface(v.width, v.height, d2enum.FilterNearest)
err = buttonSprite.RenderSegmented(v.pressedToggledSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+3)
if err != nil {
fmt.Printf("failed to render button pressedToggledSurface, err: %v\n", err)
}
2020-07-23 16:56:50 +00:00
label.SetPosition(xOffset, textY)
label.Render(v.pressedToggledSurface)
2019-10-26 03:41:54 +00:00
}
2020-07-23 16:56:50 +00:00
2019-10-26 03:41:54 +00:00
if buttonLayout.DisabledFrame != -1 {
2020-07-23 16:56:50 +00:00
v.disabledSurface, _ = renderer.NewSurface(v.width, v.height, d2enum.FilterNearest)
err = buttonSprite.RenderSegmented(v.disabledSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.DisabledFrame)
2020-07-23 16:56:50 +00:00
if err != nil {
fmt.Printf("failed to render button disabledSurface, err: %v\n", err)
}
label.SetPosition(xOffset, textY)
label.Render(v.disabledSurface)
2019-10-26 03:41:54 +00:00
}
}
}
2019-10-26 03:41:54 +00:00
// OnActivated defines the callback handler for the activate event
func (v *Button) OnActivated(callback func()) {
v.onClick = callback
}
// Activate calls the on activated callback handler, if any
func (v *Button) Activate() {
2019-10-26 03:41:54 +00:00
if v.onClick == nil {
return
}
2020-07-23 16:56:50 +00:00
2019-10-26 03:41:54 +00:00
v.onClick()
}
// Render renders the button
2020-07-26 18:52:54 +00:00
func (v *Button) Render(target d2interface.Surface) error {
target.PushFilter(d2enum.FilterNearest)
target.PushTranslation(v.x, v.y)
2020-07-23 16:56:50 +00:00
defer target.PopN(2)
2019-10-26 03:41:54 +00:00
2020-07-23 16:56:50 +00:00
var err error
switch {
case !v.enabled:
target.PushColor(color.RGBA{R: 128, G: 128, B: 128, A: 195})
defer target.Pop()
2020-07-23 16:56:50 +00:00
err = target.Render(v.disabledSurface)
case v.toggled && v.pressed:
err = target.Render(v.pressedToggledSurface)
case v.pressed:
err = target.Render(v.pressedSurface)
case v.toggled:
err = target.Render(v.toggledSurface)
default:
err = target.Render(v.normalSurface)
}
if err != nil {
fmt.Printf("failed to render button surface, err: %v\n", err)
}
2020-07-26 18:52:54 +00:00
return nil
}
2020-07-23 16:56:50 +00:00
// Toggle negates the toggled state of the button
func (v *Button) Toggle() {
v.toggled = !v.toggled
}
2020-07-23 16:56:50 +00:00
// Advance advances the button state
func (v *Button) Advance(elapsed float64) {
}
// GetEnabled returns the enabled state
func (v *Button) GetEnabled() bool {
return v.enabled
}
// SetEnabled sets the enabled state
func (v *Button) SetEnabled(enabled bool) {
v.enabled = enabled
}
2020-07-23 16:56:50 +00:00
// GetSize returns the size of the button
func (v *Button) GetSize() (width, height int) {
return v.width, v.height
}
// SetPosition moves the button
func (v *Button) SetPosition(x, y int) {
v.x = x
v.y = y
}
// GetPosition returns the location of the button
func (v *Button) GetPosition() (x, y int) {
return v.x, v.y
}
// GetVisible returns the visibility of the button
func (v *Button) GetVisible() bool {
return v.visible
}
// SetVisible sets the visibility of the button
func (v *Button) SetVisible(visible bool) {
v.visible = visible
}
2019-10-26 03:41:54 +00:00
// GetPressed returns the pressed state of the button
func (v *Button) GetPressed() bool {
2019-10-26 03:41:54 +00:00
return v.pressed
}
// SetPressed sets the pressed state of the button
func (v *Button) SetPressed(pressed bool) {
v.pressed = pressed
}