1
1
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:
Alex Yatskov 2020-02-24 19:35:21 -08:00 committed by GitHub
parent 2285c31b53
commit 6f2c212417
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 668 additions and 192 deletions

View File

@ -60,6 +60,10 @@ func loadFont(tablePath, spritePath, palettePath string) (*Font, error) {
return font, nil return font, nil
} }
func (f *Font) SetColor(color color.Color) {
f.color = color
}
func (f *Font) GetTextMetrics(text string) (int, int) { func (f *Font) GetTextMetrics(text string) (int, int) {
var ( var (
lineWidth int lineWidth int

131
d2core/d2gui/button.go Normal file
View 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
View 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
}

View File

@ -34,21 +34,14 @@ func Advance(elapsed float64) error {
return singleton.advance(elapsed) return singleton.advance(elapsed)
} }
func AddLayout() *Layout { func CreateLayout(positionType PositionType) *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() {
verifyWasInit() verifyWasInit()
singleton.clear() return createLayout(positionType)
}
func SetLayout(layout *Layout) {
verifyWasInit()
singleton.SetLayout(layout)
} }
func ShowLoadScreen(progress float64) { func ShowLoadScreen(progress float64) {

View File

@ -1,60 +1,41 @@
package d2gui package d2gui
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
) )
type Label struct { type Label struct {
widgetBase widgetBase
text string
font *d2asset.Font
surface d2render.Surface surface d2render.Surface
} }
func createLabel(text string, fontStyle FontStyle) *Label { func createLabel(text string, fontStyle FontStyle) (*Label, error) {
font, _ := loadFont(fontStyle) font, err := loadFont(fontStyle)
label := &Label{font: font} if err != nil {
label.SetText(text) return nil, err
label.visible = true }
return label
}
func (l *Label) SetText(text string) *Label { width, height := font.GetTextMetrics(text)
l.text = text surface, err := d2render.NewSurface(width, height, d2render.FilterNearest)
l.cache() if err != nil {
return l 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 { func (l *Label) render(target d2render.Surface) error {
if l.surface == nil {
return nil
}
return target.Render(l.surface) 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) { func (l *Label) getSize() (int, int) {
if l.surface == nil {
return 0, 0
}
return l.surface.GetSize() return l.surface.GetSize()
} }

View File

@ -3,6 +3,7 @@ package d2gui
import ( import (
"image/color" "image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
) )
@ -10,26 +11,136 @@ import (
type layoutEntry struct { type layoutEntry struct {
widget widget widget widget
x int
y int
width int
height int
mouseOver bool mouseOver bool
mouseDown [3]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 { type Layout struct {
widgetBase widgetBase
entries []*layoutEntry
width int
height int
verticalAlign VerticalAlign
horizontalAlign HorizontalAlign
positionType PositionType
entries []*layoutEntry
} }
func createLayout() *Layout { func createLayout(positionType PositionType) *Layout {
layout := new(Layout) layout := &Layout{positionType: positionType}
layout.visible = true layout.SetVisible(true)
return layout 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 { func (l *Layout) render(target d2render.Surface) error {
l.adjustEntryPlacement()
for _, entry := range l.entries { for _, entry := range l.entries {
if entry.widget.isVisible() { if !entry.widget.isVisible() {
l.renderWidget(entry.widget, target) continue
l.renderWidgetDebug(entry.widget, target) }
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 { func (l *Layout) advance(elapsed float64) error {
for _, entry := range l.entries { for _, entry := range l.entries {
if entry.widget.isVisible() { if err := entry.widget.advance(elapsed); err != nil {
if err := entry.widget.advance(elapsed); err != nil { return err
return err
}
} }
} }
return nil return nil
} }
func (l *Layout) renderWidget(widget widget, target d2render.Surface) { func (l *Layout) renderEntry(entry *layoutEntry, target d2render.Surface) error {
target.PushTranslation(widget.getPosition()) target.PushTranslation(entry.x, entry.y)
defer target.Pop() defer target.Pop()
widget.render(target) return entry.widget.render(target)
} }
func (l *Layout) renderWidgetDebug(widget widget, target d2render.Surface) { func (l *Layout) renderEntryDebug(entry *layoutEntry, target d2render.Surface) error {
target.PushTranslation(widget.getPosition()) target.PushTranslation(entry.x, entry.y)
defer target.Pop() 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(entry.width, 0, drawColor)
target.DrawLine(width, 0, drawColor) target.DrawLine(0, entry.height, drawColor)
target.DrawLine(0, height, drawColor)
target.PushTranslation(width, 0) target.PushTranslation(entry.width, 0)
target.DrawLine(0, height, drawColor) target.DrawLine(0, entry.height, drawColor)
target.Pop() target.Pop()
target.PushTranslation(0, height) target.PushTranslation(0, entry.height)
target.DrawLine(width, 0, drawColor) target.DrawLine(entry.width, 0, drawColor)
target.Pop() 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) { 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 { func (l *Layout) onMouseButtonDown(event d2input.MouseEvent) bool {
for _, entry := range l.entries { for _, entry := range l.entries {
eventLocal := event 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 entry.mouseDown[event.Button] = true
} }
} }
@ -94,9 +242,10 @@ func (l *Layout) onMouseButtonUp(event d2input.MouseEvent) bool {
for _, entry := range l.entries { for _, entry := range l.entries {
eventLocal := event eventLocal := event
if l.adjustEventCoords(entry.widget, &eventLocal.X, &eventLocal.Y) { if l.adjustEntryEvent(entry, &eventLocal.X, &eventLocal.Y) {
if entry.mouseDown[event.Button] { 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 { for _, entry := range l.entries {
eventLocal := event 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 { if entry.mouseOver {
entry.widget.onMouseOver(eventLocal) entry.widget.onMouseOver(eventLocal)
} else { } else {
@ -126,38 +276,90 @@ func (l *Layout) onMouseMove(event d2input.MouseMoveEvent) bool {
return false return false
} }
func (l *Layout) adjustEventCoords(widget widget, eventX, eventY *int) bool { func (l *Layout) adjustEntryEvent(entry *layoutEntry, eventX, eventY *int) bool {
x, y := widget.getPosition() *eventX -= entry.x
width, height := widget.getSize() *eventY -= entry.y
*eventX -= x if *eventX < 0 || *eventY < 0 || *eventX >= entry.width || *eventY >= entry.height {
*eventY -= y
if *eventX < 0 || *eventY < 0 || *eventX >= width || *eventY >= height {
return false return false
} }
return true return true
} }
func (l *Layout) addLayout() *Layout { func (l *Layout) adjustEntryPlacement() {
layout := createLayout() width, height := l.getSize()
l.entries = append(l.entries, &layoutEntry{widget: layout})
return layout
}
func (l *Layout) addSprite(imagePath, palettePath string) *Sprite { var expanderCount int
sprite := createSprite(imagePath, palettePath) for _, entry := range l.entries {
l.entries = append(l.entries, &layoutEntry{widget: sprite}) if entry.widget.isVisible() && entry.widget.isExpanding() {
return sprite expanderCount++
} }
}
func (l *Layout) addLabel(text string, fontStyle FontStyle) *Label { var expanderWidth, expanderHeight int
label := createLabel(text, fontStyle) if expanderCount > 0 {
l.entries = append(l.entries, &layoutEntry{widget: label}) contentWidth, contentHeight := l.getContentSize()
return label
}
func (l *Layout) clear() { switch l.positionType {
l.entries = nil 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
}
}
} }

View File

@ -11,7 +11,7 @@ import (
) )
type manager struct { type manager struct {
Layout layout *Layout
cursorAnim *d2asset.Animation cursorAnim *d2asset.Animation
cursorX int cursorX int
@ -46,19 +46,35 @@ func createGuiManager() (*manager, error) {
return manager, nil return manager, nil
} }
func (m *manager) SetLayout(layout *Layout) {
m.layout = layout
}
func (m *manager) OnMouseButtonDown(event d2input.MouseEvent) bool { 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 { 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 { func (m *manager) OnMouseMove(event d2input.MouseMoveEvent) bool {
m.cursorX = event.X m.cursorX = event.X
m.cursorY = event.Y 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 { 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 { if err := m.renderLoadScreen(target); err != nil {
return err return err
} }
} else { } else if m.layout != nil {
if err := m.Layout.render(target); err != nil { m.layout.SetSize(target.GetSize())
if err := m.layout.render(target); err != nil {
return err return err
} }
} }
@ -103,8 +120,8 @@ func (m *manager) renderCursor(target d2render.Surface) error {
} }
func (m *manager) advance(elapsed float64) error { func (m *manager) advance(elapsed float64) error {
if !m.loading { if !m.loading && m.layout != nil {
if err := m.Layout.advance(elapsed); err != nil { if err := m.layout.advance(elapsed); err != nil {
return err return err
} }
} }
@ -136,6 +153,6 @@ func (m *manager) hideCursor() {
} }
func (m *manager) clear() { func (m *manager) clear() {
m.Layout.clear() m.SetLayout(nil)
m.hideLoadScreen() m.hideLoadScreen()
} }

31
d2core/d2gui/spacer.go Normal file
View 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
}

View File

@ -1,7 +1,6 @@
package d2gui package d2gui
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
) )
@ -16,11 +15,17 @@ type Sprite struct {
animation *d2asset.Animation animation *d2asset.Animation
} }
func createSprite(imagePath, palettePath string) *Sprite { func createSprite(imagePath, palettePath string) (*Sprite, error) {
sprite := new(Sprite) animation, err := d2asset.LoadAnimation(imagePath, palettePath)
sprite.animation, _ = d2asset.LoadAnimation(imagePath, palettePath) if err != nil {
sprite.visible = true return nil, err
return sprite }
sprite := &Sprite{}
sprite.animation = animation
sprite.SetVisible(true)
return sprite, nil
} }
func (s *Sprite) SetSegmented(segmentsX, segmentsY, frameOffset int) { 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 { func (s *Sprite) render(target d2render.Surface) error {
if s.animation == nil { return renderSegmented(s.animation, s.segmentsX, s.segmentsY, s.frameOffset, target)
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
} }
func (s *Sprite) advance(elapsed float64) error { func (s *Sprite) advance(elapsed float64) error {
if s.animation == nil {
return nil
}
return s.animation.Advance(elapsed) return s.animation.Advance(elapsed)
} }
func (s *Sprite) getSize() (int, int) { func (s *Sprite) getSize() (int, int) {
if s.animation == nil {
return 0, 0
}
return s.animation.GetCurrentFrameSize() return s.animation.GetCurrentFrameSize()
} }

View File

@ -2,7 +2,6 @@ package d2gui
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
) )
type FontStyle int type FontStyle int
@ -11,9 +10,11 @@ const (
FontStyle16Units FontStyle = iota FontStyle16Units FontStyle = iota
FontStyle30Units FontStyle30Units
FontStyle42Units FontStyle42Units
FontStyleExocet10
FontStyleFormal10Static FontStyleFormal10Static
FontStyleFormal11Units FontStyleFormal11Units
FontStyleFormal12Static FontStyleFormal12Static
FontStyleRediculous
) )
type fontStyleConfig struct { type fontStyleConfig struct {
@ -25,12 +26,37 @@ var fontStyleConfigs = map[FontStyle]fontStyleConfig{
FontStyle16Units: {d2resource.Font16, d2resource.PaletteUnits}, FontStyle16Units: {d2resource.Font16, d2resource.PaletteUnits},
FontStyle30Units: {d2resource.Font30, d2resource.PaletteUnits}, FontStyle30Units: {d2resource.Font30, d2resource.PaletteUnits},
FontStyle42Units: {d2resource.Font42, d2resource.PaletteUnits}, FontStyle42Units: {d2resource.Font42, d2resource.PaletteUnits},
FontStyleExocet10: {d2resource.FontExocet10, d2resource.PaletteUnits},
FontStyleFormal10Static: {d2resource.FontFormal10, d2resource.PaletteStatic}, FontStyleFormal10Static: {d2resource.FontFormal10, d2resource.PaletteStatic},
FontStyleFormal11Units: {d2resource.FontFormal11, d2resource.PaletteUnits}, FontStyleFormal11Units: {d2resource.FontFormal11, d2resource.PaletteUnits},
FontStyleFormal12Static: {d2resource.FontFormal12, d2resource.PaletteStatic}, FontStyleFormal12Static: {d2resource.FontFormal12, d2resource.PaletteStatic},
FontStyleRediculous: {d2resource.FontRediculous, d2resource.PaletteUnits},
} }
func loadFont(fontStyle FontStyle) (*d2asset.Font, error) { type ButtonStyle int
config := fontStyleConfigs[fontStyle]
return d2asset.LoadFont(config.fontBasePath+".tbl", config.fontBasePath+".dc6", config.palettePath) 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},
} }

View File

@ -12,26 +12,30 @@ type widget interface {
render(target d2render.Surface) error render(target d2render.Surface) error
advance(elapsed float64) error advance(elapsed float64) error
onMouseEnter(event d2input.MouseMoveEvent) onMouseMove(event d2input.MouseMoveEvent) bool
onMouseLeave(event d2input.MouseMoveEvent) onMouseEnter(event d2input.MouseMoveEvent) bool
onMouseOver(event d2input.MouseMoveEvent) onMouseLeave(event d2input.MouseMoveEvent) bool
onMouseClick(event d2input.MouseEvent) onMouseOver(event d2input.MouseMoveEvent) bool
onMouseButtonDown(event d2input.MouseEvent) bool
onMouseButtonUp(event d2input.MouseEvent) bool
onMouseButtonClick(event d2input.MouseEvent) bool
getPosition() (int, int) getPosition() (int, int)
getSize() (int, int) getSize() (int, int)
getLayer() int getLayer() int
isVisible() bool isVisible() bool
isExpanding() bool
} }
type widgetBase struct { type widgetBase struct {
x int x int
y int y int
layer int layer int
visible bool visible bool
expanding bool
mouseEnterHandler MouseMoveHandler mouseEnterHandler MouseMoveHandler
mouseLeaveHandler MouseMoveHandler mouseLeaveHandler MouseMoveHandler
mouseMoveHandler MouseMoveHandler
mouseClickHandler MouseHandler mouseClickHandler MouseHandler
} }
@ -48,6 +52,10 @@ func (w *widgetBase) SetVisible(visible bool) {
w.visible = visible w.visible = visible
} }
func (w *widgetBase) SetExpanding(expanding bool) {
w.expanding = expanding
}
func (w *widgetBase) SetMouseEnterHandler(handler MouseMoveHandler) { func (w *widgetBase) SetMouseEnterHandler(handler MouseMoveHandler) {
w.mouseEnterHandler = handler w.mouseEnterHandler = handler
} }
@ -56,10 +64,6 @@ func (w *widgetBase) SetMouseLeaveHandler(handler MouseMoveHandler) {
w.mouseLeaveHandler = handler w.mouseLeaveHandler = handler
} }
func (w *widgetBase) SetMouseMoveHandler(handler MouseMoveHandler) {
w.mouseMoveHandler = handler
}
func (w *widgetBase) SetMouseClickHandler(handler MouseHandler) { func (w *widgetBase) SetMouseClickHandler(handler MouseHandler) {
w.mouseClickHandler = handler w.mouseClickHandler = handler
} }
@ -68,6 +72,10 @@ func (w *widgetBase) getPosition() (int, int) {
return w.x, w.y return w.x, w.y
} }
func (w *widgetBase) getSize() (int, int) {
return 0, 0
}
func (w *widgetBase) getLayer() int { func (w *widgetBase) getLayer() int {
return w.layer return w.layer
} }
@ -76,30 +84,54 @@ func (w *widgetBase) isVisible() bool {
return w.visible 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 { func (w *widgetBase) advance(elapsed float64) error {
return nil return nil
} }
func (w *widgetBase) onMouseEnter(event d2input.MouseMoveEvent) { func (w *widgetBase) onMouseEnter(event d2input.MouseMoveEvent) bool {
if w.mouseEnterHandler != nil { if w.mouseEnterHandler != nil {
w.mouseEnterHandler(event) w.mouseEnterHandler(event)
} }
return false
} }
func (w *widgetBase) onMouseLeave(event d2input.MouseMoveEvent) { func (w *widgetBase) onMouseLeave(event d2input.MouseMoveEvent) bool {
if w.mouseLeaveHandler != nil { if w.mouseLeaveHandler != nil {
w.mouseLeaveHandler(event) w.mouseLeaveHandler(event)
} }
return false
} }
func (w *widgetBase) onMouseOver(event d2input.MouseMoveEvent) { func (w *widgetBase) onMouseButtonClick(event d2input.MouseEvent) bool {
if w.mouseMoveHandler != nil {
w.mouseMoveHandler(event)
}
}
func (w *widgetBase) onMouseClick(event d2input.MouseEvent) {
if w.mouseClickHandler != nil { if w.mouseClickHandler != nil {
w.mouseClickHandler(event) 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
} }

View File

@ -43,7 +43,7 @@ func Advance(elapsed float64) error {
} }
d2ui.Reset() d2ui.Reset()
d2gui.Clear() d2gui.SetLayout(nil)
if _, ok := singleton.nextScene.(SceneLoadHandler); ok { if _, ok := singleton.nextScene.(SceneLoadHandler); ok {
d2gui.ShowLoadScreen(0) d2gui.ShowLoadScreen(0)

View 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
}

View File

@ -153,6 +153,9 @@ func initialize() error {
d2term.BindAction("quit", "exits the game", func() { d2term.BindAction("quit", "exits the game", func() {
os.Exit(0) os.Exit(0)
}) })
d2term.BindAction("scene-gui", "enters the gui playground scene", func() {
d2scene.SetNextScene(d2gamescene.CreateGuiTestMain())
})
if err := d2asset.Initialize(); err != nil { if err := d2asset.Initialize(); err != nil {
return err return err