leonsal пре 8 година
родитељ
комит
9676130488
2 измењених фајлова са 321 додато и 0 уклоњено
  1. 312 0
      gui/builder.go
  2. 9 0
      math32/color.go

+ 312 - 0
gui/builder.go

@@ -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
+}

+ 9 - 0
math32/color.go

@@ -117,6 +117,15 @@ func (c *Color) Clone() *Color {
 	return NewColor(c.R, c.G, c.B)
 }
 
+//
+// ColorToValue returns the integer value of the color with
+// the specified name. If name not found returns 0.
+//
+func ColorUint(name string) uint {
+
+	return colorKeywords[name]
+}
+
 var colorKeywords = map[string]uint{
 	"aliceblue":            0xF0F8FF,
 	"antiquewhite":         0xFAEBD7,