builder.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  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. "sort"
  10. "strconv"
  11. "strings"
  12. "github.com/g3n/engine/math32"
  13. "gopkg.in/yaml.v2"
  14. )
  15. // Builder builds GUI objects from a declarative description in YAML format
  16. type Builder struct {
  17. desc map[string]*panelDesc
  18. panels []IPanel // first level panels
  19. }
  20. type panelStyle struct {
  21. Borders string
  22. Paddings string
  23. BorderColor string
  24. BgColor string
  25. FgColor string
  26. }
  27. type panelStyles struct {
  28. Normal panelStyle
  29. Over panelStyle
  30. Focus panelStyle
  31. Pressed panelStyle
  32. Disabled panelStyle
  33. }
  34. type panelDesc struct {
  35. Type string
  36. Name string
  37. Posx float32
  38. Posy float32
  39. Width float32
  40. Height float32
  41. Margins string
  42. Borders string
  43. BorderColor string
  44. Paddings string
  45. Color string
  46. Enabled bool
  47. Visible bool
  48. Renderable bool
  49. Children []*panelDesc
  50. Layout layoutAttr
  51. Styles *panelStyles
  52. Text string
  53. FontSize *float32
  54. FontDPI *float32
  55. PlaceHolder string
  56. MaxLength *uint
  57. }
  58. type layoutAttr struct {
  59. Type string
  60. }
  61. const (
  62. descTypePanel = "Panel"
  63. descTypeLabel = "Label"
  64. descTypeEdit = "Edit"
  65. fieldMargins = "margins"
  66. fieldBorders = "borders"
  67. fieldBorderColor = "bordercolor"
  68. fieldPaddings = "paddings"
  69. fieldColor = "color"
  70. )
  71. //
  72. // NewBuilder creates and returns a pointer to a new gui Builder object
  73. //
  74. func NewBuilder() *Builder {
  75. b := new(Builder)
  76. return b
  77. }
  78. //
  79. // ParseString parses a string with gui objects descriptions in YAML format
  80. // It there was a previously parsed description, it is cleared.
  81. //
  82. func (b *Builder) ParseString(desc string) error {
  83. // Try assuming the description contains a single root panel
  84. var pd panelDesc
  85. err := yaml.Unmarshal([]byte(desc), &pd)
  86. if err != nil {
  87. return err
  88. }
  89. if pd.Type != "" {
  90. b.desc = make(map[string]*panelDesc)
  91. b.desc[""] = &pd
  92. fmt.Printf("\n%+v\n", b.desc)
  93. return nil
  94. }
  95. // Try assuming the description is a map of panels
  96. var pdm map[string]*panelDesc
  97. err = yaml.Unmarshal([]byte(desc), &pdm)
  98. if err != nil {
  99. return err
  100. }
  101. b.desc = pdm
  102. fmt.Printf("\n%+v\n", b.desc)
  103. return nil
  104. }
  105. //
  106. // ParseFile builds gui objects from the specified file which
  107. // must contain objects descriptions in YAML format
  108. //
  109. func (b *Builder) ParseFile(filepath string) error {
  110. // Reads all file data
  111. f, err := os.Open(filepath)
  112. if err != nil {
  113. return err
  114. }
  115. data, err := ioutil.ReadAll(f)
  116. if err != nil {
  117. return err
  118. }
  119. err = f.Close()
  120. if err != nil {
  121. return err
  122. }
  123. // Parses file data
  124. return b.ParseString(string(data))
  125. }
  126. //
  127. // Names returns a sorted list of names of top level previously parsed objects.
  128. // If there is only a single object with no name, its name is returned
  129. // as an empty string
  130. //
  131. func (b *Builder) Names() []string {
  132. var objs []string
  133. for name, _ := range b.desc {
  134. objs = append(objs, name)
  135. }
  136. sort.Strings(objs)
  137. return objs
  138. }
  139. //
  140. // Build builds a gui object and all its children recursively.
  141. // The specified name should be a top level name from a
  142. // from a previously parsed description
  143. // If the descriptions contains a single object with no name,
  144. // It should be specified the empty string to build this object.
  145. //
  146. func (b *Builder) Build(name string) (IPanel, error) {
  147. pd, ok := b.desc[name]
  148. if !ok {
  149. return nil, fmt.Errorf("Object name:%s not found", name)
  150. }
  151. return b.build(pd, name, nil)
  152. }
  153. //
  154. // build builds gui objects from the specified description and its children recursively
  155. //
  156. func (b *Builder) build(pd *panelDesc, pname string, parent *Panel) (IPanel, error) {
  157. fmt.Printf("\n%+v\n\n", pd)
  158. var err error
  159. var pan IPanel
  160. switch pd.Type {
  161. case descTypePanel:
  162. pan, err = b.buildPanel(pd, pname)
  163. case descTypeLabel:
  164. pan, err = b.buildLabel(pd, pname)
  165. case descTypeEdit:
  166. pan, err = b.buildEdit(pd, pname)
  167. default:
  168. err = fmt.Errorf("Invalid panel type:%s", pd.Type)
  169. }
  170. if err != nil {
  171. return nil, err
  172. }
  173. if parent != nil {
  174. parent.Add(pan)
  175. }
  176. return pan, nil
  177. }
  178. func (b *Builder) buildPanel(pd *panelDesc, pname string) (IPanel, error) {
  179. log.Error("buildPanel:[%s]", pd.Borders)
  180. pan := NewPanel(pd.Width, pd.Height)
  181. pan.SetPosition(pd.Posx, pd.Posy)
  182. // Set margin sizes
  183. bs, err := b.parseBorderSizes(pname, fieldMargins, pd.Margins)
  184. if err != nil {
  185. return nil, err
  186. }
  187. if bs != nil {
  188. pan.SetMarginsFrom(bs)
  189. }
  190. // Set border sizes
  191. bs, err = b.parseBorderSizes(pname, fieldBorders, pd.Borders)
  192. if err != nil {
  193. return nil, err
  194. }
  195. if bs != nil {
  196. pan.SetBordersFrom(bs)
  197. }
  198. // Set border color
  199. c, err := b.parseColor(pname, fieldBorderColor, pd.BorderColor)
  200. if err != nil {
  201. return nil, err
  202. }
  203. if c != nil {
  204. pan.SetBordersColor4(c)
  205. }
  206. // Set paddings sizes
  207. bs, err = b.parseBorderSizes(pname, fieldPaddings, pd.Paddings)
  208. if err != nil {
  209. return nil, err
  210. }
  211. if bs != nil {
  212. pan.SetPaddingsFrom(bs)
  213. }
  214. // Set color
  215. c, err = b.parseColor(pname, fieldColor, pd.Color)
  216. if err != nil {
  217. return nil, err
  218. }
  219. if c != nil {
  220. pan.SetColor4(c)
  221. }
  222. // Children
  223. for i := 0; i < len(pd.Children); i++ {
  224. child, err := b.build(pd.Children[i], pname, pan)
  225. if err != nil {
  226. return nil, err
  227. }
  228. pan.Add(child)
  229. }
  230. return pan, nil
  231. }
  232. func (b *Builder) buildLabel(pd *panelDesc, name string) (IPanel, error) {
  233. label := NewLabel(pd.Text)
  234. label.SetPosition(pd.Posx, pd.Posy)
  235. log.Error("label pos:%v", label.Position())
  236. return label, nil
  237. }
  238. func (b *Builder) buildEdit(pa *panelDesc, name string) (IPanel, error) {
  239. return nil, nil
  240. }
  241. //
  242. // parseBorderSizes parses a string field which can contain one float value or
  243. // float values. In the first case all borders has the same width
  244. //
  245. func (b *Builder) parseBorderSizes(pname, fname, field string) (*BorderSizes, error) {
  246. va, err := b.parseFloats(pname, fname, field, 1, 4)
  247. if va == nil || err != nil {
  248. return nil, err
  249. }
  250. if len(va) == 1 {
  251. return &BorderSizes{va[0], va[0], va[0], va[0]}, nil
  252. }
  253. return &BorderSizes{va[0], va[1], va[2], va[3]}, nil
  254. }
  255. //
  256. // parseColor parses a string field which can contain a color name or
  257. // a list of 3 or 4 float values for the color components
  258. //
  259. func (b *Builder) parseColor(pname, fname, field string) (*math32.Color4, error) {
  260. // Checks if field is empty
  261. field = strings.Trim(field, " ")
  262. if field == "" {
  263. return nil, nil
  264. }
  265. // Checks if field is a color name
  266. value := math32.ColorUint(field)
  267. if value != 0 {
  268. var c math32.Color
  269. c.SetName(field)
  270. return &math32.Color4{c.R, c.G, c.B, 1}, nil
  271. }
  272. // Accept 3 or 4 floats values
  273. va, err := b.parseFloats(pname, fname, field, 3, 4)
  274. if err != nil {
  275. return nil, err
  276. }
  277. if len(va) == 3 {
  278. return &math32.Color4{va[0], va[1], va[2], 1}, nil
  279. }
  280. return &math32.Color4{va[0], va[1], va[2], va[3]}, nil
  281. }
  282. //
  283. // parseFloats parses a string with a list of floats with the specified size
  284. // and returns a slice. The specified size is 0 any number of floats is allowed.
  285. // The individual values can be separated by spaces or commas
  286. //
  287. func (b *Builder) parseFloats(pname, fname, field string, min, max int) ([]float32, error) {
  288. // Checks if field is empty
  289. field = strings.Trim(field, " ")
  290. if field == "" {
  291. return nil, nil
  292. }
  293. // Separate individual fields
  294. var parts []string
  295. if strings.Index(field, ",") < 0 {
  296. parts = strings.Split(field, " ")
  297. } else {
  298. parts = strings.Split(field, ",")
  299. }
  300. if len(parts) < min || len(parts) > max {
  301. return nil, b.err(pname, fname, "Invalid number of float32 values")
  302. }
  303. // Parse each field value and appends to slice
  304. var values []float32
  305. for i := 0; i < len(parts); i++ {
  306. val, err := strconv.ParseFloat(parts[i], 32)
  307. if err != nil {
  308. return nil, fmt.Errorf("Error parsing float32 field:[%s]: %s", field, err)
  309. }
  310. values = append(values, float32(val))
  311. }
  312. return values, nil
  313. }
  314. func (b *Builder) err(pname, fname, msg string) error {
  315. return fmt.Errorf("Error in object:%s field:%s -> %s", pname, fname, msg)
  316. }