mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-10-01 15:46:17 -04:00
Continued work on GUI (#316)
* Configuration cleanup * Cleanup * Continued UI work
This commit is contained in:
parent
2285c31b53
commit
6f2c212417
@ -60,6 +60,10 @@ func loadFont(tablePath, spritePath, palettePath string) (*Font, error) {
|
||||
return font, nil
|
||||
}
|
||||
|
||||
func (f *Font) SetColor(color color.Color) {
|
||||
f.color = color
|
||||
}
|
||||
|
||||
func (f *Font) GetTextMetrics(text string) (int, int) {
|
||||
var (
|
||||
lineWidth int
|
||||
|
131
d2core/d2gui/button.go
Normal file
131
d2core/d2gui/button.go
Normal file
@ -0,0 +1,131 @@
|
||||
package d2gui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
type buttonState int
|
||||
|
||||
const (
|
||||
buttonStateDefault buttonState = iota
|
||||
buttonStatePressed
|
||||
buttonStateToggled
|
||||
buttonStatePressedToggled
|
||||
)
|
||||
|
||||
type Button struct {
|
||||
widgetBase
|
||||
|
||||
width int
|
||||
height int
|
||||
state buttonState
|
||||
surfaces []d2render.Surface
|
||||
}
|
||||
|
||||
func createButton(text string, buttonStyle ButtonStyle) (*Button, error) {
|
||||
config, ok := buttonStyleConfigs[buttonStyle]
|
||||
if !ok {
|
||||
return nil, errors.New("invalid button style")
|
||||
}
|
||||
|
||||
animation, err := d2asset.LoadAnimation(config.animationPath, config.palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buttonWidth int
|
||||
for i := 0; i < config.segmentsX; i++ {
|
||||
w, _, err := animation.GetFrameSize(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buttonWidth += w
|
||||
}
|
||||
|
||||
var buttonHeight int
|
||||
for i := 0; i < config.segmentsY; i++ {
|
||||
_, h, err := animation.GetFrameSize(i * config.segmentsY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buttonHeight += h
|
||||
}
|
||||
|
||||
font, err := loadFont(config.fontStyle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
textColor := color.RGBA{R: 0x64, G: 0x64, B: 0x64, A: 0xff}
|
||||
textWidth, textHeight := font.GetTextMetrics(text)
|
||||
textX := buttonWidth/2 - textWidth/2
|
||||
textY := buttonHeight/2 - textHeight/2 + config.textOffset
|
||||
|
||||
surfaceCount := animation.GetFrameCount() / (config.segmentsX * config.segmentsY)
|
||||
surfaces := make([]d2render.Surface, surfaceCount)
|
||||
for i := 0; i < surfaceCount; i++ {
|
||||
surface, err := d2render.NewSurface(buttonWidth, buttonHeight, d2render.FilterNearest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := renderSegmented(animation, config.segmentsX, config.segmentsY, i, surface); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
font.SetColor(textColor)
|
||||
|
||||
var textOffsetX, textOffsetY int
|
||||
switch buttonState(i) {
|
||||
case buttonStatePressed, buttonStatePressedToggled:
|
||||
textOffsetX = -2
|
||||
textOffsetY = 2
|
||||
break
|
||||
}
|
||||
|
||||
surface.PushTranslation(textX+textOffsetX, textY+textOffsetY)
|
||||
err = font.RenderText(text, surface)
|
||||
surface.Pop()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
surfaces[i] = surface
|
||||
}
|
||||
|
||||
button := &Button{width: buttonWidth, height: buttonHeight, surfaces: surfaces}
|
||||
button.SetVisible(true)
|
||||
|
||||
return button, nil
|
||||
}
|
||||
|
||||
func (b *Button) onMouseButtonDown(event d2input.MouseEvent) bool {
|
||||
b.state = buttonStatePressed
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Button) onMouseButtonUp(event d2input.MouseEvent) bool {
|
||||
b.state = buttonStateDefault
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Button) onMouseLeave(event d2input.MouseMoveEvent) bool {
|
||||
b.state = buttonStateDefault
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Button) render(target d2render.Surface) error {
|
||||
return target.Render(b.surfaces[b.state])
|
||||
}
|
||||
|
||||
func (b *Button) getSize() (int, int) {
|
||||
return b.width, b.height
|
||||
}
|
46
d2core/d2gui/common.go
Normal file
46
d2core/d2gui/common.go
Normal file
@ -0,0 +1,46 @@
|
||||
package d2gui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
func loadFont(fontStyle FontStyle) (*d2asset.Font, error) {
|
||||
config, ok := fontStyleConfigs[fontStyle]
|
||||
if !ok {
|
||||
return nil, errors.New("invalid font style")
|
||||
}
|
||||
|
||||
return d2asset.LoadFont(config.fontBasePath+".tbl", config.fontBasePath+".dc6", config.palettePath)
|
||||
}
|
||||
|
||||
func renderSegmented(animation *d2asset.Animation, segmentsX, segmentsY, frameOffset int, target d2render.Surface) error {
|
||||
var currentY int
|
||||
for y := 0; y < segmentsY; y++ {
|
||||
var currentX int
|
||||
var maxHeight int
|
||||
for x := 0; x < segmentsX; x++ {
|
||||
if err := animation.SetCurrentFrame(x + y*segmentsX + frameOffset*segmentsX*segmentsY); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
target.PushTranslation(x+currentX, y+currentY)
|
||||
err := animation.Render(target)
|
||||
target.Pop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
width, height := animation.GetCurrentFrameSize()
|
||||
maxHeight = d2common.MaxInt(maxHeight, height)
|
||||
currentX += width
|
||||
}
|
||||
|
||||
currentY += maxHeight
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -34,21 +34,14 @@ func Advance(elapsed float64) error {
|
||||
return singleton.advance(elapsed)
|
||||
}
|
||||
|
||||
func AddLayout() *Layout {
|
||||
return singleton.addLayout()
|
||||
}
|
||||
|
||||
func AddSprite(imagePath, palettePath string) *Sprite {
|
||||
return singleton.addSprite(imagePath, palettePath)
|
||||
}
|
||||
|
||||
func AddLabel(text string, fontStyle FontStyle) *Label {
|
||||
return singleton.addLabel(text, fontStyle)
|
||||
}
|
||||
|
||||
func Clear() {
|
||||
func CreateLayout(positionType PositionType) *Layout {
|
||||
verifyWasInit()
|
||||
singleton.clear()
|
||||
return createLayout(positionType)
|
||||
}
|
||||
|
||||
func SetLayout(layout *Layout) {
|
||||
verifyWasInit()
|
||||
singleton.SetLayout(layout)
|
||||
}
|
||||
|
||||
func ShowLoadScreen(progress float64) {
|
||||
|
@ -1,60 +1,41 @@
|
||||
package d2gui
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
type Label struct {
|
||||
widgetBase
|
||||
|
||||
text string
|
||||
font *d2asset.Font
|
||||
surface d2render.Surface
|
||||
}
|
||||
|
||||
func createLabel(text string, fontStyle FontStyle) *Label {
|
||||
font, _ := loadFont(fontStyle)
|
||||
label := &Label{font: font}
|
||||
label.SetText(text)
|
||||
label.visible = true
|
||||
return label
|
||||
}
|
||||
func createLabel(text string, fontStyle FontStyle) (*Label, error) {
|
||||
font, err := loadFont(fontStyle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (l *Label) SetText(text string) *Label {
|
||||
l.text = text
|
||||
l.cache()
|
||||
return l
|
||||
width, height := font.GetTextMetrics(text)
|
||||
surface, err := d2render.NewSurface(width, height, d2render.FilterNearest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := font.RenderText(text, surface); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label := &Label{surface: surface}
|
||||
label.SetVisible(true)
|
||||
|
||||
return label, nil
|
||||
}
|
||||
|
||||
func (l *Label) render(target d2render.Surface) error {
|
||||
if l.surface == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return target.Render(l.surface)
|
||||
}
|
||||
|
||||
func (l *Label) cache() error {
|
||||
l.surface = nil
|
||||
if l.font == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
width, height := l.font.GetTextMetrics(l.text)
|
||||
|
||||
var err error
|
||||
if l.surface, err = d2render.NewSurface(width, height, d2render.FilterNearest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return l.font.RenderText(l.text, l.surface)
|
||||
}
|
||||
|
||||
func (l *Label) getSize() (int, int) {
|
||||
if l.surface == nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
return l.surface.GetSize()
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package d2gui
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
@ -10,26 +11,136 @@ import (
|
||||
type layoutEntry struct {
|
||||
widget widget
|
||||
|
||||
x int
|
||||
y int
|
||||
width int
|
||||
height int
|
||||
|
||||
mouseOver bool
|
||||
mouseDown [3]bool
|
||||
}
|
||||
|
||||
type VerticalAlign int
|
||||
|
||||
const (
|
||||
VerticalAlignTop VerticalAlign = iota
|
||||
VerticalAlignMiddle
|
||||
VerticalAlignBottom
|
||||
)
|
||||
|
||||
type HorizontalAlign int
|
||||
|
||||
const (
|
||||
HorizontalAlignLeft HorizontalAlign = iota
|
||||
HorizontalAlignCenter
|
||||
HorizontalAlignRight
|
||||
)
|
||||
|
||||
type PositionType int
|
||||
|
||||
const (
|
||||
PositionTypeAbsolute PositionType = iota
|
||||
PositionTypeVertical
|
||||
PositionTypeHorizontal
|
||||
)
|
||||
|
||||
type Layout struct {
|
||||
widgetBase
|
||||
entries []*layoutEntry
|
||||
|
||||
width int
|
||||
height int
|
||||
verticalAlign VerticalAlign
|
||||
horizontalAlign HorizontalAlign
|
||||
positionType PositionType
|
||||
entries []*layoutEntry
|
||||
}
|
||||
|
||||
func createLayout() *Layout {
|
||||
layout := new(Layout)
|
||||
layout.visible = true
|
||||
func createLayout(positionType PositionType) *Layout {
|
||||
layout := &Layout{positionType: positionType}
|
||||
layout.SetVisible(true)
|
||||
|
||||
return layout
|
||||
}
|
||||
|
||||
func (l *Layout) SetSize(width, height int) {
|
||||
l.width = width
|
||||
l.height = height
|
||||
}
|
||||
|
||||
func (l *Layout) SetVerticalAlign(verticalAlign VerticalAlign) {
|
||||
l.verticalAlign = verticalAlign
|
||||
}
|
||||
|
||||
func (l *Layout) SetHorizontalAlign(horizontalAlign HorizontalAlign) {
|
||||
l.horizontalAlign = horizontalAlign
|
||||
}
|
||||
|
||||
func (l *Layout) AddLayout(positionType PositionType) *Layout {
|
||||
layout := createLayout(positionType)
|
||||
l.entries = append(l.entries, &layoutEntry{widget: layout})
|
||||
return layout
|
||||
}
|
||||
|
||||
func (l *Layout) AddSpacerStatic(width, height int) *SpacerStatic {
|
||||
spacer := createSpacerStatic(width, height)
|
||||
l.entries = append(l.entries, &layoutEntry{widget: spacer})
|
||||
return spacer
|
||||
}
|
||||
|
||||
func (l *Layout) AddSpacerDynamic() *SpacerDynamic {
|
||||
spacer := createSpacerDynamic()
|
||||
l.entries = append(l.entries, &layoutEntry{widget: spacer})
|
||||
return spacer
|
||||
}
|
||||
|
||||
func (l *Layout) AddSprite(imagePath, palettePath string) (*Sprite, error) {
|
||||
sprite, err := createSprite(imagePath, palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.entries = append(l.entries, &layoutEntry{widget: sprite})
|
||||
return sprite, nil
|
||||
}
|
||||
|
||||
func (l *Layout) AddLabel(text string, fontStyle FontStyle) (*Label, error) {
|
||||
label, err := createLabel(text, fontStyle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.entries = append(l.entries, &layoutEntry{widget: label})
|
||||
return label, nil
|
||||
}
|
||||
|
||||
func (l *Layout) AddButton(text string, buttonStyle ButtonStyle) (*Button, error) {
|
||||
button, err := createButton(text, buttonStyle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.entries = append(l.entries, &layoutEntry{widget: button})
|
||||
return button, nil
|
||||
}
|
||||
|
||||
func (l *Layout) Clear() {
|
||||
l.entries = nil
|
||||
}
|
||||
|
||||
func (l *Layout) render(target d2render.Surface) error {
|
||||
l.adjustEntryPlacement()
|
||||
|
||||
for _, entry := range l.entries {
|
||||
if entry.widget.isVisible() {
|
||||
l.renderWidget(entry.widget, target)
|
||||
l.renderWidgetDebug(entry.widget, target)
|
||||
if !entry.widget.isVisible() {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := l.renderEntry(entry, target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := l.renderEntryDebug(entry, target); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,51 +149,88 @@ func (l *Layout) render(target d2render.Surface) error {
|
||||
|
||||
func (l *Layout) advance(elapsed float64) error {
|
||||
for _, entry := range l.entries {
|
||||
if entry.widget.isVisible() {
|
||||
if err := entry.widget.advance(elapsed); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := entry.widget.advance(elapsed); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Layout) renderWidget(widget widget, target d2render.Surface) {
|
||||
target.PushTranslation(widget.getPosition())
|
||||
func (l *Layout) renderEntry(entry *layoutEntry, target d2render.Surface) error {
|
||||
target.PushTranslation(entry.x, entry.y)
|
||||
defer target.Pop()
|
||||
|
||||
widget.render(target)
|
||||
return entry.widget.render(target)
|
||||
}
|
||||
|
||||
func (l *Layout) renderWidgetDebug(widget widget, target d2render.Surface) {
|
||||
target.PushTranslation(widget.getPosition())
|
||||
func (l *Layout) renderEntryDebug(entry *layoutEntry, target d2render.Surface) error {
|
||||
target.PushTranslation(entry.x, entry.y)
|
||||
defer target.Pop()
|
||||
|
||||
drawColor := color.RGBA{R: 0x00, G: 0x00, B: 0xff, A: 0xb0}
|
||||
drawColor := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
|
||||
switch entry.widget.(type) {
|
||||
case *Layout:
|
||||
drawColor = color.RGBA{R: 0xff, G: 0x00, B: 0xff, A: 0xff}
|
||||
case *SpacerStatic, *SpacerDynamic:
|
||||
drawColor = color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0xff}
|
||||
case *Label:
|
||||
drawColor = color.RGBA{R: 0x00, G: 0x00, B: 0xff, A: 0xff}
|
||||
case *Button:
|
||||
drawColor = color.RGBA{R: 0xff, G: 0xff, B: 0x00, A: 0xff}
|
||||
}
|
||||
|
||||
width, height := widget.getSize()
|
||||
target.DrawLine(width, 0, drawColor)
|
||||
target.DrawLine(0, height, drawColor)
|
||||
target.DrawLine(entry.width, 0, drawColor)
|
||||
target.DrawLine(0, entry.height, drawColor)
|
||||
|
||||
target.PushTranslation(width, 0)
|
||||
target.DrawLine(0, height, drawColor)
|
||||
target.PushTranslation(entry.width, 0)
|
||||
target.DrawLine(0, entry.height, drawColor)
|
||||
target.Pop()
|
||||
|
||||
target.PushTranslation(0, height)
|
||||
target.DrawLine(width, 0, drawColor)
|
||||
target.PushTranslation(0, entry.height)
|
||||
target.DrawLine(entry.width, 0, drawColor)
|
||||
target.Pop()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Layout) getContentSize() (int, int) {
|
||||
var width, height int
|
||||
|
||||
for _, entry := range l.entries {
|
||||
x, y := entry.widget.getPosition()
|
||||
w, h := entry.widget.getSize()
|
||||
|
||||
switch l.positionType {
|
||||
case PositionTypeVertical:
|
||||
width = d2common.MaxInt(width, w)
|
||||
height += h
|
||||
break
|
||||
case PositionTypeHorizontal:
|
||||
width += w
|
||||
height = d2common.MaxInt(height, h)
|
||||
break
|
||||
case PositionTypeAbsolute:
|
||||
width = d2common.MaxInt(width, x+w)
|
||||
height = d2common.MaxInt(height, y+h)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return width, height
|
||||
}
|
||||
|
||||
func (l *Layout) getSize() (int, int) {
|
||||
return 0, 0
|
||||
width, height := l.getContentSize()
|
||||
return d2common.MaxInt(width, l.width), d2common.MaxInt(height, l.height)
|
||||
}
|
||||
|
||||
func (l *Layout) onMouseButtonDown(event d2input.MouseEvent) bool {
|
||||
for _, entry := range l.entries {
|
||||
eventLocal := event
|
||||
|
||||
if l.adjustEventCoords(entry.widget, &eventLocal.X, &eventLocal.Y) {
|
||||
if l.adjustEntryEvent(entry, &eventLocal.X, &eventLocal.Y) {
|
||||
entry.widget.onMouseButtonDown(eventLocal)
|
||||
entry.mouseDown[event.Button] = true
|
||||
}
|
||||
}
|
||||
@ -94,9 +242,10 @@ func (l *Layout) onMouseButtonUp(event d2input.MouseEvent) bool {
|
||||
for _, entry := range l.entries {
|
||||
eventLocal := event
|
||||
|
||||
if l.adjustEventCoords(entry.widget, &eventLocal.X, &eventLocal.Y) {
|
||||
if l.adjustEntryEvent(entry, &eventLocal.X, &eventLocal.Y) {
|
||||
if entry.mouseDown[event.Button] {
|
||||
entry.widget.onMouseClick(eventLocal)
|
||||
entry.widget.onMouseButtonClick(eventLocal)
|
||||
entry.widget.onMouseButtonUp(eventLocal)
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,7 +259,8 @@ func (l *Layout) onMouseMove(event d2input.MouseMoveEvent) bool {
|
||||
for _, entry := range l.entries {
|
||||
eventLocal := event
|
||||
|
||||
if l.adjustEventCoords(entry.widget, &eventLocal.X, &eventLocal.Y) {
|
||||
if l.adjustEntryEvent(entry, &eventLocal.X, &eventLocal.Y) {
|
||||
entry.widget.onMouseMove(eventLocal)
|
||||
if entry.mouseOver {
|
||||
entry.widget.onMouseOver(eventLocal)
|
||||
} else {
|
||||
@ -126,38 +276,90 @@ func (l *Layout) onMouseMove(event d2input.MouseMoveEvent) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *Layout) adjustEventCoords(widget widget, eventX, eventY *int) bool {
|
||||
x, y := widget.getPosition()
|
||||
width, height := widget.getSize()
|
||||
func (l *Layout) adjustEntryEvent(entry *layoutEntry, eventX, eventY *int) bool {
|
||||
*eventX -= entry.x
|
||||
*eventY -= entry.y
|
||||
|
||||
*eventX -= x
|
||||
*eventY -= y
|
||||
|
||||
if *eventX < 0 || *eventY < 0 || *eventX >= width || *eventY >= height {
|
||||
if *eventX < 0 || *eventY < 0 || *eventX >= entry.width || *eventY >= entry.height {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (l *Layout) addLayout() *Layout {
|
||||
layout := createLayout()
|
||||
l.entries = append(l.entries, &layoutEntry{widget: layout})
|
||||
return layout
|
||||
}
|
||||
func (l *Layout) adjustEntryPlacement() {
|
||||
width, height := l.getSize()
|
||||
|
||||
func (l *Layout) addSprite(imagePath, palettePath string) *Sprite {
|
||||
sprite := createSprite(imagePath, palettePath)
|
||||
l.entries = append(l.entries, &layoutEntry{widget: sprite})
|
||||
return sprite
|
||||
}
|
||||
var expanderCount int
|
||||
for _, entry := range l.entries {
|
||||
if entry.widget.isVisible() && entry.widget.isExpanding() {
|
||||
expanderCount++
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Layout) addLabel(text string, fontStyle FontStyle) *Label {
|
||||
label := createLabel(text, fontStyle)
|
||||
l.entries = append(l.entries, &layoutEntry{widget: label})
|
||||
return label
|
||||
}
|
||||
var expanderWidth, expanderHeight int
|
||||
if expanderCount > 0 {
|
||||
contentWidth, contentHeight := l.getContentSize()
|
||||
|
||||
func (l *Layout) clear() {
|
||||
l.entries = nil
|
||||
switch l.positionType {
|
||||
case PositionTypeVertical:
|
||||
expanderHeight = (height - contentHeight) / expanderCount
|
||||
break
|
||||
case PositionTypeHorizontal:
|
||||
expanderWidth = (width - contentWidth) / expanderCount
|
||||
break
|
||||
}
|
||||
|
||||
expanderWidth = d2common.MaxInt(0, expanderWidth)
|
||||
expanderHeight = d2common.MaxInt(0, expanderHeight)
|
||||
}
|
||||
|
||||
var offsetX, offsetY int
|
||||
for _, entry := range l.entries {
|
||||
if !entry.widget.isVisible() {
|
||||
continue
|
||||
}
|
||||
|
||||
if entry.widget.isExpanding() {
|
||||
entry.width, entry.height = expanderWidth, expanderHeight
|
||||
} else {
|
||||
entry.width, entry.height = entry.widget.getSize()
|
||||
}
|
||||
|
||||
switch l.positionType {
|
||||
case PositionTypeVertical:
|
||||
entry.y = offsetY
|
||||
offsetY += entry.height
|
||||
switch l.horizontalAlign {
|
||||
case HorizontalAlignLeft:
|
||||
entry.x = 0
|
||||
break
|
||||
case HorizontalAlignCenter:
|
||||
entry.x = width/2 - entry.width/2
|
||||
break
|
||||
case HorizontalAlignRight:
|
||||
entry.x = width - entry.width
|
||||
break
|
||||
}
|
||||
break
|
||||
case PositionTypeHorizontal:
|
||||
entry.x = offsetX
|
||||
offsetX += entry.width
|
||||
switch l.verticalAlign {
|
||||
case VerticalAlignTop:
|
||||
entry.y = 0
|
||||
break
|
||||
case VerticalAlignMiddle:
|
||||
entry.y = height/2 - entry.height/2
|
||||
break
|
||||
case VerticalAlignBottom:
|
||||
entry.y = height - entry.height
|
||||
break
|
||||
}
|
||||
break
|
||||
case PositionTypeAbsolute:
|
||||
entry.x, entry.y = entry.widget.getPosition()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
type manager struct {
|
||||
Layout
|
||||
layout *Layout
|
||||
|
||||
cursorAnim *d2asset.Animation
|
||||
cursorX int
|
||||
@ -46,19 +46,35 @@ func createGuiManager() (*manager, error) {
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
func (m *manager) SetLayout(layout *Layout) {
|
||||
m.layout = layout
|
||||
}
|
||||
|
||||
func (m *manager) OnMouseButtonDown(event d2input.MouseEvent) bool {
|
||||
return m.Layout.onMouseButtonDown(event)
|
||||
if m.layout == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return m.layout.onMouseButtonDown(event)
|
||||
}
|
||||
|
||||
func (m *manager) OnMouseButtonUp(event d2input.MouseEvent) bool {
|
||||
return m.Layout.onMouseButtonUp(event)
|
||||
if m.layout == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return m.layout.onMouseButtonUp(event)
|
||||
}
|
||||
|
||||
func (m *manager) OnMouseMove(event d2input.MouseMoveEvent) bool {
|
||||
m.cursorX = event.X
|
||||
m.cursorY = event.Y
|
||||
|
||||
return m.Layout.onMouseMove(event)
|
||||
if m.layout == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return m.layout.onMouseMove(event)
|
||||
}
|
||||
|
||||
func (m *manager) render(target d2render.Surface) error {
|
||||
@ -66,8 +82,9 @@ func (m *manager) render(target d2render.Surface) error {
|
||||
if err := m.renderLoadScreen(target); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := m.Layout.render(target); err != nil {
|
||||
} else if m.layout != nil {
|
||||
m.layout.SetSize(target.GetSize())
|
||||
if err := m.layout.render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -103,8 +120,8 @@ func (m *manager) renderCursor(target d2render.Surface) error {
|
||||
}
|
||||
|
||||
func (m *manager) advance(elapsed float64) error {
|
||||
if !m.loading {
|
||||
if err := m.Layout.advance(elapsed); err != nil {
|
||||
if !m.loading && m.layout != nil {
|
||||
if err := m.layout.advance(elapsed); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -136,6 +153,6 @@ func (m *manager) hideCursor() {
|
||||
}
|
||||
|
||||
func (m *manager) clear() {
|
||||
m.Layout.clear()
|
||||
m.SetLayout(nil)
|
||||
m.hideLoadScreen()
|
||||
}
|
||||
|
31
d2core/d2gui/spacer.go
Normal file
31
d2core/d2gui/spacer.go
Normal file
@ -0,0 +1,31 @@
|
||||
package d2gui
|
||||
|
||||
type SpacerStatic struct {
|
||||
widgetBase
|
||||
|
||||
width int
|
||||
height int
|
||||
}
|
||||
|
||||
func createSpacerStatic(width, height int) *SpacerStatic {
|
||||
spacer := &SpacerStatic{width: width, height: height}
|
||||
spacer.SetVisible(true)
|
||||
|
||||
return spacer
|
||||
}
|
||||
|
||||
func (s *SpacerStatic) getSize() (int, int) {
|
||||
return s.width, s.height
|
||||
}
|
||||
|
||||
type SpacerDynamic struct {
|
||||
widgetBase
|
||||
}
|
||||
|
||||
func createSpacerDynamic() *SpacerDynamic {
|
||||
spacer := &SpacerDynamic{}
|
||||
spacer.SetVisible(true)
|
||||
spacer.SetExpanding(true)
|
||||
|
||||
return spacer
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package d2gui
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
@ -16,11 +15,17 @@ type Sprite struct {
|
||||
animation *d2asset.Animation
|
||||
}
|
||||
|
||||
func createSprite(imagePath, palettePath string) *Sprite {
|
||||
sprite := new(Sprite)
|
||||
sprite.animation, _ = d2asset.LoadAnimation(imagePath, palettePath)
|
||||
sprite.visible = true
|
||||
return sprite
|
||||
func createSprite(imagePath, palettePath string) (*Sprite, error) {
|
||||
animation, err := d2asset.LoadAnimation(imagePath, palettePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sprite := &Sprite{}
|
||||
sprite.animation = animation
|
||||
sprite.SetVisible(true)
|
||||
|
||||
return sprite, nil
|
||||
}
|
||||
|
||||
func (s *Sprite) SetSegmented(segmentsX, segmentsY, frameOffset int) {
|
||||
@ -30,57 +35,13 @@ func (s *Sprite) SetSegmented(segmentsX, segmentsY, frameOffset int) {
|
||||
}
|
||||
|
||||
func (s *Sprite) render(target d2render.Surface) error {
|
||||
if s.animation == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, height := s.animation.GetCurrentFrameSize()
|
||||
target.PushTranslation(0, -height)
|
||||
defer target.Pop()
|
||||
|
||||
if s.segmentsX == 0 && s.segmentsY == 0 {
|
||||
return s.animation.Render(target)
|
||||
}
|
||||
|
||||
var currentY int
|
||||
for y := 0; y < s.segmentsY; y++ {
|
||||
var currentX int
|
||||
var maxHeight int
|
||||
for x := 0; x < s.segmentsX; x++ {
|
||||
if err := s.animation.SetCurrentFrame(x + y*s.segmentsX + s.frameOffset*s.segmentsX*s.segmentsY); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
target.PushTranslation(s.x+currentX, s.y+currentY)
|
||||
err := s.animation.Render(target)
|
||||
target.Pop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
width, height := s.animation.GetCurrentFrameSize()
|
||||
maxHeight = d2common.MaxInt(maxHeight, height)
|
||||
currentX += width
|
||||
}
|
||||
|
||||
currentY += maxHeight
|
||||
}
|
||||
|
||||
return nil
|
||||
return renderSegmented(s.animation, s.segmentsX, s.segmentsY, s.frameOffset, target)
|
||||
}
|
||||
|
||||
func (s *Sprite) advance(elapsed float64) error {
|
||||
if s.animation == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.animation.Advance(elapsed)
|
||||
}
|
||||
|
||||
func (s *Sprite) getSize() (int, int) {
|
||||
if s.animation == nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
return s.animation.GetCurrentFrameSize()
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package d2gui
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
)
|
||||
|
||||
type FontStyle int
|
||||
@ -11,9 +10,11 @@ const (
|
||||
FontStyle16Units FontStyle = iota
|
||||
FontStyle30Units
|
||||
FontStyle42Units
|
||||
FontStyleExocet10
|
||||
FontStyleFormal10Static
|
||||
FontStyleFormal11Units
|
||||
FontStyleFormal12Static
|
||||
FontStyleRediculous
|
||||
)
|
||||
|
||||
type fontStyleConfig struct {
|
||||
@ -25,12 +26,37 @@ var fontStyleConfigs = map[FontStyle]fontStyleConfig{
|
||||
FontStyle16Units: {d2resource.Font16, d2resource.PaletteUnits},
|
||||
FontStyle30Units: {d2resource.Font30, d2resource.PaletteUnits},
|
||||
FontStyle42Units: {d2resource.Font42, d2resource.PaletteUnits},
|
||||
FontStyleExocet10: {d2resource.FontExocet10, d2resource.PaletteUnits},
|
||||
FontStyleFormal10Static: {d2resource.FontFormal10, d2resource.PaletteStatic},
|
||||
FontStyleFormal11Units: {d2resource.FontFormal11, d2resource.PaletteUnits},
|
||||
FontStyleFormal12Static: {d2resource.FontFormal12, d2resource.PaletteStatic},
|
||||
FontStyleRediculous: {d2resource.FontRediculous, d2resource.PaletteUnits},
|
||||
}
|
||||
|
||||
func loadFont(fontStyle FontStyle) (*d2asset.Font, error) {
|
||||
config := fontStyleConfigs[fontStyle]
|
||||
return d2asset.LoadFont(config.fontBasePath+".tbl", config.fontBasePath+".dc6", config.palettePath)
|
||||
type ButtonStyle int
|
||||
|
||||
type buttonStyleConfig struct {
|
||||
segmentsX int
|
||||
segmentsY int
|
||||
animationPath string
|
||||
palettePath string
|
||||
fontStyle FontStyle
|
||||
textOffset int
|
||||
}
|
||||
|
||||
const (
|
||||
ButtonStyleMedium ButtonStyle = iota
|
||||
ButtonStyleNarrow
|
||||
ButtonStyleOkCancel
|
||||
ButtonStyleShort
|
||||
ButtonStyleTall
|
||||
ButtonStyleWide
|
||||
)
|
||||
|
||||
var buttonStyleConfigs = map[ButtonStyle]buttonStyleConfig{
|
||||
ButtonStyleMedium: {1, 1, d2resource.MediumButtonBlank, d2resource.PaletteUnits, FontStyleExocet10, 0},
|
||||
ButtonStyleOkCancel: {1, 1, d2resource.CancelButton, d2resource.PaletteUnits, FontStyleRediculous, 0},
|
||||
ButtonStyleShort: {1, 1, d2resource.ShortButtonBlank, d2resource.PaletteUnits, FontStyleRediculous, -1},
|
||||
ButtonStyleTall: {1, 1, d2resource.TallButtonBlank, d2resource.PaletteUnits, FontStyleExocet10, 5},
|
||||
ButtonStyleWide: {2, 1, d2resource.WideButtonBlank, d2resource.PaletteUnits, FontStyleExocet10, 1},
|
||||
}
|
||||
|
@ -12,26 +12,30 @@ type widget interface {
|
||||
render(target d2render.Surface) error
|
||||
advance(elapsed float64) error
|
||||
|
||||
onMouseEnter(event d2input.MouseMoveEvent)
|
||||
onMouseLeave(event d2input.MouseMoveEvent)
|
||||
onMouseOver(event d2input.MouseMoveEvent)
|
||||
onMouseClick(event d2input.MouseEvent)
|
||||
onMouseMove(event d2input.MouseMoveEvent) bool
|
||||
onMouseEnter(event d2input.MouseMoveEvent) bool
|
||||
onMouseLeave(event d2input.MouseMoveEvent) bool
|
||||
onMouseOver(event d2input.MouseMoveEvent) bool
|
||||
onMouseButtonDown(event d2input.MouseEvent) bool
|
||||
onMouseButtonUp(event d2input.MouseEvent) bool
|
||||
onMouseButtonClick(event d2input.MouseEvent) bool
|
||||
|
||||
getPosition() (int, int)
|
||||
getSize() (int, int)
|
||||
getLayer() int
|
||||
isVisible() bool
|
||||
isExpanding() bool
|
||||
}
|
||||
|
||||
type widgetBase struct {
|
||||
x int
|
||||
y int
|
||||
layer int
|
||||
visible bool
|
||||
x int
|
||||
y int
|
||||
layer int
|
||||
visible bool
|
||||
expanding bool
|
||||
|
||||
mouseEnterHandler MouseMoveHandler
|
||||
mouseLeaveHandler MouseMoveHandler
|
||||
mouseMoveHandler MouseMoveHandler
|
||||
mouseClickHandler MouseHandler
|
||||
}
|
||||
|
||||
@ -48,6 +52,10 @@ func (w *widgetBase) SetVisible(visible bool) {
|
||||
w.visible = visible
|
||||
}
|
||||
|
||||
func (w *widgetBase) SetExpanding(expanding bool) {
|
||||
w.expanding = expanding
|
||||
}
|
||||
|
||||
func (w *widgetBase) SetMouseEnterHandler(handler MouseMoveHandler) {
|
||||
w.mouseEnterHandler = handler
|
||||
}
|
||||
@ -56,10 +64,6 @@ func (w *widgetBase) SetMouseLeaveHandler(handler MouseMoveHandler) {
|
||||
w.mouseLeaveHandler = handler
|
||||
}
|
||||
|
||||
func (w *widgetBase) SetMouseMoveHandler(handler MouseMoveHandler) {
|
||||
w.mouseMoveHandler = handler
|
||||
}
|
||||
|
||||
func (w *widgetBase) SetMouseClickHandler(handler MouseHandler) {
|
||||
w.mouseClickHandler = handler
|
||||
}
|
||||
@ -68,6 +72,10 @@ func (w *widgetBase) getPosition() (int, int) {
|
||||
return w.x, w.y
|
||||
}
|
||||
|
||||
func (w *widgetBase) getSize() (int, int) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func (w *widgetBase) getLayer() int {
|
||||
return w.layer
|
||||
}
|
||||
@ -76,30 +84,54 @@ func (w *widgetBase) isVisible() bool {
|
||||
return w.visible
|
||||
}
|
||||
|
||||
func (w *widgetBase) isExpanding() bool {
|
||||
return w.expanding
|
||||
}
|
||||
|
||||
func (w *widgetBase) render(target d2render.Surface) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *widgetBase) advance(elapsed float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseEnter(event d2input.MouseMoveEvent) {
|
||||
func (w *widgetBase) onMouseEnter(event d2input.MouseMoveEvent) bool {
|
||||
if w.mouseEnterHandler != nil {
|
||||
w.mouseEnterHandler(event)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseLeave(event d2input.MouseMoveEvent) {
|
||||
func (w *widgetBase) onMouseLeave(event d2input.MouseMoveEvent) bool {
|
||||
if w.mouseLeaveHandler != nil {
|
||||
w.mouseLeaveHandler(event)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseOver(event d2input.MouseMoveEvent) {
|
||||
if w.mouseMoveHandler != nil {
|
||||
w.mouseMoveHandler(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseClick(event d2input.MouseEvent) {
|
||||
func (w *widgetBase) onMouseButtonClick(event d2input.MouseEvent) bool {
|
||||
if w.mouseClickHandler != nil {
|
||||
w.mouseClickHandler(event)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseMove(event d2input.MouseMoveEvent) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseOver(event d2input.MouseMoveEvent) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseButtonDown(event d2input.MouseEvent) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *widgetBase) onMouseButtonUp(event d2input.MouseEvent) bool {
|
||||
return false
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ func Advance(elapsed float64) error {
|
||||
}
|
||||
|
||||
d2ui.Reset()
|
||||
d2gui.Clear()
|
||||
d2gui.SetLayout(nil)
|
||||
|
||||
if _, ok := singleton.nextScene.(SceneLoadHandler); ok {
|
||||
d2gui.ShowLoadScreen(0)
|
||||
|
49
d2game/d2gamescene/gui_testing.go
Normal file
49
d2game/d2gamescene/gui_testing.go
Normal file
@ -0,0 +1,49 @@
|
||||
package d2gamescene
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
|
||||
)
|
||||
|
||||
type GuiTestMain struct{}
|
||||
|
||||
func CreateGuiTestMain() *GuiTestMain {
|
||||
return &GuiTestMain{}
|
||||
}
|
||||
|
||||
func (g *GuiTestMain) OnLoad() error {
|
||||
layout := d2gui.CreateLayout(d2gui.PositionTypeHorizontal)
|
||||
//
|
||||
layoutLeft := layout.AddLayout(d2gui.PositionTypeVertical)
|
||||
layoutLeft.SetHorizontalAlign(d2gui.HorizontalAlignCenter)
|
||||
layoutLeft.AddLabel("FontStyle16Units", d2gui.FontStyle16Units)
|
||||
layoutLeft.AddSpacerStatic(0, 100)
|
||||
layoutLeft.AddLabel("FontStyle30Units", d2gui.FontStyle30Units)
|
||||
layoutLeft.AddLabel("FontStyle42Units", d2gui.FontStyle42Units)
|
||||
layoutLeft.AddLabel("FontStyleFormal10Static", d2gui.FontStyleFormal10Static)
|
||||
layoutLeft.AddLabel("FontStyleFormal11Units", d2gui.FontStyleFormal11Units)
|
||||
layoutLeft.AddLabel("FontStyleFormal12Static", d2gui.FontStyleFormal12Static)
|
||||
|
||||
layout.AddSpacerDynamic()
|
||||
|
||||
layoutRight := layout.AddLayout(d2gui.PositionTypeVertical)
|
||||
layoutRight.SetHorizontalAlign(d2gui.HorizontalAlignRight)
|
||||
layoutRight.AddButton("Medium", d2gui.ButtonStyleMedium)
|
||||
layoutRight.AddButton("Narrow", d2gui.ButtonStyleNarrow)
|
||||
layoutRight.AddButton("OkCancel", d2gui.ButtonStyleOkCancel)
|
||||
layoutRight.AddButton("Short", d2gui.ButtonStyleShort)
|
||||
layoutRight.AddButton("Wide", d2gui.ButtonStyleWide)
|
||||
|
||||
layout.SetVerticalAlign(d2gui.VerticalAlignMiddle)
|
||||
d2gui.SetLayout(layout)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GuiTestMain) Render(screen d2render.Surface) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GuiTestMain) Advance(tickTime float64) error {
|
||||
return nil
|
||||
}
|
3
main.go
3
main.go
@ -153,6 +153,9 @@ func initialize() error {
|
||||
d2term.BindAction("quit", "exits the game", func() {
|
||||
os.Exit(0)
|
||||
})
|
||||
d2term.BindAction("scene-gui", "enters the gui playground scene", func() {
|
||||
d2scene.SetNextScene(d2gamescene.CreateGuiTestMain())
|
||||
})
|
||||
|
||||
if err := d2asset.Initialize(); err != nil {
|
||||
return err
|
||||
|
Loading…
Reference in New Issue
Block a user