| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- // 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
- }
|