2019-11-10 12:28:41 -05:00
|
|
|
package d2ui
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2020-07-22 15:03:03 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
2020-06-29 00:41:58 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
2020-06-23 18:12:08 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
2019-11-10 12:28:41 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// TextBox represents a text input box
|
|
|
|
type TextBox struct {
|
2020-08-06 10:30:23 -04:00
|
|
|
manager *UIManager
|
|
|
|
textLabel *Label
|
|
|
|
lineBar *Label
|
2019-11-10 12:28:41 -05:00
|
|
|
text string
|
2020-07-26 13:23:46 -04:00
|
|
|
filter string
|
2019-11-10 12:28:41 -05:00
|
|
|
x int
|
|
|
|
y int
|
2020-07-26 13:23:46 -04:00
|
|
|
bgSprite *Sprite
|
2019-11-10 12:28:41 -05:00
|
|
|
visible bool
|
|
|
|
enabled bool
|
2020-07-22 15:03:03 -04:00
|
|
|
isFocused bool
|
2019-11-10 12:28:41 -05:00
|
|
|
}
|
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
// 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 17:31:45 -04:00
|
|
|
bgSprite, _ := ui.NewSprite(d2resource.TextBox2, d2resource.PaletteUnits)
|
2020-08-06 10:30:23 -04:00
|
|
|
tb := &TextBox{
|
2020-06-18 14:11:04 -04:00
|
|
|
filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
2019-12-21 20:53:18 -05:00
|
|
|
bgSprite: bgSprite,
|
2020-08-06 10:30:23 -04:00
|
|
|
textLabel: ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
|
|
|
|
lineBar: ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
|
2019-11-10 12:28:41 -05:00
|
|
|
enabled: true,
|
|
|
|
visible: true,
|
|
|
|
}
|
2020-06-23 18:12:08 -04:00
|
|
|
tb.lineBar.SetText("_")
|
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
ui.addWidget(tb)
|
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
return tb
|
2019-11-10 12:28:41 -05:00
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// SetFilter sets the text box filter
|
2020-06-18 14:11:04 -04:00
|
|
|
func (v *TextBox) SetFilter(filter string) {
|
|
|
|
v.filter = filter
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// Render renders the text box
|
|
|
|
func (v *TextBox) Render(target d2interface.Surface) error {
|
2019-11-10 12:28:41 -05:00
|
|
|
if !v.visible {
|
2020-07-26 14:52:54 -04:00
|
|
|
return nil
|
2019-11-10 12:28:41 -05:00
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
|
|
|
if err := v.bgSprite.Render(target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-12-21 20:53:18 -05:00
|
|
|
v.textLabel.Render(target)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
if (time.Now().UnixNano()/1e6)&(1<<8) > 0 {
|
2019-12-21 20:53:18 -05:00
|
|
|
v.lineBar.Render(target)
|
2019-11-10 12:28:41 -05:00
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
|
|
|
return nil
|
2019-11-10 12:28:41 -05:00
|
|
|
}
|
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
// bindManager binds the textbox to the UI manager
|
|
|
|
func (v *TextBox) bindManager(manager *UIManager) {
|
|
|
|
v.manager = manager
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// OnKeyChars handles key character events
|
2020-07-03 15:09:16 -04:00
|
|
|
func (v *TextBox) OnKeyChars(event d2interface.KeyCharsEvent) bool {
|
2020-07-22 15:03:03 -04:00
|
|
|
if !v.isFocused || !v.visible || !v.enabled {
|
2020-06-23 18:12:08 -04:00
|
|
|
return false
|
2019-11-10 12:28:41 -05:00
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-07-03 15:09:16 -04:00
|
|
|
newText := string(event.Chars())
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
if len(newText) > 0 {
|
|
|
|
v.text += newText
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
v.SetText(v.text)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
return true
|
2019-11-10 12:28:41 -05:00
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// OnKeyRepeat handles key repeat events
|
2020-07-03 15:09:16 -04:00
|
|
|
func (v *TextBox) OnKeyRepeat(event d2interface.KeyEvent) bool {
|
2020-07-06 21:26:08 -04:00
|
|
|
if event.Key() == d2enum.KeyBackspace && debounceEvents(event.Duration()) {
|
2019-11-10 12:28:41 -05:00
|
|
|
if len(v.text) >= 1 {
|
|
|
|
v.text = v.text[:len(v.text)-1]
|
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
v.SetText(v.text)
|
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func debounceEvents(numFrames int) bool {
|
|
|
|
const (
|
|
|
|
delay = 30
|
|
|
|
interval = 3
|
|
|
|
)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
if numFrames == 1 {
|
|
|
|
return true
|
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
if numFrames >= delay && (numFrames-delay)%interval == 0 {
|
|
|
|
return true
|
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-23 18:12:08 -04:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// Advance updates the text box
|
2020-08-06 10:30:23 -04:00
|
|
|
func (v *TextBox) Advance(_ float64) error {
|
|
|
|
return nil
|
2019-11-10 12:28:41 -05:00
|
|
|
}
|
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
// Update updates the textbox (not currently implemented)
|
2020-06-22 23:51:23 -04:00
|
|
|
func (v *TextBox) Update() {
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// GetText returns the text box's text
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *TextBox) GetText() string {
|
2019-11-10 12:28:41 -05:00
|
|
|
return v.text
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// SetText sets the text box's text
|
|
|
|
//nolint:gomnd // Built-in values
|
2019-11-10 12:28:41 -05:00
|
|
|
func (v *TextBox) SetText(newText string) {
|
|
|
|
result := ""
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
for _, c := range newText {
|
2020-06-18 14:11:04 -04:00
|
|
|
if !strings.Contains(v.filter, string(c)) {
|
2019-11-10 12:28:41 -05:00
|
|
|
continue
|
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
result += string(c)
|
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
if len(result) > 15 {
|
|
|
|
result = result[0:15]
|
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
v.text = result
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
for {
|
|
|
|
tw, _ := v.textLabel.GetTextMetrics(result)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
if tw > 150 {
|
|
|
|
result = result[1:]
|
|
|
|
continue
|
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-02-01 21:51:49 -05:00
|
|
|
v.lineBar.SetPosition(v.x+6+tw, v.y+3)
|
2019-11-10 12:28:41 -05:00
|
|
|
v.textLabel.SetText(result)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// GetSize returns the size of the text box
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *TextBox) GetSize() (width, height int) {
|
2019-12-21 20:53:18 -05:00
|
|
|
return v.bgSprite.GetCurrentFrameSize()
|
2019-11-10 12:28:41 -05:00
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// SetPosition sets the position of the text box
|
|
|
|
//nolint:gomnd // Built-in values
|
2019-12-21 20:53:18 -05:00
|
|
|
func (v *TextBox) SetPosition(x, y int) {
|
2020-07-07 20:16:22 -04:00
|
|
|
lw, _ := v.textLabel.GetSize()
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-11-10 12:28:41 -05:00
|
|
|
v.x = x
|
|
|
|
v.y = y
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2019-12-21 20:53:18 -05:00
|
|
|
v.textLabel.SetPosition(v.x+6, v.y+3)
|
2020-07-07 20:16:22 -04:00
|
|
|
v.lineBar.SetPosition(v.x+6+lw, v.y+3)
|
2019-12-21 20:53:18 -05:00
|
|
|
v.bgSprite.SetPosition(v.x, v.y+26)
|
2019-11-10 12:28:41 -05:00
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// GetPosition returns the position of the text box
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *TextBox) GetPosition() (x, y int) {
|
2019-11-10 12:28:41 -05:00
|
|
|
return v.x, v.y
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// GetVisible returns the visibility of the text box
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *TextBox) GetVisible() bool {
|
2019-11-10 12:28:41 -05:00
|
|
|
return v.visible
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// SetVisible sets the visibility of the text box
|
2019-11-10 12:28:41 -05:00
|
|
|
func (v *TextBox) SetVisible(visible bool) {
|
|
|
|
v.visible = visible
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// GetEnabled returns the enabled state of the text box
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *TextBox) GetEnabled() bool {
|
2019-11-10 12:28:41 -05:00
|
|
|
return v.enabled
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// SetEnabled sets the enabled state of the text box
|
2019-11-10 12:28:41 -05:00
|
|
|
func (v *TextBox) SetEnabled(enabled bool) {
|
|
|
|
v.enabled = enabled
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// SetPressed does nothing for text boxes
|
|
|
|
func (v *TextBox) SetPressed(_ bool) {
|
2019-11-10 12:28:41 -05:00
|
|
|
// no op
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// GetPressed does nothing for text boxes
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *TextBox) GetPressed() bool {
|
2019-11-10 12:28:41 -05:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// OnActivated handles activation events for the text box
|
|
|
|
func (v *TextBox) OnActivated(_ func()) {
|
2019-11-10 12:28:41 -05:00
|
|
|
// no op
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// Activate activates the text box
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *TextBox) Activate() {
|
2020-07-22 15:03:03 -04:00
|
|
|
v.isFocused = true
|
2019-11-10 12:28:41 -05:00
|
|
|
}
|