builder.go 30 KB

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