|
|
@@ -0,0 +1,312 @@
|
|
|
+// Copyright 2016 The G3N Authors. All rights reserved.
|
|
|
+// Use of this source code is governed by a BSD-style
|
|
|
+// license that can be found in the LICENSE file.
|
|
|
+
|
|
|
+package gui
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "io/ioutil"
|
|
|
+ "os"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+
|
|
|
+ "github.com/g3n/engine/math32"
|
|
|
+ "gopkg.in/yaml.v2"
|
|
|
+)
|
|
|
+
|
|
|
+// Builder builds GUI objects from a declarative description in YAML format
|
|
|
+type Builder struct {
|
|
|
+ panels []IPanel // first level panels
|
|
|
+ mapPath map[string]IPanel
|
|
|
+}
|
|
|
+
|
|
|
+type panelDesc struct {
|
|
|
+ Type string
|
|
|
+ Posx float32
|
|
|
+ Posy float32
|
|
|
+ Width float32
|
|
|
+ Height float32
|
|
|
+ Margins string
|
|
|
+ Borders string
|
|
|
+ BorderColor string
|
|
|
+ Paddings string
|
|
|
+ Color string
|
|
|
+ Enabled bool
|
|
|
+ Visible bool
|
|
|
+ Renderable bool
|
|
|
+ Children []panelDesc
|
|
|
+ Layout layoutAttr
|
|
|
+ Text string
|
|
|
+ FontSize float32
|
|
|
+ FontDPI float32
|
|
|
+ PlaceHolder string
|
|
|
+ MaxLength uint
|
|
|
+}
|
|
|
+
|
|
|
+type layoutAttr struct {
|
|
|
+ Type string
|
|
|
+}
|
|
|
+
|
|
|
+const (
|
|
|
+ descPanel = "Panel"
|
|
|
+ descLabel = "Label"
|
|
|
+ descEdit = "Edit"
|
|
|
+)
|
|
|
+
|
|
|
+//
|
|
|
+// NewBuilder creates and returns a pointer to a new gui Builder object
|
|
|
+//
|
|
|
+func NewBuilder() *Builder {
|
|
|
+
|
|
|
+ b := new(Builder)
|
|
|
+
|
|
|
+ return b
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+// BuildFromData builds gui objects from the specified description
|
|
|
+// in YAML format
|
|
|
+//
|
|
|
+func (b *Builder) BuildFromString(desc string) error {
|
|
|
+
|
|
|
+ // Try assuming the description contains a single root panel
|
|
|
+ var pd panelDesc
|
|
|
+ err := yaml.Unmarshal([]byte(desc), &pd)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if pd.Type != "" {
|
|
|
+ pan, err := b.build(&pd, "", nil)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ b.panels = append(b.panels, pan)
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // Try assuming the description is a map of panels
|
|
|
+ var pdm map[string]panelDesc
|
|
|
+ err = yaml.Unmarshal([]byte(desc), &pdm)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // Builds each panel in the map
|
|
|
+ for name, pd := range pdm {
|
|
|
+ pan, err := b.build(&pd, name, nil)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ b.panels = append(b.panels, pan)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+// BuildFromFile builds gui objects from the specified file which
|
|
|
+// must contain objects descriptions in YAML format
|
|
|
+//
|
|
|
+func (b *Builder) BuildFromFile(filepath string) error {
|
|
|
+
|
|
|
+ // Reads all file data
|
|
|
+ f, err := os.Open(filepath)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ data, err := ioutil.ReadAll(f)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ err = f.Close()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parses file data
|
|
|
+ return b.BuildFromString(string(data))
|
|
|
+}
|
|
|
+
|
|
|
+func (b *Builder) Panels() []IPanel {
|
|
|
+
|
|
|
+ return b.panels
|
|
|
+}
|
|
|
+
|
|
|
+func (b *Builder) Label(path string) (*Label, error) {
|
|
|
+
|
|
|
+ return nil, nil
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+// build builds gui objects from the specified description and its children recursively
|
|
|
+//
|
|
|
+func (b *Builder) build(pd *panelDesc, name string, parent *Panel) (IPanel, error) {
|
|
|
+
|
|
|
+ fmt.Printf("\n%+v\n\n", pd)
|
|
|
+ var err error
|
|
|
+ var pan IPanel
|
|
|
+ switch pd.Type {
|
|
|
+ case descPanel:
|
|
|
+ pan, err = b.buildPanel(pd)
|
|
|
+ case descLabel:
|
|
|
+ pan, err = b.buildLabel(pd)
|
|
|
+ case descEdit:
|
|
|
+ pan, err = b.buildEdit(pd)
|
|
|
+ default:
|
|
|
+ err = fmt.Errorf("Invalid panel type:%s", pd.Type)
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if parent != nil {
|
|
|
+ parent.Add(pan)
|
|
|
+ }
|
|
|
+ return pan, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (b *Builder) buildPanel(pd *panelDesc) (IPanel, error) {
|
|
|
+
|
|
|
+ log.Error("buildPanel:[%s]", pd.Borders)
|
|
|
+ pan := NewPanel(pd.Width, pd.Height)
|
|
|
+
|
|
|
+ // Set margin sizes
|
|
|
+ bs, err := b.parseBorderSizes(pd.Margins)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if bs != nil {
|
|
|
+ pan.SetMarginsFrom(bs)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set border sizes
|
|
|
+ bs, err = b.parseBorderSizes(pd.Borders)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if bs != nil {
|
|
|
+ pan.SetBordersFrom(bs)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set border color
|
|
|
+ c, err := b.parseColor(pd.BorderColor)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if c != nil {
|
|
|
+ pan.SetBordersColor4(c)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set paddings sizes
|
|
|
+ bs, err = b.parseBorderSizes(pd.Paddings)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if bs != nil {
|
|
|
+ pan.SetPaddingsFrom(bs)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set color
|
|
|
+ c, err = b.parseColor(pd.Color)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if c != nil {
|
|
|
+ pan.SetColor4(c)
|
|
|
+ }
|
|
|
+
|
|
|
+ return pan, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (b *Builder) buildLabel(pa *panelDesc) (IPanel, error) {
|
|
|
+
|
|
|
+ label := NewLabel("df")
|
|
|
+
|
|
|
+ return label, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (b *Builder) buildEdit(pa *panelDesc) (IPanel, error) {
|
|
|
+
|
|
|
+ return nil, nil
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+// parseFloats parses a string with a list of floats with the specified size
|
|
|
+// and returns a slice. The specified size is 0 any number of floats is allowed.
|
|
|
+// The individual values can be separated by spaces or commas
|
|
|
+//
|
|
|
+func (b *Builder) parseFloats(field string, min, max int) ([]float32, error) {
|
|
|
+
|
|
|
+ // Checks if field is empty
|
|
|
+ field = strings.Trim(field, " ")
|
|
|
+ if field == "" {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // Separate individual fields
|
|
|
+ var parts []string
|
|
|
+ if strings.Index(field, ",") < 0 {
|
|
|
+ parts = strings.Split(field, " ")
|
|
|
+ } else {
|
|
|
+ parts = strings.Split(field, ",")
|
|
|
+ }
|
|
|
+ if len(parts) < min || len(parts) > max {
|
|
|
+ return nil, fmt.Errorf("Invalid number(%d) of float32 fields in:[%s]", len(parts), field)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parse each field value and appends to slice
|
|
|
+ var values []float32
|
|
|
+ for i := 0; i < len(parts); i++ {
|
|
|
+ val, err := strconv.ParseFloat(parts[i], 32)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("Error parsing float32 field:[%s]: %s", field, err)
|
|
|
+ }
|
|
|
+ values = append(values, float32(val))
|
|
|
+ }
|
|
|
+ return values, nil
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+// parseBorderSizes parses a string field which can contain one float value or
|
|
|
+// float values. In the first case all borders has the same width
|
|
|
+//
|
|
|
+func (b *Builder) parseBorderSizes(field string) (*BorderSizes, error) {
|
|
|
+
|
|
|
+ va, err := b.parseFloats(field, 1, 4)
|
|
|
+ if va == nil || err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if len(va) == 1 {
|
|
|
+ return &BorderSizes{va[0], va[0], va[0], va[0]}, nil
|
|
|
+ }
|
|
|
+ return &BorderSizes{va[0], va[1], va[2], va[3]}, nil
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+// parseColor parses a field which can contain a color name or
|
|
|
+// a list of 3 or 4 float values for the color components
|
|
|
+//
|
|
|
+func (b *Builder) parseColor(field string) (*math32.Color4, error) {
|
|
|
+
|
|
|
+ // Checks if field is empty
|
|
|
+ field = strings.Trim(field, " ")
|
|
|
+ if field == "" {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // Checks if field is a color name
|
|
|
+ value := math32.ColorUint(field)
|
|
|
+ if value != 0 {
|
|
|
+ var c math32.Color
|
|
|
+ c.SetName(field)
|
|
|
+ return &math32.Color4{c.R, c.G, c.B, 1}, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // Accept 3 or 4 floats values
|
|
|
+ va, err := b.parseFloats(field, 3, 4)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if len(va) == 3 {
|
|
|
+ return &math32.Color4{va[0], va[1], va[2], 1}, nil
|
|
|
+ }
|
|
|
+ return &math32.Color4{va[0], va[1], va[2], va[3]}, nil
|
|
|
+}
|