1
0
Fork 0
anymenu/item.go

228 lines
4.7 KiB
Go

package menu
import (
"log"
"strings"
"time"
"github.com/juju/errors"
"github.com/veandco/go-sdl2/sdl"
)
type listItem struct {
Label string `json:"label"`
ActionKey string `json:"key"`
Keep bool `json:"keep"`
Command string `json:"command"`
Condition *ifCondition `json:"if"`
Items []listItem `json:"items"`
active bool
subLabel string
}
type ifCondition struct {
Command string `json:"command"`
Output map[string]listItem `json:"output"`
cachedOutput *string
busy bool
}
func (li *listItem) render(r *sdl.Renderer, offsetY int32) error {
if err := drawItemBackground(r, offsetY, li.active); err != nil {
return errors.Annotate(err, "draw item background")
}
if err := drawItemLabel(r, offsetY, *li); err != nil {
return errors.Annotate(err, "draw item label")
}
if err := drawActionKeyLabel(r, offsetY, *li); err != nil {
return errors.Annotate(err, "draw item label")
}
return nil
}
func (li *listItem) call() error {
defer func() {
if err := recover(); err != nil {
log.Printf("Action failed: %v", err)
}
}()
switch {
case li.Condition != nil:
if li.Condition.cachedOutput == nil {
return nil
}
out := *li.Condition.cachedOutput
if match, ok := li.Condition.Output[out]; ok {
_, err := execCommand(match.Command)
return err
}
log.Println("no match", out)
return nil
case li.Command != "":
_, err := execCommand(li.Command)
return err
default:
return nil
}
}
func (li *listItem) label() string {
if li.subLabel != "" {
return li.Label + " " + li.subLabel
}
return li.Label
}
func drawItemBackground(r *sdl.Renderer, offsetY int32, active bool) error {
if err := setDrawColor(r, theme.ItemBackground); err != nil {
return errors.Annotate(err, "set item background color")
}
actionKeyFrame := &sdl.Rect{
X: int32(paddingX + borderWidth),
Y: offsetY,
W: int32(itemHeight()),
H: int32(itemHeight()),
}
var err error
if active {
err = r.DrawRect(actionKeyFrame)
} else {
err = r.FillRect(actionKeyFrame)
}
if err != nil {
return errors.Annotate(err, "draw item")
}
itemBackground := &sdl.Rect{
X: int32(borderWidth + paddingX + itemHeight() + paddingX),
Y: offsetY,
W: int32(windowWidth - 3*paddingX - itemHeight() - 2*borderWidth),
H: int32(itemHeight()),
}
if active {
err = r.DrawRect(itemBackground)
} else {
err = r.FillRect(itemBackground)
}
if err != nil {
return errors.Annotate(err, "draw item")
}
return nil
}
func drawItemLabel(r *sdl.Renderer, offsetY int32, li listItem) error {
label := li.Label
if li.Condition != nil {
if li.Condition.cachedOutput != nil {
out := *li.Condition.cachedOutput
if match, ok := li.Condition.Output[out]; ok {
label = match.Label
}
}
if li.Condition.busy {
label += " [busy]"
}
}
color := theme.ItemText
if li.Condition != nil && li.Condition.busy {
color = theme.TextBusy
}
labelTexture, err := renderText(r, label, color)
if err != nil {
return errors.Annotate(err, "draw item label")
}
defer labelTexture.Destroy()
lw, lh, err := font.SizeUTF8(label)
if err != nil {
return err
}
const magicNumber = 3
itemLabel := &sdl.Rect{
X: int32(paddingX*2+itemHeight()+(itemHeight()-lh)/2) + magicNumber,
Y: offsetY + int32(itemHeight()-lh)/2,
W: int32(lw),
H: int32(lh),
}
if err := r.Copy(labelTexture, nil, itemLabel); err != nil {
return errors.Annotate(err, "render item label")
}
return nil
}
func drawActionKeyLabel(r *sdl.Renderer, offsetY int32, li listItem) error {
label := strings.ToUpper(li.ActionKey)
color := theme.ItemText
if li.Condition != nil && li.Condition.busy {
color = theme.TextBusy
}
labelTexture, err := renderText(r, label, color)
if err != nil {
return errors.Annotate(err, "draw item label")
}
defer labelTexture.Destroy()
lw, lh, err := font.SizeUTF8(label)
if err != nil {
return err
}
itemLabel := &sdl.Rect{
X: int32(paddingX + (itemHeight()-lw)/2),
Y: offsetY + int32(itemHeight()-lh)/2,
W: int32(lw),
H: int32(lh),
}
if err := r.Copy(labelTexture, nil, itemLabel); err != nil {
return errors.Annotate(err, "render item label")
}
return nil
}
func renderText(r *sdl.Renderer, text string, c sdl.Color) (t *sdl.Texture, err error) {
fs, err := font.RenderUTF8Blended(text, c)
if err != nil {
return nil, err
}
defer fs.Free()
t, err = r.CreateTextureFromSurface(fs)
if err != nil {
return nil, err
}
return
}
func (c *ifCondition) evaluate() {
fn := func() {
c.busy = true
out, err := execCommand(c.Command)
if err != nil {
log.Printf("Error evaluating condition: %v", err)
} else {
out = strings.TrimSpace(out)
c.cachedOutput = &out
}
c.busy = false
}
fn()
t := time.NewTicker(2 * time.Second)
defer t.Stop()
for range t.C {
fn()
}
}