1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-19 21:55:24 +00:00
OpenDiablo2/d2core/d2ui/textbox.go

233 lines
4.5 KiB
Go
Raw Normal View History

package d2ui
import (
"strings"
"time"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
)
// TextBox represents a text input box
type TextBox struct {
manager *UIManager
textLabel *Label
lineBar *Label
text string
filter string
x int
y int
bgSprite *Sprite
visible bool
enabled bool
isFocused bool
}
// NewTextbox creates a new instance of a text box
func (ui *UIManager) NewTextbox() *TextBox {
Decouple asset manager from renderer (#730) * improve AssetManager implementation Notable changes are: * removed the individual managers inside of d2asset, only one asset manager * AssetManager now has caches for the types of files it loads * created a type for TextDictionary (the txt file structs) * fixed a file path bug in d2loader Source * fixed a asset stream bug in d2loader Asset * d2loader.Loader now needs a d2config.Config on creation (for resolving locale files) * updated the mpq file in d2asset test data, added test case for "sub-directory" * added a Data method to d2asset.Asset. The data is cached on first full read. * renamed ArchiveDataStream to DataStream in d2interface * moved palette utility func out of d2asset and into d2util * bugfix for MacOS mpq loader issue * lint fixes, added data caching to filesystem asset * adding comment for mpq asset close * Decouple d2asset from d2render Notable changes in d2common: * d2dcc.Load now fully decodes the dcc and stores the directions/frames in the dcc struct * un-exported dcc.decodeDirection, it is only used in d2dcc * removed font interface from d2interface, we only have one font implementation * added `Renderer` method to d2interface.Surface, animations use this to bind to a renderer and create surfaces as they need * added `BindRenderer` method to animation interface Notable changes in d2common/d2asset: * **d2asset.NewAssetManager only needs to be passed a d2config.Config**, it is decoupled from d2render * exported Animation * Animation implementation binds to the renderer to create surfaces only on the first time it is rendered * font, dcc, dc6 initialization logic moved out of asset_manager.go * for dc6 and dcc animations, the process of decoding and creating render surfaces has been broken into different methods * the d2asset.Font struct now stores font table data for initialization purposes Notable changes in d2core/d2render: * Surfaces store a renderer reference, this allows animations to bind to the renderer and create a surface just-in-time **These last changes should have been a separate PR, sorry.** Notable changes in d2core/d2ui: * ui.NewSprite now handles creating an animation internally, only needs image and palette path as arguments Notable Changes in d2game: Because of the change in d2ui, all instances of this code pattern... ```golang animation, err := screen.asset.LoadAnimation(imgPath, palettePath) sprite, err := screen.ui.NewSprite(animation) ``` ... becomes this ... ```golang sprite, err := screen.ui.NewSprite(imgPath, palettePath) ```
2020-09-14 21:31:45 +00:00
bgSprite, _ := ui.NewSprite(d2resource.TextBox2, d2resource.PaletteUnits)
tb := &TextBox{
2020-06-18 18:11:04 +00:00
filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
bgSprite: bgSprite,
textLabel: ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
lineBar: ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
enabled: true,
visible: true,
}
tb.lineBar.SetText("_")
ui.addWidget(tb)
return tb
}
2020-07-26 18:52:54 +00:00
// SetFilter sets the text box filter
2020-06-18 18:11:04 +00:00
func (v *TextBox) SetFilter(filter string) {
v.filter = filter
}
2020-07-26 18:52:54 +00:00
// Render renders the text box
func (v *TextBox) Render(target d2interface.Surface) error {
if !v.visible {
2020-07-26 18:52:54 +00:00
return nil
}
2020-07-26 18:52:54 +00:00
if err := v.bgSprite.Render(target); err != nil {
return err
}
v.textLabel.Render(target)
2020-07-26 18:52:54 +00:00
if (time.Now().UnixNano()/1e6)&(1<<8) > 0 {
v.lineBar.Render(target)
}
2020-07-26 18:52:54 +00:00
return nil
}
// bindManager binds the textbox to the UI manager
func (v *TextBox) bindManager(manager *UIManager) {
v.manager = manager
}
2020-07-26 18:52:54 +00:00
// OnKeyChars handles key character events
func (v *TextBox) OnKeyChars(event d2interface.KeyCharsEvent) bool {
if !v.isFocused || !v.visible || !v.enabled {
return false
}
2020-07-26 18:52:54 +00:00
newText := string(event.Chars())
2020-07-26 18:52:54 +00:00
if len(newText) > 0 {
v.text += newText
2020-07-26 18:52:54 +00:00
v.SetText(v.text)
2020-07-26 18:52:54 +00:00
return true
}
2020-07-26 18:52:54 +00:00
return false
}
2020-07-26 18:52:54 +00:00
// OnKeyRepeat handles key repeat events
func (v *TextBox) OnKeyRepeat(event d2interface.KeyEvent) bool {
if event.Key() == d2enum.KeyBackspace && debounceEvents(event.Duration()) {
if len(v.text) >= 1 {
v.text = v.text[:len(v.text)-1]
}
2020-07-26 18:52:54 +00:00
v.SetText(v.text)
}
2020-07-26 18:52:54 +00:00
return false
}
func debounceEvents(numFrames int) bool {
const (
delay = 30
interval = 3
)
2020-07-26 18:52:54 +00:00
if numFrames == 1 {
return true
}
2020-07-26 18:52:54 +00:00
if numFrames >= delay && (numFrames-delay)%interval == 0 {
return true
}
2020-07-26 18:52:54 +00:00
return false
}
2020-07-26 18:52:54 +00:00
// Advance updates the text box
func (v *TextBox) Advance(_ float64) error {
return nil
}
// Update updates the textbox (not currently implemented)
func (v *TextBox) Update() {
}
2020-07-26 18:52:54 +00:00
// GetText returns the text box's text
func (v *TextBox) GetText() string {
return v.text
}
2020-07-26 18:52:54 +00:00
// SetText sets the text box's text
//nolint:gomnd // Built-in values
func (v *TextBox) SetText(newText string) {
result := ""
2020-07-26 18:52:54 +00:00
for _, c := range newText {
2020-06-18 18:11:04 +00:00
if !strings.Contains(v.filter, string(c)) {
continue
}
2020-07-26 18:52:54 +00:00
result += string(c)
}
2020-07-26 18:52:54 +00:00
if len(result) > 15 {
result = result[0:15]
}
2020-07-26 18:52:54 +00:00
v.text = result
2020-07-26 18:52:54 +00:00
for {
tw, _ := v.textLabel.GetTextMetrics(result)
2020-07-26 18:52:54 +00:00
if tw > 150 {
result = result[1:]
continue
}
2020-07-26 18:52:54 +00:00
v.lineBar.SetPosition(v.x+6+tw, v.y+3)
v.textLabel.SetText(result)
2020-07-26 18:52:54 +00:00
break
}
}
2020-07-26 18:52:54 +00:00
// GetSize returns the size of the text box
func (v *TextBox) GetSize() (width, height int) {
return v.bgSprite.GetCurrentFrameSize()
}
2020-07-26 18:52:54 +00:00
// SetPosition sets the position of the text box
//nolint:gomnd // Built-in values
func (v *TextBox) SetPosition(x, y int) {
lw, _ := v.textLabel.GetSize()
2020-07-26 18:52:54 +00:00
v.x = x
v.y = y
2020-07-26 18:52:54 +00:00
v.textLabel.SetPosition(v.x+6, v.y+3)
v.lineBar.SetPosition(v.x+6+lw, v.y+3)
v.bgSprite.SetPosition(v.x, v.y+26)
}
2020-07-26 18:52:54 +00:00
// GetPosition returns the position of the text box
func (v *TextBox) GetPosition() (x, y int) {
return v.x, v.y
}
2020-07-26 18:52:54 +00:00
// GetVisible returns the visibility of the text box
func (v *TextBox) GetVisible() bool {
return v.visible
}
2020-07-26 18:52:54 +00:00
// SetVisible sets the visibility of the text box
func (v *TextBox) SetVisible(visible bool) {
v.visible = visible
}
2020-07-26 18:52:54 +00:00
// GetEnabled returns the enabled state of the text box
func (v *TextBox) GetEnabled() bool {
return v.enabled
}
2020-07-26 18:52:54 +00:00
// SetEnabled sets the enabled state of the text box
func (v *TextBox) SetEnabled(enabled bool) {
v.enabled = enabled
}
2020-07-26 18:52:54 +00:00
// SetPressed does nothing for text boxes
func (v *TextBox) SetPressed(_ bool) {
// no op
}
2020-07-26 18:52:54 +00:00
// GetPressed does nothing for text boxes
func (v *TextBox) GetPressed() bool {
return false
}
2020-07-26 18:52:54 +00:00
// OnActivated handles activation events for the text box
func (v *TextBox) OnActivated(_ func()) {
// no op
}
2020-07-26 18:52:54 +00:00
// Activate activates the text box
func (v *TextBox) Activate() {
v.isFocused = true
}