builder.go 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109
  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/gui/assets/icon"
  13. "github.com/g3n/engine/math32"
  14. "github.com/g3n/engine/window"
  15. "gopkg.in/yaml.v2"
  16. )
  17. // Builder builds GUI objects from a declarative description in YAML format
  18. type Builder struct {
  19. am map[string]interface{} // parsed attribute map
  20. builders map[string]BuilderFunc // map of builder functions by type
  21. attribs map[string]AttribCheckFunc // map of attribute name with check functions
  22. layouts map[string]IBuilderLayout // map of layout type to layout builder
  23. imgpath string // base path for image panels files
  24. }
  25. // IBuilderLayout is the interface for all layout builders
  26. type IBuilderLayout interface {
  27. BuildLayout(b *Builder, am map[string]interface{}) (ILayout, error)
  28. BuildParams(b *Builder, am map[string]interface{}) (interface{}, error)
  29. }
  30. // BuilderFunc is type for functions which build a gui object from an attribute map
  31. type BuilderFunc func(*Builder, map[string]interface{}) (IPanel, error)
  32. // AttribCheckFunc is the type for all attribute check functions
  33. type AttribCheckFunc func(b *Builder, am map[string]interface{}, fname string) error
  34. // IgnoreSuffix specified the suffix of ignored keys
  35. const IgnoreSuffix = "_"
  36. // Panel and layout types
  37. const (
  38. TypePanel = "panel"
  39. TypeImagePanel = "imagepanel"
  40. TypeLabel = "label"
  41. TypeImageLabel = "imagelabel"
  42. TypeButton = "button"
  43. TypeCheckBox = "checkbox"
  44. TypeRadioButton = "radiobutton"
  45. TypeEdit = "edit"
  46. TypeVList = "vlist"
  47. TypeHList = "hlist"
  48. TypeDropDown = "dropdown"
  49. TypeHSlider = "hslider"
  50. TypeVSlider = "vslider"
  51. TypeHSplitter = "hsplitter"
  52. TypeVSplitter = "vsplitter"
  53. TypeSeparator = "separator"
  54. TypeTree = "tree"
  55. TypeTreeNode = "node"
  56. TypeMenuBar = "menubar"
  57. TypeMenu = "menu"
  58. TypeWindow = "window"
  59. TypeChart = "chart"
  60. TypeTable = "table"
  61. TypeHBoxLayout = "hbox"
  62. TypeVBoxLayout = "vbox"
  63. TypeGridLayout = "grid"
  64. TypeDockLayout = "dock"
  65. )
  66. // Common attribute names
  67. const (
  68. AttribAlignv = "alignv" // Align
  69. AttribAlignh = "alignh" // Align
  70. AttribAspectHeight = "aspectheight" // float32
  71. AttribAspectWidth = "aspectwidth" // float32
  72. AttribBgColor = "bgcolor" // Color4
  73. AttribBorders = "borders" // BorderSizes
  74. AttribBorderColor = "bordercolor" // Color4
  75. AttribChecked = "checked" // bool
  76. AttribColor = "color" // Color4
  77. AttribCols = "cols" // int GridLayout
  78. AttribColSpan = "colspan" // int GridLayout
  79. AttribColumns = "columns" // []map[string]interface{} Table
  80. AttribCountStepx = "countstepx" // float32
  81. AttribEdge = "edge" // int
  82. AttribEnabled = "enabled" // bool
  83. AttribExpand = "expand" // float32
  84. AttribExpandh = "expandh" // bool
  85. AttribExpandv = "expandv" // bool
  86. AttribFirstx = "firstx" // float32
  87. AttribFontColor = "fontcolor" // Color4
  88. AttribFontDPI = "fontdpi" // float32
  89. AttribFontSize = "fontsize" // float32
  90. AttribFormat = "format" // string
  91. AttribGroup = "group" // string
  92. AttribHeight = "height" // float32
  93. AttribIcon = "icon" // string
  94. AttribImageFile = "imagefile" // string
  95. AttribImageLabel = "imagelabel" // []map[string]interface{}
  96. AttribItems = "items" // []map[string]interface{}
  97. AttribLayout = "layout" // map[string]interface{}
  98. AttribLayoutParams = "layoutparams" // map[string]interface{}
  99. AttribLineSpacing = "linespacing" // float32
  100. AttribLines = "lines" // int
  101. AttribMargin = "margin" // float32
  102. AttribMargins = "margins" // BorderSizes
  103. AttribMinHeight = "minheight" // bool
  104. AttribMinWidth = "minwidth" // bool
  105. AttribName = "name" // string
  106. AttribPaddings = "paddings" // BorderSizes
  107. AttribPanel0 = "panel0" // map[string]interface{}
  108. AttribPanel1 = "panel1" // map[string]interface{}
  109. AttribParentInternal = "parent_" // string (internal attribute)
  110. AttribPlaceHolder = "placeholder" // string
  111. AttribPosition = "position" // []float32
  112. AttribRangeAuto = "rangeauto" // bool
  113. AttribRangeMin = "rangemin" // float32
  114. AttribRangeMax = "rangemax" // float32
  115. AttribRender = "render" // bool
  116. AttribResizable = "resizable" // Resizable
  117. AttribScaleFactor = "scalefactor" // float32
  118. AttribScalex = "scalex" // map[string]interface{}
  119. AttribScaley = "scaley" // map[string]interface{}
  120. AttribShortcut = "shortcut" // []int
  121. AttribSpacing = "spacing" // float32
  122. AttribSplit = "split" // float32
  123. AttribStepx = "stepx" // float32
  124. AttribText = "text" // string
  125. AttribTitle = "title" // string
  126. AttribType = "type" // string
  127. AttribWidth = "width" // float32
  128. AttribValue = "value" // float32
  129. AttribVisible = "visible" // bool
  130. )
  131. const (
  132. aPOS = 1 << iota // attribute position
  133. aSIZE = 1 << iota // attribute size
  134. aNAME = 1 << iota // attribute name
  135. aMARGINS = 1 << iota // attribute margins widths
  136. aBORDERS = 1 << iota // attribute borders widths
  137. aBORDERCOLOR = 1 << iota // attribute border color
  138. aPADDINGS = 1 << iota // attribute paddings widths
  139. aCOLOR = 1 << iota // attribute panel bgcolor
  140. aENABLED = 1 << iota // attribute enabled for events
  141. aRENDER = 1 << iota // attribute renderable
  142. aVISIBLE = 1 << iota // attribute visible
  143. asPANEL = 0xFF // attribute set for panels
  144. asWIDGET = aPOS | aNAME | aSIZE | aENABLED | aVISIBLE // attribute set for widgets
  145. )
  146. // maps align name with align parameter
  147. var mapAlignh = map[string]Align{
  148. "none": AlignNone,
  149. "left": AlignLeft,
  150. "right": AlignRight,
  151. "width": AlignWidth,
  152. "center": AlignCenter,
  153. }
  154. // maps align name with align parameter
  155. var mapAlignv = map[string]Align{
  156. "none": AlignNone,
  157. "top": AlignTop,
  158. "bottom": AlignBottom,
  159. "height": AlignHeight,
  160. "center": AlignCenter,
  161. }
  162. // maps edge name (dock layout) with edge parameter
  163. var mapEdgeName = map[string]int{
  164. "top": DockTop,
  165. "right": DockRight,
  166. "bottom": DockBottom,
  167. "left": DockLeft,
  168. "center": DockCenter,
  169. }
  170. // maps resize border name (window) with parameter value
  171. var mapResizable = map[string]Resizable{
  172. "top": ResizeTop,
  173. "right": ResizeRight,
  174. "bottom": ResizeBottom,
  175. "left": ResizeLeft,
  176. "all": ResizeAll,
  177. }
  178. // NewBuilder creates and returns a pointer to a new gui Builder object
  179. func NewBuilder() *Builder {
  180. b := new(Builder)
  181. // Sets map of object type to builder function
  182. b.builders = map[string]BuilderFunc{
  183. TypePanel: buildPanel,
  184. TypeImagePanel: buildImagePanel,
  185. TypeLabel: buildLabel,
  186. TypeImageLabel: buildImageLabel,
  187. TypeButton: buildButton,
  188. TypeEdit: buildEdit,
  189. TypeCheckBox: buildCheckBox,
  190. TypeRadioButton: buildRadioButton,
  191. TypeVList: buildVList,
  192. TypeHList: buildHList,
  193. TypeDropDown: buildDropDown,
  194. TypeMenu: buildMenu,
  195. TypeMenuBar: buildMenu,
  196. TypeHSlider: buildSlider,
  197. TypeVSlider: buildSlider,
  198. TypeHSplitter: buildSplitter,
  199. TypeVSplitter: buildSplitter,
  200. TypeTree: buildTree,
  201. TypeWindow: buildWindow,
  202. TypeChart: buildChart,
  203. TypeTable: buildTable,
  204. }
  205. // Sets map of layout type name to layout function
  206. b.layouts = map[string]IBuilderLayout{
  207. TypeHBoxLayout: &BuilderLayoutHBox{},
  208. TypeVBoxLayout: &BuilderLayoutVBox{},
  209. TypeGridLayout: &BuilderLayoutGrid{},
  210. TypeDockLayout: &BuilderLayoutDock{},
  211. }
  212. // Sets map of attribute name to check function
  213. b.attribs = map[string]AttribCheckFunc{
  214. AttribAlignv: AttribCheckAlign,
  215. AttribAlignh: AttribCheckAlign,
  216. AttribAspectWidth: AttribCheckFloat,
  217. AttribAspectHeight: AttribCheckFloat,
  218. AttribHeight: AttribCheckFloat,
  219. AttribMargin: AttribCheckFloat,
  220. AttribMargins: AttribCheckBorderSizes,
  221. AttribBgColor: AttribCheckColor,
  222. AttribBorders: AttribCheckBorderSizes,
  223. AttribBorderColor: AttribCheckColor,
  224. AttribChecked: AttribCheckBool,
  225. AttribColor: AttribCheckColor,
  226. AttribCols: AttribCheckInt,
  227. AttribColSpan: AttribCheckInt,
  228. AttribCountStepx: AttribCheckFloat,
  229. AttribEdge: AttribCheckEdge,
  230. AttribEnabled: AttribCheckBool,
  231. AttribExpand: AttribCheckFloat,
  232. AttribExpandh: AttribCheckBool,
  233. AttribExpandv: AttribCheckBool,
  234. AttribFirstx: AttribCheckFloat,
  235. AttribFontColor: AttribCheckColor,
  236. AttribFontDPI: AttribCheckFloat,
  237. AttribFontSize: AttribCheckFloat,
  238. AttribFormat: AttribCheckString,
  239. AttribGroup: AttribCheckString,
  240. AttribIcon: AttribCheckIcons,
  241. AttribImageFile: AttribCheckString,
  242. AttribImageLabel: AttribCheckMap,
  243. AttribItems: AttribCheckListMap,
  244. AttribLayout: AttribCheckLayout,
  245. AttribLayoutParams: AttribCheckMap,
  246. AttribLineSpacing: AttribCheckFloat,
  247. AttribLines: AttribCheckInt,
  248. AttribMinHeight: AttribCheckBool,
  249. AttribMinWidth: AttribCheckBool,
  250. AttribName: AttribCheckString,
  251. AttribPaddings: AttribCheckBorderSizes,
  252. AttribPanel0: AttribCheckMap,
  253. AttribPanel1: AttribCheckMap,
  254. AttribPlaceHolder: AttribCheckString,
  255. AttribPosition: AttribCheckPosition,
  256. AttribRangeAuto: AttribCheckBool,
  257. AttribRangeMin: AttribCheckFloat,
  258. AttribRangeMax: AttribCheckFloat,
  259. AttribRender: AttribCheckBool,
  260. AttribResizable: AttribCheckResizable,
  261. AttribScaleFactor: AttribCheckFloat,
  262. AttribScalex: AttribCheckMap,
  263. AttribScaley: AttribCheckMap,
  264. AttribShortcut: AttribCheckMenuShortcut,
  265. AttribSpacing: AttribCheckFloat,
  266. AttribSplit: AttribCheckFloat,
  267. AttribStepx: AttribCheckFloat,
  268. AttribText: AttribCheckString,
  269. AttribTitle: AttribCheckString,
  270. AttribType: AttribCheckStringLower,
  271. AttribValue: AttribCheckFloat,
  272. AttribVisible: AttribCheckBool,
  273. AttribWidth: AttribCheckFloat,
  274. }
  275. return b
  276. }
  277. // ParseString parses a string with gui objects descriptions in YAML format
  278. // It there was a previously parsed description, it is cleared.
  279. func (b *Builder) ParseString(desc string) error {
  280. // Parses descriptor string in YAML format saving result in
  281. // a map of interface{} to interface{} as YAML allows numeric keys.
  282. var mii map[interface{}]interface{}
  283. err := yaml.Unmarshal([]byte(desc), &mii)
  284. if err != nil {
  285. return err
  286. }
  287. // If all the values of the top level map keys are other maps,
  288. // then it is a description of several objects, otherwise it is
  289. // a description of a single object.
  290. single := false
  291. for _, v := range mii {
  292. _, ok := v.(map[interface{}]interface{})
  293. if !ok {
  294. single = true
  295. break
  296. }
  297. }
  298. // Internal function which converts map[interface{}]interface{} to
  299. // map[string]interface{} recursively and lower case of all map keys.
  300. // It also sets a field named "parent_", which pointer to the parent map
  301. // This field causes a circular reference in the result map which prevents
  302. // the use of Go's Printf to print the result map.
  303. var visitor func(v, par interface{}) (interface{}, error)
  304. visitor = func(v, par interface{}) (interface{}, error) {
  305. switch vt := v.(type) {
  306. case []interface{}:
  307. ls := []interface{}{}
  308. for _, item := range vt {
  309. ci, err := visitor(item, par)
  310. if err != nil {
  311. return nil, err
  312. }
  313. ls = append(ls, ci)
  314. }
  315. return ls, nil
  316. case map[interface{}]interface{}:
  317. ms := make(map[string]interface{})
  318. for k, v := range vt {
  319. // Checks key
  320. ks, ok := k.(string)
  321. if !ok {
  322. return nil, fmt.Errorf("Keys must be strings")
  323. }
  324. ks = strings.ToLower(ks)
  325. // Ignores keys suffixed by IgnoreSuffix
  326. if strings.HasSuffix(ks, IgnoreSuffix) {
  327. continue
  328. }
  329. // Checks value
  330. vi, err := visitor(v, ms)
  331. if err != nil {
  332. return nil, err
  333. }
  334. ms[ks] = vi
  335. // If has panel has parent or is a single top level panel, checks attributes
  336. if par != nil || single {
  337. // Get attribute check function
  338. acf, ok := b.attribs[ks]
  339. if !ok {
  340. return nil, fmt.Errorf("Invalid attribute:%s", ks)
  341. }
  342. // Checks attribute
  343. err = acf(b, ms, ks)
  344. if err != nil {
  345. return nil, err
  346. }
  347. }
  348. }
  349. if par != nil {
  350. ms[AttribParentInternal] = par
  351. }
  352. return ms, nil
  353. default:
  354. return v, nil
  355. }
  356. return nil, nil
  357. }
  358. // Get map[string]interface{} with lower case keys from parsed descritor
  359. res, err := visitor(mii, nil)
  360. if err != nil {
  361. return err
  362. }
  363. msi, ok := res.(map[string]interface{})
  364. if !ok {
  365. return fmt.Errorf("Parsed result is not a map")
  366. }
  367. b.am = msi
  368. //b.debugPrint(b.am, 1)
  369. return nil
  370. }
  371. // ParseFile parses a file with gui objects descriptions in YAML format
  372. // It there was a previously parsed description, it is cleared.
  373. func (b *Builder) ParseFile(filepath string) error {
  374. // Reads all file data
  375. f, err := os.Open(filepath)
  376. if err != nil {
  377. return err
  378. }
  379. data, err := ioutil.ReadAll(f)
  380. if err != nil {
  381. return err
  382. }
  383. err = f.Close()
  384. if err != nil {
  385. return err
  386. }
  387. // Parses file data
  388. return b.ParseString(string(data))
  389. }
  390. // Names returns a sorted list of names of top level previously parsed objects.
  391. // Only objects with defined types are returned.
  392. // If there is only a single object with no name, its name is returned
  393. // as an empty string
  394. func (b *Builder) Names() []string {
  395. var objs []string
  396. // Single object
  397. if b.am[AttribType] != nil {
  398. objs = append(objs, "")
  399. return objs
  400. }
  401. // Multiple objects
  402. for name := range b.am {
  403. objs = append(objs, name)
  404. }
  405. sort.Strings(objs)
  406. return objs
  407. }
  408. // Build builds a gui object and all its children recursively.
  409. // The specified name should be a top level name from a
  410. // from a previously parsed description
  411. // If the descriptions contains a single object with no name,
  412. // It should be specified the empty string to build this object.
  413. func (b *Builder) Build(name string) (IPanel, error) {
  414. // Only one object
  415. if name == "" {
  416. return b.build(b.am, nil)
  417. }
  418. // Map of gui objects
  419. am, ok := b.am[name]
  420. if !ok {
  421. return nil, fmt.Errorf("Object name:%s not found", name)
  422. }
  423. return b.build(am.(map[string]interface{}), nil)
  424. }
  425. // SetImagepath Sets the path for image panels relative image files
  426. func (b *Builder) SetImagepath(path string) {
  427. b.imgpath = path
  428. }
  429. // AddBuilderPanel adds a panel builder function for the specified type name.
  430. // If the type name already exists it is replaced.
  431. func (b *Builder) AddBuilderPanel(typename string, bf BuilderFunc) {
  432. b.builders[typename] = bf
  433. }
  434. // AddBuilderLayout adds a layout builder object for the specified type name.
  435. // If the type name already exists it is replaced.
  436. func (b *Builder) AddBuilderLayout(typename string, bl IBuilderLayout) {
  437. b.layouts[typename] = bl
  438. }
  439. // AddAttrib adds an attribute type and its checker/converte
  440. // If the attribute type name already exists it is replaced.
  441. func (b *Builder) AddAttrib(typename string, acf AttribCheckFunc) {
  442. b.attribs[typename] = acf
  443. }
  444. // build builds the gui object from the specified description.
  445. // All its children are also built recursively
  446. // Returns the built object or an error
  447. func (b *Builder) build(am map[string]interface{}, iparent IPanel) (IPanel, error) {
  448. // Get panel type
  449. itype := am[AttribType]
  450. if itype == nil {
  451. return nil, fmt.Errorf("Type not specified")
  452. }
  453. typename := itype.(string)
  454. // Get builder function for this type name
  455. builder := b.builders[typename]
  456. if builder == nil {
  457. return nil, fmt.Errorf("Invalid type:%v", typename)
  458. }
  459. // Builds panel
  460. pan, err := builder(b, am)
  461. if err != nil {
  462. return nil, err
  463. }
  464. // Adds built panel to parent
  465. if iparent != nil {
  466. iparent.GetPanel().Add(pan)
  467. }
  468. return pan, nil
  469. }
  470. // setAttribs sets common attributes from the description to the specified panel
  471. // The attributes which are set can be specified by the specified bitmask.
  472. func (b *Builder) setAttribs(am map[string]interface{}, ipan IPanel, attr uint) error {
  473. panel := ipan.GetPanel()
  474. // Set optional position
  475. if attr&aPOS != 0 && am[AttribPosition] != nil {
  476. va := am[AttribPosition].([]float32)
  477. panel.SetPosition(va[0], va[1])
  478. }
  479. // Set optional panel width
  480. if attr&aSIZE != 0 && am[AttribWidth] != nil {
  481. panel.SetWidth(am[AttribWidth].(float32))
  482. }
  483. // Sets optional panel height
  484. if attr&aSIZE != 0 && am[AttribHeight] != nil {
  485. panel.SetHeight(am[AttribHeight].(float32))
  486. }
  487. // Set optional margin sizes
  488. if attr&aMARGINS != 0 && am[AttribMargins] != nil {
  489. panel.SetMarginsFrom(am[AttribMargins].(*BorderSizes))
  490. }
  491. // Set optional border sizes
  492. if attr&aBORDERS != 0 && am[AttribBorders] != nil {
  493. panel.SetBordersFrom(am[AttribBorders].(*BorderSizes))
  494. }
  495. // Set optional border color
  496. if attr&aBORDERCOLOR != 0 && am[AttribBorderColor] != nil {
  497. panel.SetBordersColor4(am[AttribBorderColor].(*math32.Color4))
  498. }
  499. // Set optional paddings sizes
  500. if attr&aPADDINGS != 0 && am[AttribPaddings] != nil {
  501. panel.SetPaddingsFrom(am[AttribPaddings].(*BorderSizes))
  502. }
  503. // Set optional panel color
  504. if attr&aCOLOR != 0 && am[AttribColor] != nil {
  505. panel.SetColor4(am[AttribColor].(*math32.Color4))
  506. }
  507. if attr&aNAME != 0 && am[AttribName] != nil {
  508. panel.SetName(am[AttribName].(string))
  509. }
  510. if attr&aVISIBLE != 0 && am[AttribVisible] != nil {
  511. panel.SetVisible(am[AttribVisible].(bool))
  512. }
  513. if attr&aENABLED != 0 && am[AttribEnabled] != nil {
  514. panel.SetEnabled(am[AttribEnabled].(bool))
  515. }
  516. if attr&aRENDER != 0 && am[AttribRender] != nil {
  517. panel.SetRenderable(am[AttribRender].(bool))
  518. }
  519. // Sets optional layout (must pass IPanel not *Panel)
  520. err := b.setLayout(am, ipan)
  521. if err != nil {
  522. return nil
  523. }
  524. // Sets optional layout params
  525. err = b.setLayoutParams(am, panel)
  526. return err
  527. }
  528. // setLayout sets the optional layout of the specified panel
  529. func (b *Builder) setLayout(am map[string]interface{}, ipan IPanel) error {
  530. // Get layout type
  531. lai := am[AttribLayout]
  532. if lai == nil {
  533. return nil
  534. }
  535. lam := lai.(map[string]interface{})
  536. ltype := lam[AttribType]
  537. if ltype == nil {
  538. return b.err(am, AttribType, "Layout must have a type")
  539. }
  540. // Get layout builder
  541. lbuilder := b.layouts[ltype.(string)]
  542. if lbuilder == nil {
  543. return b.err(am, AttribType, "Invalid layout type")
  544. }
  545. // Builds layout builder and set to panel
  546. layout, err := lbuilder.BuildLayout(b, lam)
  547. if err != nil {
  548. return err
  549. }
  550. ipan.SetLayout(layout)
  551. return nil
  552. }
  553. // setLayoutParams sets the optional layout params of the specified panel and its attributes
  554. func (b *Builder) setLayoutParams(am map[string]interface{}, ipan IPanel) error {
  555. // Get layout params attributes
  556. lpi := am[AttribLayoutParams]
  557. if lpi == nil {
  558. return nil
  559. }
  560. lp := lpi.(map[string]interface{})
  561. // Get layout type from parent
  562. pi := am[AttribParentInternal]
  563. if pi == nil {
  564. return b.err(am, AttribType, "Panel has no parent")
  565. }
  566. par := pi.(map[string]interface{})
  567. v := par[AttribLayout]
  568. if v == nil {
  569. return nil
  570. }
  571. playout := v.(map[string]interface{})
  572. pltype := playout[AttribType].(string)
  573. // Get layout builder and builds layout params
  574. lbuilder := b.layouts[pltype]
  575. params, err := lbuilder.BuildParams(b, lp)
  576. if err != nil {
  577. return err
  578. }
  579. ipan.GetPanel().SetLayoutParams(params)
  580. return nil
  581. }
  582. // AttribCheckResizable checks and converts attribute with list of window resizable borders
  583. func AttribCheckResizable(b *Builder, am map[string]interface{}, fname string) error {
  584. // If attribute not found, ignore
  585. v := am[fname]
  586. if v == nil {
  587. return nil
  588. }
  589. // Attribute must be string
  590. vs, ok := v.(string)
  591. if !ok {
  592. return b.err(am, fname, "Invalid resizable attribute")
  593. }
  594. // Each string field must be a valid resizable name
  595. parts := strings.Fields(vs)
  596. var res Resizable
  597. for _, name := range parts {
  598. v, ok := mapResizable[name]
  599. if !ok {
  600. return b.err(am, fname, "Invalid resizable name:"+name)
  601. }
  602. res |= v
  603. }
  604. am[fname] = res
  605. return nil
  606. }
  607. // AttribCheckEdge checks and converts attribute with name of layout edge
  608. func AttribCheckEdge(b *Builder, am map[string]interface{}, fname string) error {
  609. v := am[fname]
  610. if v == nil {
  611. return nil
  612. }
  613. vs, ok := v.(string)
  614. if !ok {
  615. return b.err(am, fname, "Invalid edge name")
  616. }
  617. edge, ok := mapEdgeName[vs]
  618. if !ok {
  619. return b.err(am, fname, "Invalid edge name")
  620. }
  621. am[fname] = edge
  622. return nil
  623. }
  624. // AttribCheckLayout checks and converts layout attribute
  625. func AttribCheckLayout(b *Builder, am map[string]interface{}, fname string) error {
  626. v := am[fname]
  627. if v == nil {
  628. return nil
  629. }
  630. msi, ok := v.(map[string]interface{})
  631. if !ok {
  632. return b.err(am, fname, "Not a map")
  633. }
  634. lti := msi[AttribType]
  635. if lti == nil {
  636. return b.err(am, fname, "Layout must have a type")
  637. }
  638. lfunc := b.layouts[lti.(string)]
  639. if lfunc == nil {
  640. return b.err(am, fname, "Invalid layout type")
  641. }
  642. return nil
  643. }
  644. // AttribCheckAlign checks and converts layout align* attribute
  645. func AttribCheckAlign(b *Builder, am map[string]interface{}, fname string) error {
  646. v := am[fname]
  647. if v == nil {
  648. return nil
  649. }
  650. vs, ok := v.(string)
  651. if !ok {
  652. return b.err(am, fname, "Invalid alignment")
  653. }
  654. var align Align
  655. if fname == AttribAlignh {
  656. align, ok = mapAlignh[vs]
  657. } else {
  658. align, ok = mapAlignv[vs]
  659. }
  660. if !ok {
  661. return b.err(am, fname, "Invalid alignment")
  662. }
  663. am[fname] = align
  664. return nil
  665. }
  666. // AttribCheckMenuShortcut checks and converts attribute describing menu shortcut key
  667. func AttribCheckMenuShortcut(b *Builder, am map[string]interface{}, fname string) error {
  668. v := am[fname]
  669. if v == nil {
  670. return nil
  671. }
  672. vs, ok := v.(string)
  673. if !ok {
  674. return b.err(am, fname, "Not a string")
  675. }
  676. sc := strings.Trim(vs, " ")
  677. if sc == "" {
  678. return nil
  679. }
  680. parts := strings.Split(sc, "+")
  681. var mods window.ModifierKey
  682. for i := 0; i < len(parts)-1; i++ {
  683. switch parts[i] {
  684. case "Shift":
  685. mods |= window.ModShift
  686. case "Ctrl":
  687. mods |= window.ModControl
  688. case "Alt":
  689. mods |= window.ModAlt
  690. default:
  691. return b.err(am, fname, "Invalid shortcut:"+sc)
  692. }
  693. }
  694. // The last part must be a key
  695. keyname := parts[len(parts)-1]
  696. var keycode int
  697. found := false
  698. for kcode, kname := range mapKeyText {
  699. if kname == keyname {
  700. keycode = int(kcode)
  701. found = true
  702. break
  703. }
  704. }
  705. if !found {
  706. return b.err(am, fname, "Invalid shortcut:"+sc)
  707. }
  708. am[fname] = []int{int(mods), keycode}
  709. return nil
  710. }
  711. // AttribCheckListMap checks and converts attribute to []map[string]interface{}
  712. func AttribCheckListMap(b *Builder, am map[string]interface{}, fname string) error {
  713. v := am[fname]
  714. if v == nil {
  715. return nil
  716. }
  717. li, ok := v.([]interface{})
  718. if !ok {
  719. return b.err(am, fname, "Not a list")
  720. }
  721. lmsi := make([]map[string]interface{}, 0)
  722. for i := 0; i < len(li); i++ {
  723. item := li[i]
  724. msi, ok := item.(map[string]interface{})
  725. if !ok {
  726. return b.err(am, fname, "Item is not a map")
  727. }
  728. lmsi = append(lmsi, msi)
  729. }
  730. am[fname] = lmsi
  731. return nil
  732. }
  733. // AttribCheckMap checks and converts attribute to map[string]interface{}
  734. func AttribCheckMap(b *Builder, am map[string]interface{}, fname string) error {
  735. v := am[fname]
  736. if v == nil {
  737. return nil
  738. }
  739. msi, ok := v.(map[string]interface{})
  740. if !ok {
  741. return b.err(am, fname, "Not a map")
  742. }
  743. am[fname] = msi
  744. return nil
  745. }
  746. // AttribCheckIcons checks and converts attribute with a list of icon names or codepoints
  747. func AttribCheckIcons(b *Builder, am map[string]interface{}, fname string) error {
  748. v := am[fname]
  749. if v == nil {
  750. return nil
  751. }
  752. fs, ok := v.(string)
  753. if !ok {
  754. return b.err(am, fname, "Not a string")
  755. }
  756. text := ""
  757. parts := strings.Fields(fs)
  758. for i := 0; i < len(parts); i++ {
  759. // Try name first
  760. cp := icon.Codepoint(parts[i])
  761. if cp != "" {
  762. text += string(cp)
  763. continue
  764. }
  765. // Try to parse as hex value
  766. val, err := strconv.ParseUint(parts[i], 16, 32)
  767. if err != nil {
  768. return b.err(am, fname, fmt.Sprintf("Invalid icon codepoint value/name:%v", parts[i]))
  769. }
  770. text += string(val)
  771. }
  772. am[fname] = text
  773. return nil
  774. }
  775. // AttribCheckColor checks and converts attribute with color name or color component values
  776. func AttribCheckColor(b *Builder, am map[string]interface{}, fname string) error {
  777. // Checks if field is nil
  778. v := am[fname]
  779. if v == nil {
  780. return nil
  781. }
  782. // Converts to string
  783. fs, ok := v.(string)
  784. if !ok {
  785. return b.err(am, fname, "Not a string")
  786. }
  787. // Checks if string field is empty
  788. fs = strings.Trim(fs, " ")
  789. if fs == "" {
  790. return nil
  791. }
  792. // If string has 1 or 2 fields it must be a color name and optional alpha
  793. parts := strings.Fields(fs)
  794. if len(parts) == 1 || len(parts) == 2 {
  795. // First part must be a color name
  796. c, ok := math32.IsColorName(parts[0])
  797. if !ok {
  798. return b.err(am, fname, fmt.Sprintf("Invalid color name:%s", parts[0]))
  799. }
  800. c4 := math32.Color4{c.R, c.G, c.B, 1}
  801. if len(parts) == 2 {
  802. val, err := strconv.ParseFloat(parts[1], 32)
  803. if err != nil {
  804. return b.err(am, fname, fmt.Sprintf("Invalid float32 value:%s", parts[1]))
  805. }
  806. c4.A = float32(val)
  807. }
  808. am[fname] = &c4
  809. return nil
  810. }
  811. // Accept 3 or 4 floats values
  812. va, err := b.parseFloats(am, fname, 3, 4)
  813. if err != nil {
  814. return err
  815. }
  816. if len(va) == 3 {
  817. am[fname] = &math32.Color4{va[0], va[1], va[2], 1}
  818. return nil
  819. }
  820. am[fname] = &math32.Color4{va[0], va[1], va[2], va[3]}
  821. return nil
  822. }
  823. // AttribCheckBorderSizes checks and convert attribute with border sizes
  824. func AttribCheckBorderSizes(b *Builder, am map[string]interface{}, fname string) error {
  825. va, err := b.parseFloats(am, fname, 1, 4)
  826. if err != nil {
  827. return err
  828. }
  829. if va == nil {
  830. return nil
  831. }
  832. if len(va) == 1 {
  833. am[fname] = &BorderSizes{va[0], va[0], va[0], va[0]}
  834. return nil
  835. }
  836. am[fname] = &BorderSizes{va[0], va[1], va[2], va[3]}
  837. return nil
  838. }
  839. // AttribCheckPosition checks and convert attribute with x and y position
  840. func AttribCheckPosition(b *Builder, am map[string]interface{}, fname string) error {
  841. v := am[fname]
  842. if v == nil {
  843. return nil
  844. }
  845. af, err := b.parseFloats(am, fname, 2, 2)
  846. if err != nil {
  847. return err
  848. }
  849. am[fname] = af
  850. return nil
  851. }
  852. // AttribCheckStringLower checks and convert string attribute to lower case
  853. func AttribCheckStringLower(b *Builder, am map[string]interface{}, fname string) error {
  854. err := AttribCheckString(b, am, fname)
  855. if err != nil {
  856. return err
  857. }
  858. if v := am[fname]; v != nil {
  859. am[fname] = strings.ToLower(v.(string))
  860. }
  861. return nil
  862. }
  863. // AttribCheckFloat checks and convert attribute to float32
  864. func AttribCheckFloat(b *Builder, am map[string]interface{}, fname string) error {
  865. v := am[fname]
  866. if v == nil {
  867. return nil
  868. }
  869. switch n := v.(type) {
  870. case int:
  871. am[fname] = float32(n)
  872. return nil
  873. case float64:
  874. am[fname] = float32(n)
  875. return nil
  876. default:
  877. return b.err(am, fname, fmt.Sprintf("Not a number:%T", v))
  878. }
  879. return nil
  880. }
  881. // AttribCheckInt checks and convert attribute to int
  882. func AttribCheckInt(b *Builder, am map[string]interface{}, fname string) error {
  883. v := am[fname]
  884. if v == nil {
  885. return nil
  886. }
  887. vint, ok := v.(int)
  888. if !ok {
  889. return b.err(am, fname, "Not an integer")
  890. }
  891. am[fname] = vint
  892. return nil
  893. }
  894. // AttribCheckString checks and convert attribute to string
  895. func AttribCheckString(b *Builder, am map[string]interface{}, fname string) error {
  896. v := am[fname]
  897. if v == nil {
  898. return nil
  899. }
  900. s, ok := v.(string)
  901. if !ok {
  902. return b.err(am, fname, "Not a string")
  903. }
  904. am[fname] = s
  905. return nil
  906. }
  907. // AttribCheckBool checks and convert attribute to bool
  908. func AttribCheckBool(b *Builder, am map[string]interface{}, fname string) error {
  909. v := am[fname]
  910. if v == nil {
  911. return nil
  912. }
  913. bv, ok := v.(bool)
  914. if !ok {
  915. return b.err(am, fname, "Not a bool")
  916. }
  917. am[fname] = bv
  918. return nil
  919. }
  920. // parseFloats parses a string with a list of floats with the specified size
  921. // and returns a slice. The specified size is 0 any number of floats is allowed.
  922. // The individual values can be separated by spaces or commas
  923. func (b *Builder) parseFloats(am map[string]interface{}, fname string, min, max int) ([]float32, error) {
  924. // Checks if field is empty
  925. v := am[fname]
  926. if v == nil {
  927. return nil, nil
  928. }
  929. // If field has only one value, it is an int or a float64
  930. switch ft := v.(type) {
  931. case int:
  932. return []float32{float32(ft)}, nil
  933. case float64:
  934. return []float32{float32(ft)}, nil
  935. }
  936. // Converts to string
  937. fs, ok := v.(string)
  938. if !ok {
  939. return nil, b.err(am, fname, "Not a string")
  940. }
  941. // Checks if string field is empty
  942. fs = strings.Trim(fs, " ")
  943. if fs == "" {
  944. return nil, nil
  945. }
  946. // Separate individual fields
  947. var parts []string
  948. if strings.Index(fs, ",") < 0 {
  949. parts = strings.Fields(fs)
  950. } else {
  951. parts = strings.Split(fs, ",")
  952. }
  953. if len(parts) < min || len(parts) > max {
  954. return nil, b.err(am, fname, "Invalid number of float32 values")
  955. }
  956. // Parse each field value and appends to slice
  957. var values []float32
  958. for i := 0; i < len(parts); i++ {
  959. val, err := strconv.ParseFloat(strings.Trim(parts[i], " "), 32)
  960. if err != nil {
  961. return nil, b.err(am, fname, err.Error())
  962. }
  963. values = append(values, float32(val))
  964. }
  965. return values, nil
  966. }
  967. // err creates and returns an error for the current object, field name and with the specified message
  968. func (b *Builder) err(am map[string]interface{}, fname, msg string) error {
  969. return fmt.Errorf("Error in object:%s field:%s -> %s", am[AttribName], fname, msg)
  970. }
  971. // debugPrint prints the internal attribute map of the builder for debugging.
  972. // This map cannot be printed by fmt.Printf() because it has cycles.
  973. // A map contains a key: _parent, which pointer to is parent map, if any.
  974. func (b *Builder) debugPrint(v interface{}, level int) {
  975. switch vt := v.(type) {
  976. case map[string]interface{}:
  977. level += 3
  978. fmt.Printf("\n")
  979. for mk, mv := range vt {
  980. if mk == AttribParentInternal {
  981. continue
  982. }
  983. fmt.Printf("%s%s:", strings.Repeat(" ", level), mk)
  984. b.debugPrint(mv, level)
  985. }
  986. case []map[string]interface{}:
  987. for _, v := range vt {
  988. b.debugPrint(v, level)
  989. }
  990. default:
  991. fmt.Printf(" %v (%T)\n", vt, vt)
  992. }
  993. }