Add a partial listbox implementation
This commit is contained in:
@@ -39,11 +39,15 @@ type mainButton struct {
|
||||
}
|
||||
|
||||
func registerSimpleButton(d *Driver, r *menus.Record) error {
|
||||
return registerButton(d, r, r.SpriteId[0])
|
||||
_, err := registerButton(d, r, r.SpriteId[0])
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func registerInvokeButton(d *Driver, r *menus.Record) error {
|
||||
return registerButton(d, r, r.Share)
|
||||
_, err := registerButton(d, r, r.Share)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func registerMainButton(d *Driver, r *menus.Record) error {
|
||||
@@ -99,10 +103,10 @@ func registerDoorHotspot(d *Driver, r *menus.Record) error {
|
||||
|
||||
}
|
||||
|
||||
func registerButton(d *Driver, r *menus.Record, spriteId int) error {
|
||||
func registerButton(d *Driver, r *menus.Record, spriteId int) (*button, error) {
|
||||
sprites, err := d.menu.Sprites(spriteId, 3) // base, pressed, disabled
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
btn := &button{
|
||||
@@ -118,7 +122,7 @@ func registerButton(d *Driver, r *menus.Record, spriteId int) error {
|
||||
d.hoverables = append(d.hoverables, btn)
|
||||
d.paintables = append(d.paintables, btn)
|
||||
|
||||
return nil
|
||||
return btn, nil
|
||||
}
|
||||
|
||||
func (b *button) id() string {
|
||||
|
@@ -18,14 +18,9 @@ func init() {
|
||||
// FIXME: these need implementing
|
||||
|
||||
// Needed for Keyboard.mnu (main -> options -> keyboard)
|
||||
registerBuilder(menus.TypeLineKbd, registerDebug("Unimplemented LineKbd", nil))
|
||||
registerBuilder(menus.TypeDialogue, registerDebug("Unimplemented Dialogue", nil))
|
||||
|
||||
// Needed for Briefing.mnu
|
||||
registerBuilder(menus.TypeLineBriefing, registerDebug("Unimplemented LineBriefing", nil))
|
||||
|
||||
// Needed for ChaEquip.mnu
|
||||
registerBuilder(menus.TypeThumb, registerDebug("Unimplemented Thumb", nil))
|
||||
|
||||
// Needed for MainGameChaos.mnu
|
||||
registerBuilder(menus.TypeStatusBar, registerDebug("Unimplemented StatusBar", nil))
|
||||
|
158
internal/ui/list_box.go
Normal file
158
internal/ui/list_box.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"code.ur.gs/lupine/ordoor/internal/menus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerBuilder(menus.TypeLineKbd, ownedByMenu)
|
||||
registerBuilder(menus.TypeLineBriefing, ownedByMenu)
|
||||
|
||||
registerBuilder(menus.TypeThumb, ownedByMenu)
|
||||
registerBuilder(menus.TypeListBoxUp, ownedByMenu)
|
||||
registerBuilder(menus.TypeListBoxDown, ownedByMenu)
|
||||
}
|
||||
|
||||
// listBox is a TListBox in VCL terms. It has a number of lines of text, one of
|
||||
// which may be selected, and a slider with up and down buttons to scroll if the
|
||||
// options in the box exceed its viewing capacity.
|
||||
//
|
||||
// TODO: multi-select functionality? Is it needed?
|
||||
type listBox struct {
|
||||
upBtn *button
|
||||
downBtn *button
|
||||
|
||||
// TODO: can we reuse the slider element for the thumb?
|
||||
|
||||
base *noninteractive // The menu itself has a sprite to display
|
||||
lines []*noninteractive // We display to these
|
||||
|
||||
// The list box acts as a window onto these
|
||||
strings []string
|
||||
|
||||
// The start of our window
|
||||
offset int
|
||||
}
|
||||
|
||||
func registerListBox(d *Driver, menu *menus.Record) ([]*menus.Record, error) {
|
||||
var upBtn *menus.Record
|
||||
var downBtn *menus.Record
|
||||
var slider *menus.Record
|
||||
var items []*menus.Record
|
||||
|
||||
for _, rec := range menu.Children {
|
||||
switch rec.Type {
|
||||
case menus.TypeListBoxUp:
|
||||
if upBtn != nil {
|
||||
return nil, fmt.Errorf("Duplicate up buttons in menu %v", menu.Locator())
|
||||
}
|
||||
upBtn = rec
|
||||
case menus.TypeListBoxDown:
|
||||
if downBtn != nil {
|
||||
return nil, fmt.Errorf("Duplicate down buttons in menu %v", menu.Locator())
|
||||
}
|
||||
downBtn = rec
|
||||
case menus.TypeLineKbd, menus.TypeLineBriefing:
|
||||
items = append(items, rec)
|
||||
case menus.TypeThumb:
|
||||
if slider != nil {
|
||||
return nil, fmt.Errorf("Duplicate thumbs in menu %v", menu.Locator())
|
||||
}
|
||||
slider = rec
|
||||
default:
|
||||
return nil, fmt.Errorf("Unrecognised child in listbox menu: %v", rec.Locator())
|
||||
}
|
||||
}
|
||||
|
||||
if len(items) == 0 || slider == nil || upBtn == nil || downBtn == nil {
|
||||
return nil, fmt.Errorf("Missing items in menu %v", menu.Locator())
|
||||
}
|
||||
|
||||
// Now build the wonderful thing
|
||||
elemUp, err := registerButton(d, upBtn, upBtn.SpriteId[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
elemDown, err := registerButton(d, downBtn, downBtn.SpriteId[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
element := &listBox{
|
||||
upBtn: elemUp,
|
||||
downBtn: elemDown,
|
||||
}
|
||||
|
||||
for _, rec := range items {
|
||||
ni, err := registerNoninteractive(d, rec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
element.lines = append(element.lines, ni)
|
||||
}
|
||||
|
||||
// Internal wiring-up
|
||||
elemUp.onClick(element.up)
|
||||
elemDown.onClick(element.down)
|
||||
|
||||
// FIXME: Test data for now
|
||||
for i := 0; i < 50; i++ {
|
||||
element.strings = append(element.strings, fmt.Sprintf("FOO %v", i))
|
||||
}
|
||||
|
||||
// Register everything
|
||||
// FIXME: we should be able to freeze/unfreeze as a group. The buttons register themselves though...
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (l *listBox) SetStrings(to []string) {
|
||||
if len(to) < len(l.strings) {
|
||||
l.offset = 0 // FIXME: unconditional? Trim to max?
|
||||
}
|
||||
|
||||
l.strings = to
|
||||
|
||||
l.refresh()
|
||||
}
|
||||
|
||||
// TODO: Selected returns the index and value of the selected item
|
||||
func (l *listBox) Selected() (int, string) {
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
func (l *listBox) up() {
|
||||
if l.offset <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
l.offset -= 1
|
||||
l.refresh()
|
||||
}
|
||||
|
||||
func (l *listBox) down() {
|
||||
if l.offset > len(l.strings)-len(l.lines) {
|
||||
return
|
||||
}
|
||||
|
||||
l.offset += 1
|
||||
l.refresh()
|
||||
}
|
||||
|
||||
func (l *listBox) refresh() {
|
||||
for i, ni := range l.lines {
|
||||
// FIXME: noninteractive isn't set up for dynamic text yet. Need to
|
||||
// generate textImg on demand instead of once at start.
|
||||
ni.hoverImpl.text = ""
|
||||
if len(l.strings) > l.offset+i {
|
||||
ni.hoverImpl.text = l.strings[l.offset+i]
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Listbox offset: %v", l.offset)
|
||||
}
|
@@ -11,11 +11,42 @@ func init() {
|
||||
}
|
||||
|
||||
func registerMenu(d *Driver, r *menus.Record) ([]*menus.Record, error) {
|
||||
// Group all inventory selects that share a menu together
|
||||
childrenLeft, err := listBoxFromMenu(d, r, r.Children)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
childrenLeft, err = inventorySelectFromMenu(d, r, childrenLeft)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return all the unhandled children to be processed further
|
||||
return childrenLeft, nil
|
||||
}
|
||||
|
||||
func listBoxFromMenu(d *Driver, menu *menus.Record, children []*menus.Record) ([]*menus.Record, error) {
|
||||
ok := false
|
||||
for _, rec := range children {
|
||||
if rec.Type == menus.TypeThumb { // FIXME: we're using this to indicate a listbox
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return children, nil
|
||||
}
|
||||
|
||||
return registerListBox(d, menu)
|
||||
}
|
||||
|
||||
// Group all inventory selects that share a menu together
|
||||
func inventorySelectFromMenu(d *Driver, menu *menus.Record, children []*menus.Record) ([]*menus.Record, error) {
|
||||
var childrenLeft []*menus.Record
|
||||
var inventorySelects []*inventorySelect
|
||||
|
||||
for _, child := range r.Children {
|
||||
for _, child := range children {
|
||||
switch child.Type {
|
||||
case menus.TypeInventorySelect:
|
||||
is, err := registerInventorySelect(d, child)
|
||||
@@ -37,6 +68,5 @@ func registerMenu(d *Driver, r *menus.Record) ([]*menus.Record, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Return all the unhandled children to be processed further
|
||||
return childrenLeft, nil
|
||||
}
|
||||
|
@@ -46,6 +46,11 @@ type animationHover struct {
|
||||
}
|
||||
|
||||
func registerStatic(d *Driver, r *menus.Record) error {
|
||||
_, err := registerNoninteractive(d, r)
|
||||
return err
|
||||
}
|
||||
|
||||
func registerNoninteractive(d *Driver, r *menus.Record) (*noninteractive, error) {
|
||||
// FIXME: SpriteID takes precedence over SHARE if present, but is that right?
|
||||
spriteId := r.Share
|
||||
if len(r.SpriteId) > 0 && r.SpriteId[0] != -1 {
|
||||
@@ -54,7 +59,7 @@ func registerStatic(d *Driver, r *menus.Record) error {
|
||||
|
||||
sprite, err := d.menu.Sprite(spriteId)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ni := &noninteractive{
|
||||
@@ -67,7 +72,7 @@ func registerStatic(d *Driver, r *menus.Record) error {
|
||||
d.hoverables = append(d.hoverables, ni)
|
||||
d.paintables = append(d.paintables, ni)
|
||||
|
||||
return nil
|
||||
return ni, nil
|
||||
}
|
||||
|
||||
func registerHypertext(d *Driver, r *menus.Record) error {
|
||||
|
Reference in New Issue
Block a user