From 810b168ebfc1b5ed8d216904c5e8a314ab9f76e1 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 17 Feb 2020 19:11:52 -0800 Subject: [PATCH] Work in progress on GUI (#304) * Work in progress on GUI refactor * Remove WIP file * Remove WIP style --- d2core/d2asset/asset_manager.go | 1 + d2core/d2asset/d2asset.go | 19 +++- d2core/d2asset/font.go | 126 ++++++++++++++++++++++++ d2core/d2asset/font_manager.go | 37 ++++++++ d2core/d2gui/d2gui.go | 14 ++- d2core/d2gui/gui_manager.go | 125 ------------------------ d2core/d2gui/label.go | 60 ++++++++++++ d2core/d2gui/layout.go | 163 ++++++++++++++++++++++++++++++++ d2core/d2gui/manager.go | 141 +++++++++++++++++++++++++++ d2core/d2gui/sprite.go | 58 ++++-------- d2core/d2gui/style.go | 36 +++++++ d2core/d2gui/widget.go | 105 ++++++++++++++++++++ d2core/d2gui/widget_manager.go | 13 --- 13 files changed, 717 insertions(+), 181 deletions(-) create mode 100644 d2core/d2asset/font.go create mode 100644 d2core/d2asset/font_manager.go delete mode 100644 d2core/d2gui/gui_manager.go create mode 100644 d2core/d2gui/label.go create mode 100644 d2core/d2gui/layout.go create mode 100644 d2core/d2gui/manager.go create mode 100644 d2core/d2gui/style.go create mode 100644 d2core/d2gui/widget.go delete mode 100644 d2core/d2gui/widget_manager.go diff --git a/d2core/d2asset/asset_manager.go b/d2core/d2asset/asset_manager.go index 22f4e9430..9f9dc94b6 100644 --- a/d2core/d2asset/asset_manager.go +++ b/d2core/d2asset/asset_manager.go @@ -19,6 +19,7 @@ type assetManager struct { fileManager *fileManager paletteManager *paletteManager animationManager *animationManager + fontManager *fontManager } func loadPalette(palettePath string) (*d2datadict.PaletteRec, error) { diff --git a/d2core/d2asset/d2asset.go b/d2core/d2asset/d2asset.go index e087ce024..d26cb99a1 100644 --- a/d2core/d2asset/d2asset.go +++ b/d2core/d2asset/d2asset.go @@ -21,6 +21,7 @@ func Initialize() error { fileManager = createFileManager(config, archiveManager) paletteManager = createPaletteManager() animationManager = createAnimationManager() + fontManager = createFontManager() ) singleton = &assetManager{ @@ -28,6 +29,7 @@ func Initialize() error { fileManager, paletteManager, animationManager, + fontManager, } d2term.BindAction("assetspam", "display verbose asset manager logs", func(verbose bool) { @@ -44,10 +46,11 @@ func Initialize() error { }) d2term.BindAction("assetstat", "display asset manager cache statistics", func() { - d2term.OutputInfo("archive cache: %f%%", float64(archiveManager.cache.GetWeight())/float64(archiveManager.cache.GetBudget())*100.0) - d2term.OutputInfo("file cache: %f%%", float64(fileManager.cache.GetWeight())/float64(fileManager.cache.GetBudget())*100.0) - d2term.OutputInfo("palette cache: %f%%", float64(paletteManager.cache.GetWeight())/float64(paletteManager.cache.GetBudget())*100.0) - d2term.OutputInfo("animation cache: %f%%", float64(animationManager.cache.GetWeight())/float64(animationManager.cache.GetBudget())*100.0) + d2term.OutputInfo("archive cache: %f", float64(archiveManager.cache.GetWeight())/float64(archiveManager.cache.GetBudget())*100.0) + d2term.OutputInfo("file cache: %f", float64(fileManager.cache.GetWeight())/float64(fileManager.cache.GetBudget())*100.0) + d2term.OutputInfo("palette cache: %f", float64(paletteManager.cache.GetWeight())/float64(paletteManager.cache.GetBudget())*100.0) + d2term.OutputInfo("animation cache: %f", float64(animationManager.cache.GetWeight())/float64(animationManager.cache.GetBudget())*100.0) + d2term.OutputInfo("font cache: %f", float64(fontManager.cache.GetWeight())/float64(fontManager.cache.GetBudget())*100.0) }) d2term.BindAction("assetclear", "clear asset manager cache", func() { @@ -55,6 +58,7 @@ func Initialize() error { fileManager.cache.Clear() paletteManager.cache.Clear() animationManager.cache.Clear() + fontManager.cache.Clear() }) return nil @@ -86,6 +90,7 @@ func FileExists(filePath string) (bool, error) { } func LoadAnimation(animationPath, palettePath string) (*Animation, error) { + verifyWasInit() return LoadAnimationWithTransparency(animationPath, palettePath, 255) } @@ -95,9 +100,15 @@ func LoadAnimationWithTransparency(animationPath, palettePath string, transparen } func LoadComposite(object *d2datadict.ObjectLookupRecord, palettePath string) (*Composite, error) { + verifyWasInit() return CreateComposite(object, palettePath), nil } +func LoadFont(tablePath, spritePath, palettePath string) (*Font, error) { + verifyWasInit() + return singleton.fontManager.loadFont(tablePath, spritePath, palettePath) +} + func verifyWasInit() { if singleton == nil { panic(ErrNotInit) diff --git a/d2core/d2asset/font.go b/d2core/d2asset/font.go new file mode 100644 index 000000000..772a490d7 --- /dev/null +++ b/d2core/d2asset/font.go @@ -0,0 +1,126 @@ +package d2asset + +import ( + "encoding/binary" + "errors" + "image/color" + "strings" + + "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" +) + +type fontGlyph struct { + frame int + width int + height int +} + +type Font struct { + sheet *Animation + glyphs map[rune]fontGlyph + color color.Color +} + +func loadFont(tablePath, spritePath, palettePath string) (*Font, error) { + sheet, err := LoadAnimation(spritePath, palettePath) + if err != nil { + return nil, err + } + + data, err := LoadFile(tablePath) + if err != nil { + return nil, err + } + + if string(data[:5]) != "Woo!\x01" { + return nil, errors.New("invalid font table format") + } + + _, maxCharHeight := sheet.GetFrameBounds() + + glyphs := make(map[rune]fontGlyph) + for i := 12; i < len(data); i += 14 { + code := rune(binary.LittleEndian.Uint16(data[i : i+2])) + + var glyph fontGlyph + glyph.frame = int(binary.LittleEndian.Uint16(data[i+8 : i+10])) + glyph.width = int(data[i+3]) + glyph.height = maxCharHeight // int(data[i+4]) + + glyphs[code] = glyph + } + + font := &Font{ + sheet: sheet, + glyphs: glyphs, + color: color.White, + } + + return font, nil +} + +func (f *Font) GetTextMetrics(text string) (int, int) { + var ( + lineWidth int + lineHeight int + totalWidth int + totalHeight int + ) + + for _, c := range text { + if c == '\n' { + totalWidth = d2common.MaxInt(totalWidth, lineWidth) + totalHeight += lineHeight + lineWidth = 0 + lineHeight = 0 + } else if glyph, ok := f.glyphs[c]; ok { + lineWidth += glyph.width + lineHeight = d2common.MaxInt(lineHeight, glyph.height) + } + } + + totalWidth = d2common.MaxInt(totalWidth, lineWidth) + totalHeight += lineHeight + + return totalWidth, totalHeight +} + +func (f *Font) Clone() *Font { + return &Font{ + sheet: f.sheet, + glyphs: f.glyphs, + color: f.color, + } +} + +func (f *Font) RenderText(text string, target d2render.Surface) error { + f.sheet.SetColorMod(f.color) + f.sheet.SetBlend(false) + + lines := strings.Split(text, "\n") + + for _, line := range lines { + var ( + lineHeight int + lineLength int + ) + + for _, c := range line { + if glyph, ok := f.glyphs[c]; ok { + f.sheet.SetCurrentFrame(glyph.frame) + f.sheet.Render(target) + lineHeight = d2common.MaxInt(lineHeight, glyph.height) + target.PushTranslation(glyph.width, 0) + lineLength++ + } + } + + target.PopN(lineLength) + target.PushTranslation(0, lineHeight) + } + + target.PopN(len(lines)) + + return nil +} diff --git a/d2core/d2asset/font_manager.go b/d2core/d2asset/font_manager.go new file mode 100644 index 000000000..1d4fca605 --- /dev/null +++ b/d2core/d2asset/font_manager.go @@ -0,0 +1,37 @@ +package d2asset + +import ( + "fmt" + + "github.com/OpenDiablo2/OpenDiablo2/d2common" +) + +const ( + fontBudget = 64 +) + +type fontManager struct { + cache *d2common.Cache +} + +func createFontManager() *fontManager { + return &fontManager{d2common.CreateCache(fontBudget)} +} + +func (fm *fontManager) loadFont(tablePath, spritePath, palettePath string) (*Font, error) { + cachePath := fmt.Sprintf("%s;%s;%s", tablePath, spritePath, palettePath) + if font, found := fm.cache.Retrieve(cachePath); found { + return font.(*Font).Clone(), nil + } + + font, err := loadFont(tablePath, spritePath, palettePath) + if err != nil { + return nil, err + } + + if err := fm.cache.Insert(cachePath, font.Clone(), 1); err != nil { + return nil, err + } + + return font, nil +} diff --git a/d2core/d2gui/d2gui.go b/d2core/d2gui/d2gui.go index 7bc7b5a6a..a9881a4e7 100644 --- a/d2core/d2gui/d2gui.go +++ b/d2core/d2gui/d2gui.go @@ -11,7 +11,7 @@ var ( ErrNotInit = errors.New("gui system is not initialized") ) -var singleton *guiManager +var singleton *manager func Initialize() error { verifyNotInit() @@ -34,6 +34,18 @@ 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() { verifyWasInit() singleton.clear() diff --git a/d2core/d2gui/gui_manager.go b/d2core/d2gui/gui_manager.go deleted file mode 100644 index a812d3fd8..000000000 --- a/d2core/d2gui/gui_manager.go +++ /dev/null @@ -1,125 +0,0 @@ -package d2gui - -import ( - "image/color" - "math" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" -) - -type guiWidget interface { - getLayer() int - render(target d2render.Surface) error - advance(elapsed float64) error -} - -type guiManager struct { - cursorSprite *Sprite - loadSprite *Sprite - widgets []guiWidget - loading bool -} - -func createGuiManager() (*guiManager, error) { - cursorSprite, err := CreateSprite(d2resource.CursorDefault, d2resource.PaletteUnits) - if err != nil { - return nil, err - } - - loadSprite, err := CreateSprite(d2resource.LoadingScreen, d2resource.PaletteLoading) - if err != nil { - return nil, err - } - - width, height := loadSprite.getSize() - loadSprite.SetPosition(400-width/2, 300+height/2) - - manager := &guiManager{ - cursorSprite: cursorSprite, - loadSprite: loadSprite, - } - - if err := d2input.BindHandler(manager); err != nil { - return nil, err - } - - return manager, nil -} - -func (gui *guiManager) OnMouseMove(event d2input.MouseMoveEvent) bool { - gui.cursorSprite.SetPosition(event.X, event.Y) - return false -} - -func (gui *guiManager) render(target d2render.Surface) error { - if gui.loading { - target.Clear(color.Black) - if err := gui.loadSprite.render(target); err != nil { - return err - } - } else { - for _, widget := range gui.widgets { - if err := widget.render(target); err != nil { - return err - } - } - } - - if err := gui.cursorSprite.render(target); err != nil { - return err - } - - return nil -} - -func (gui *guiManager) advance(elapsed float64) error { - if gui.loading { - gui.loadSprite.Show() - if err := gui.loadSprite.advance(elapsed); err != nil { - return err - } - } else { - gui.loadSprite.Hide() - for _, widget := range gui.widgets { - if err := widget.advance(elapsed); err != nil { - return err - } - } - } - - if err := gui.loadSprite.advance(elapsed); err != nil { - return err - } - - return nil -} - -func (gui *guiManager) showLoadScreen(progress float64) { - progress = math.Min(progress, 1.0) - progress = math.Max(progress, 0.0) - - animation := gui.loadSprite.animation - frameCount := animation.GetFrameCount() - animation.SetCurrentFrame(int(float64(frameCount-1.0) * progress)) - - gui.loading = true -} - -func (gui *guiManager) hideLoadScreen() { - gui.loading = false -} - -func (gui *guiManager) showCursor() { - gui.cursorSprite.Show() -} - -func (gui *guiManager) hideCursor() { - gui.cursorSprite.Hide() -} - -func (gui *guiManager) clear() { - gui.widgets = nil - gui.hideLoadScreen() -} diff --git a/d2core/d2gui/label.go b/d2core/d2gui/label.go new file mode 100644 index 000000000..341252ce9 --- /dev/null +++ b/d2core/d2gui/label.go @@ -0,0 +1,60 @@ +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 (l *Label) SetText(text string) *Label { + l.text = text + l.cache() + return l +} + +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() +} diff --git a/d2core/d2gui/layout.go b/d2core/d2gui/layout.go new file mode 100644 index 000000000..67530385d --- /dev/null +++ b/d2core/d2gui/layout.go @@ -0,0 +1,163 @@ +package d2gui + +import ( + "image/color" + + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" +) + +type layoutEntry struct { + widget widget + + mouseOver bool + mouseDown [3]bool +} + +type Layout struct { + widgetBase + entries []*layoutEntry +} + +func createLayout() *Layout { + layout := new(Layout) + layout.visible = true + return layout +} + +func (l *Layout) render(target d2render.Surface) error { + for _, entry := range l.entries { + if entry.widget.isVisible() { + l.renderWidget(entry.widget, target) + l.renderWidgetDebug(entry.widget, target) + } + } + + return nil +} + +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 + } + } + } + + return nil +} + +func (l *Layout) renderWidget(widget widget, target d2render.Surface) { + target.PushTranslation(widget.getPosition()) + defer target.Pop() + + widget.render(target) +} + +func (l *Layout) renderWidgetDebug(widget widget, target d2render.Surface) { + target.PushTranslation(widget.getPosition()) + defer target.Pop() + + drawColor := color.RGBA{R: 0x00, G: 0x00, B: 0xff, A: 0xb0} + + width, height := widget.getSize() + target.DrawLine(width, 0, drawColor) + target.DrawLine(0, height, drawColor) + + target.PushTranslation(width, 0) + target.DrawLine(0, height, drawColor) + target.Pop() + + target.PushTranslation(0, height) + target.DrawLine(width, 0, drawColor) + target.Pop() +} + +func (l *Layout) getSize() (int, int) { + return 0, 0 +} + +func (l *Layout) onMouseButtonDown(event d2input.MouseEvent) bool { + for _, entry := range l.entries { + eventLocal := event + + if l.adjustEventCoords(entry.widget, &eventLocal.X, &eventLocal.Y) { + entry.mouseDown[event.Button] = true + } + } + + return false +} + +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 entry.mouseDown[event.Button] { + entry.widget.onMouseClick(eventLocal) + } + } + + entry.mouseDown[event.Button] = false + } + + return false +} + +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 entry.mouseOver { + entry.widget.onMouseOver(eventLocal) + } else { + entry.widget.onMouseEnter(eventLocal) + } + entry.mouseOver = true + } else if entry.mouseOver { + entry.widget.onMouseLeave(eventLocal) + entry.mouseOver = false + } + } + + return false +} + +func (l *Layout) adjustEventCoords(widget widget, eventX, eventY *int) bool { + x, y := widget.getPosition() + width, height := widget.getSize() + + *eventX -= x + *eventY -= y + + if *eventX < 0 || *eventY < 0 || *eventX >= width || *eventY >= 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) addSprite(imagePath, palettePath string) *Sprite { + sprite := createSprite(imagePath, palettePath) + l.entries = append(l.entries, &layoutEntry{widget: sprite}) + return sprite +} + +func (l *Layout) addLabel(text string, fontStyle FontStyle) *Label { + label := createLabel(text, fontStyle) + l.entries = append(l.entries, &layoutEntry{widget: label}) + return label +} + +func (l *Layout) clear() { + l.entries = nil +} diff --git a/d2core/d2gui/manager.go b/d2core/d2gui/manager.go new file mode 100644 index 000000000..1e94a4b7e --- /dev/null +++ b/d2core/d2gui/manager.go @@ -0,0 +1,141 @@ +package d2gui + +import ( + "image/color" + "math" + + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" +) + +type manager struct { + Layout + + cursorAnim *d2asset.Animation + cursorX int + cursorY int + cursorVisible bool + + loadingAnim *d2asset.Animation + loading bool +} + +func createGuiManager() (*manager, error) { + cursorAnim, err := d2asset.LoadAnimation(d2resource.CursorDefault, d2resource.PaletteUnits) + if err != nil { + return nil, err + } + + loadingAnim, err := d2asset.LoadAnimation(d2resource.LoadingScreen, d2resource.PaletteLoading) + if err != nil { + return nil, err + } + + manager := &manager{ + cursorAnim: cursorAnim, + loadingAnim: loadingAnim, + cursorVisible: true, + } + + if err := d2input.BindHandler(manager); err != nil { + return nil, err + } + + return manager, nil +} + +func (m *manager) OnMouseButtonDown(event d2input.MouseEvent) bool { + return m.Layout.onMouseButtonDown(event) +} + +func (m *manager) OnMouseButtonUp(event d2input.MouseEvent) bool { + 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) +} + +func (m *manager) render(target d2render.Surface) error { + if m.loading { + if err := m.renderLoadScreen(target); err != nil { + return err + } + } else { + if err := m.Layout.render(target); err != nil { + return err + } + } + + if m.cursorVisible { + if err := m.renderCursor(target); err != nil { + return err + } + } + + return nil +} + +func (m *manager) renderLoadScreen(target d2render.Surface) error { + target.Clear(color.Black) + + screenWidth, screenHeight := target.GetSize() + animWidth, animHeight := m.loadingAnim.GetCurrentFrameSize() + target.PushTranslation(screenWidth/2-animWidth/2, screenHeight/2+animHeight/2) + target.PushTranslation(0, -animHeight) + defer target.PopN(2) + + return m.loadingAnim.Render(target) +} + +func (m *manager) renderCursor(target d2render.Surface) error { + _, height := m.cursorAnim.GetCurrentFrameSize() + target.PushTranslation(m.cursorX, m.cursorY) + target.PushTranslation(0, -height) + defer target.PopN(2) + + return m.cursorAnim.Render(target) +} + +func (m *manager) advance(elapsed float64) error { + if !m.loading { + if err := m.Layout.advance(elapsed); err != nil { + return err + } + } + + return nil +} + +func (m *manager) showLoadScreen(progress float64) { + progress = math.Min(progress, 1.0) + progress = math.Max(progress, 0.0) + + animation := m.loadingAnim + frameCount := animation.GetFrameCount() + animation.SetCurrentFrame(int(float64(frameCount-1.0) * progress)) + + m.loading = true +} + +func (m *manager) hideLoadScreen() { + m.loading = false +} + +func (m *manager) showCursor() { + m.cursorVisible = true +} + +func (m *manager) hideCursor() { + m.cursorVisible = false +} + +func (m *manager) clear() { + m.Layout.clear() + m.hideLoadScreen() +} diff --git a/d2core/d2gui/sprite.go b/d2core/d2gui/sprite.go index 14d7c8560..e366b5c85 100644 --- a/d2core/d2gui/sprite.go +++ b/d2core/d2gui/sprite.go @@ -7,29 +7,20 @@ import ( ) type Sprite struct { - x int - y int + widgetBase segmentsX int segmentsY int frameOffset int - visible bool animation *d2asset.Animation } -func CreateSprite(imagePath, palettePath string) (*Sprite, error) { - animation, err := d2asset.LoadAnimation(imagePath, palettePath) - if err != nil { - return nil, err - } - - sprite := &Sprite{ - animation: animation, - visible: true, - } - - return sprite, nil +func createSprite(imagePath, palettePath string) *Sprite { + sprite := new(Sprite) + sprite.animation, _ = d2asset.LoadAnimation(imagePath, palettePath) + sprite.visible = true + return sprite } func (s *Sprite) SetSegmented(segmentsX, segmentsY, frameOffset int) { @@ -38,34 +29,13 @@ func (s *Sprite) SetSegmented(segmentsX, segmentsY, frameOffset int) { s.frameOffset = frameOffset } -func (s *Sprite) SetPosition(x, y int) { - s.x = x - s.y = y -} - -func (s *Sprite) Show() { - s.visible = true -} - -func (s *Sprite) Hide() { - s.visible = false -} - -func (s *Sprite) getPosition() (int, int) { - return s.x, s.y -} - -func (s *Sprite) getSize() (int, int) { - return s.animation.GetCurrentFrameSize() -} - func (s *Sprite) render(target d2render.Surface) error { - if !s.visible { + if s.animation == nil { return nil } _, height := s.animation.GetCurrentFrameSize() - target.PushTranslation(s.x, s.y-height) + target.PushTranslation(0, -height) defer target.Pop() if s.segmentsX == 0 && s.segmentsY == 0 { @@ -100,5 +70,17 @@ func (s *Sprite) render(target d2render.Surface) error { } 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() +} diff --git a/d2core/d2gui/style.go b/d2core/d2gui/style.go new file mode 100644 index 000000000..2d0a8de4b --- /dev/null +++ b/d2core/d2gui/style.go @@ -0,0 +1,36 @@ +package d2gui + +import ( + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" +) + +type FontStyle int + +const ( + FontStyle16Units FontStyle = iota + FontStyle30Units + FontStyle42Units + FontStyleFormal10Static + FontStyleFormal11Units + FontStyleFormal12Static +) + +type fontStyleConfig struct { + fontBasePath string + palettePath string +} + +var fontStyleConfigs = map[FontStyle]fontStyleConfig{ + FontStyle16Units: {d2resource.Font16, d2resource.PaletteUnits}, + FontStyle30Units: {d2resource.Font30, d2resource.PaletteUnits}, + FontStyle42Units: {d2resource.Font42, d2resource.PaletteUnits}, + FontStyleFormal10Static: {d2resource.FontFormal10, d2resource.PaletteStatic}, + FontStyleFormal11Units: {d2resource.FontFormal11, d2resource.PaletteUnits}, + FontStyleFormal12Static: {d2resource.FontFormal12, d2resource.PaletteStatic}, +} + +func loadFont(fontStyle FontStyle) (*d2asset.Font, error) { + config := fontStyleConfigs[fontStyle] + return d2asset.LoadFont(config.fontBasePath+".tbl", config.fontBasePath+".dc6", config.palettePath) +} diff --git a/d2core/d2gui/widget.go b/d2core/d2gui/widget.go new file mode 100644 index 000000000..58f5c56c2 --- /dev/null +++ b/d2core/d2gui/widget.go @@ -0,0 +1,105 @@ +package d2gui + +import ( + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" +) + +type MouseHandler func(d2input.MouseEvent) +type MouseMoveHandler func(d2input.MouseMoveEvent) + +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) + + getPosition() (int, int) + getSize() (int, int) + getLayer() int + isVisible() bool +} + +type widgetBase struct { + x int + y int + layer int + visible bool + + mouseEnterHandler MouseMoveHandler + mouseLeaveHandler MouseMoveHandler + mouseMoveHandler MouseMoveHandler + mouseClickHandler MouseHandler +} + +func (w *widgetBase) SetPosition(x, y int) { + w.x = x + w.y = y +} + +func (w *widgetBase) SetLayer(layer int) { + w.layer = layer +} + +func (w *widgetBase) SetVisible(visible bool) { + w.visible = visible +} + +func (w *widgetBase) SetMouseEnterHandler(handler MouseMoveHandler) { + w.mouseEnterHandler = handler +} + +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 +} + +func (w *widgetBase) getPosition() (int, int) { + return w.x, w.y +} + +func (w *widgetBase) getLayer() int { + return w.layer +} + +func (w *widgetBase) isVisible() bool { + return w.visible +} + +func (w *widgetBase) advance(elapsed float64) error { + return nil +} + +func (w *widgetBase) onMouseEnter(event d2input.MouseMoveEvent) { + if w.mouseEnterHandler != nil { + w.mouseEnterHandler(event) + } +} + +func (w *widgetBase) onMouseLeave(event d2input.MouseMoveEvent) { + if w.mouseLeaveHandler != nil { + w.mouseLeaveHandler(event) + } +} + +func (w *widgetBase) onMouseOver(event d2input.MouseMoveEvent) { + if w.mouseMoveHandler != nil { + w.mouseMoveHandler(event) + } +} + +func (w *widgetBase) onMouseClick(event d2input.MouseEvent) { + if w.mouseClickHandler != nil { + w.mouseClickHandler(event) + } +} diff --git a/d2core/d2gui/widget_manager.go b/d2core/d2gui/widget_manager.go deleted file mode 100644 index 13fccf6a8..000000000 --- a/d2core/d2gui/widget_manager.go +++ /dev/null @@ -1,13 +0,0 @@ -package d2gui - -import ( - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" -) - -type widget interface { - Render(target d2render.Surface) error - Advance(elapsed float64) error -} - -type widgetManager struct { -}