builder.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package gui
  5. import (
  6. "fmt"
  7. "io/ioutil"
  8. "os"
  9. "strconv"
  10. "strings"
  11. "github.com/g3n/engine/math32"
  12. "gopkg.in/yaml.v2"
  13. )
  14. // Builder builds GUI objects from a declarative description in YAML format
  15. type Builder struct {
  16. panels []IPanel // first level panels
  17. mapPath map[string]IPanel
  18. }
  19. type panelDesc struct {
  20. Type string
  21. Posx float32
  22. Posy float32
  23. Width float32
  24. Height float32
  25. Margins string
  26. Borders string
  27. BorderColor string
  28. Paddings string
  29. Color string
  30. Enabled bool
  31. Visible bool
  32. Renderable bool
  33. Children []panelDesc
  34. Layout layoutAttr
  35. Text string
  36. FontSize float32
  37. FontDPI float32
  38. PlaceHolder string
  39. MaxLength uint
  40. }
  41. type layoutAttr struct {
  42. Type string
  43. }
  44. const (
  45. descPanel = "Panel"
  46. descLabel = "Label"
  47. descEdit = "Edit"
  48. )
  49. //
  50. // NewBuilder creates and returns a pointer to a new gui Builder object
  51. //
  52. func NewBuilder() *Builder {
  53. b := new(Builder)
  54. return b
  55. }
  56. //
  57. // BuildFromData builds gui objects from the specified description
  58. // in YAML format
  59. //
  60. func (b *Builder) BuildFromString(desc string) error {
  61. // Try assuming the description contains a single root panel
  62. var pd panelDesc
  63. err := yaml.Unmarshal([]byte(desc), &pd)
  64. if err != nil {
  65. return err
  66. }
  67. if pd.Type != "" {
  68. pan, err := b.build(&pd, "", nil)
  69. if err != nil {
  70. return err
  71. }
  72. b.panels = append(b.panels, pan)
  73. return nil
  74. }
  75. // Try assuming the description is a map of panels
  76. var pdm map[string]panelDesc
  77. err = yaml.Unmarshal([]byte(desc), &pdm)
  78. if err != nil {
  79. return err
  80. }
  81. // Builds each panel in the map
  82. for name, pd := range pdm {
  83. pan, err := b.build(&pd, name, nil)
  84. if err != nil {
  85. return err
  86. }
  87. b.panels = append(b.panels, pan)
  88. }
  89. return nil
  90. }
  91. //
  92. // BuildFromFile builds gui objects from the specified file which
  93. // must contain objects descriptions in YAML format
  94. //
  95. func (b *Builder) BuildFromFile(filepath string) error {
  96. // Reads all file data
  97. f, err := os.Open(filepath)
  98. if err != nil {
  99. return err
  100. }
  101. data, err := ioutil.ReadAll(f)
  102. if err != nil {
  103. return err
  104. }
  105. err = f.Close()
  106. if err != nil {
  107. return err
  108. }
  109. // Parses file data
  110. return b.BuildFromString(string(data))
  111. }
  112. func (b *Builder) Panels() []IPanel {
  113. return b.panels
  114. }
  115. func (b *Builder) Label(path string) (*Label, error) {
  116. return nil, nil
  117. }
  118. //
  119. // build builds gui objects from the specified description and its children recursively
  120. //
  121. func (b *Builder) build(pd *panelDesc, name string, parent *Panel) (IPanel, error) {
  122. fmt.Printf("\n%+v\n\n", pd)
  123. var err error
  124. var pan IPanel
  125. switch pd.Type {
  126. case descPanel:
  127. pan, err = b.buildPanel(pd)
  128. case descLabel:
  129. pan, err = b.buildLabel(pd)
  130. case descEdit:
  131. pan, err = b.buildEdit(pd)
  132. default:
  133. err = fmt.Errorf("Invalid panel type:%s", pd.Type)
  134. }
  135. if err != nil {
  136. return nil, err
  137. }
  138. if parent != nil {
  139. parent.Add(pan)
  140. }
  141. return pan, nil
  142. }
  143. func (b *Builder) buildPanel(pd *panelDesc) (IPanel, error) {
  144. log.Error("buildPanel:[%s]", pd.Borders)
  145. pan := NewPanel(pd.Width, pd.Height)
  146. // Set margin sizes
  147. bs, err := b.parseBorderSizes(pd.Margins)
  148. if err != nil {
  149. return nil, err
  150. }
  151. if bs != nil {
  152. pan.SetMarginsFrom(bs)
  153. }
  154. // Set border sizes
  155. bs, err = b.parseBorderSizes(pd.Borders)
  156. if err != nil {
  157. return nil, err
  158. }
  159. if bs != nil {
  160. pan.SetBordersFrom(bs)
  161. }
  162. // Set border color
  163. c, err := b.parseColor(pd.BorderColor)
  164. if err != nil {
  165. return nil, err
  166. }
  167. if c != nil {
  168. pan.SetBordersColor4(c)
  169. }
  170. // Set paddings sizes
  171. bs, err = b.parseBorderSizes(pd.Paddings)
  172. if err != nil {
  173. return nil, err
  174. }
  175. if bs != nil {
  176. pan.SetPaddingsFrom(bs)
  177. }
  178. // Set color
  179. c, err = b.parseColor(pd.Color)
  180. if err != nil {
  181. return nil, err
  182. }
  183. if c != nil {
  184. pan.SetColor4(c)
  185. }
  186. return pan, nil
  187. }
  188. func (b *Builder) buildLabel(pa *panelDesc) (IPanel, error) {
  189. label := NewLabel("df")
  190. return label, nil
  191. }
  192. func (b *Builder) buildEdit(pa *panelDesc) (IPanel, error) {
  193. return nil, nil
  194. }
  195. //
  196. // parseFloats parses a string with a list of floats with the specified size
  197. // and returns a slice. The specified size is 0 any number of floats is allowed.
  198. // The individual values can be separated by spaces or commas
  199. //
  200. func (b *Builder) parseFloats(field string, min, max int) ([]float32, error) {
  201. // Checks if field is empty
  202. field = strings.Trim(field, " ")
  203. if field == "" {
  204. return nil, nil
  205. }
  206. // Separate individual fields
  207. var parts []string
  208. if strings.Index(field, ",") < 0 {
  209. parts = strings.Split(field, " ")
  210. } else {
  211. parts = strings.Split(field, ",")
  212. }
  213. if len(parts) < min || len(parts) > max {
  214. return nil, fmt.Errorf("Invalid number(%d) of float32 fields in:[%s]", len(parts), field)
  215. }
  216. // Parse each field value and appends to slice
  217. var values []float32
  218. for i := 0; i < len(parts); i++ {
  219. val, err := strconv.ParseFloat(parts[i], 32)
  220. if err != nil {
  221. return nil, fmt.Errorf("Error parsing float32 field:[%s]: %s", field, err)
  222. }
  223. values = append(values, float32(val))
  224. }
  225. return values, nil
  226. }
  227. //
  228. // parseBorderSizes parses a string field which can contain one float value or
  229. // float values. In the first case all borders has the same width
  230. //
  231. func (b *Builder) parseBorderSizes(field string) (*BorderSizes, error) {
  232. va, err := b.parseFloats(field, 1, 4)
  233. if va == nil || err != nil {
  234. return nil, err
  235. }
  236. if len(va) == 1 {
  237. return &BorderSizes{va[0], va[0], va[0], va[0]}, nil
  238. }
  239. return &BorderSizes{va[0], va[1], va[2], va[3]}, nil
  240. }
  241. //
  242. // parseColor parses a field which can contain a color name or
  243. // a list of 3 or 4 float values for the color components
  244. //
  245. func (b *Builder) parseColor(field string) (*math32.Color4, error) {
  246. // Checks if field is empty
  247. field = strings.Trim(field, " ")
  248. if field == "" {
  249. return nil, nil
  250. }
  251. // Checks if field is a color name
  252. value := math32.ColorUint(field)
  253. if value != 0 {
  254. var c math32.Color
  255. c.SetName(field)
  256. return &math32.Color4{c.R, c.G, c.B, 1}, nil
  257. }
  258. // Accept 3 or 4 floats values
  259. va, err := b.parseFloats(field, 3, 4)
  260. if err != nil {
  261. return nil, err
  262. }
  263. if len(va) == 3 {
  264. return &math32.Color4{va[0], va[1], va[2], 1}, nil
  265. }
  266. return &math32.Color4{va[0], va[1], va[2], va[3]}, nil
  267. }