builder.go 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757
  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. "path/filepath"
  10. "sort"
  11. "strconv"
  12. "strings"
  13. "github.com/g3n/engine/gui/assets/icon"
  14. "github.com/g3n/engine/math32"
  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 map with gui object atttributes
  20. imgpath string // base path for image panels files
  21. builders map[string]BuilderFunc // map of builder functions by type
  22. attribs map[string]AttribCheckFunc // map of attribute name with check functions
  23. }
  24. type BuilderFunc func(*Builder, map[string]interface{}) (IPanel, error)
  25. //// descLayout contains all layout attributes
  26. //type descLayout struct {
  27. // Type string // Type of the layout: HBox, VBox, Grid, Dock, others...
  28. // Cols int // Number of columns for Grid layout
  29. // Spacing float32 // Spacing in pixels for HBox and VBox
  30. // AlignH string // HBox group alignment type
  31. // AlignV string // VBox group alignment type
  32. // MinHeight bool // HBox, VBox minimum height flag
  33. // MinWidth bool // HBox, VBox minimum width flag
  34. // ExpandH bool // Grid
  35. // ExpandV bool // Grid
  36. //}
  37. //
  38. //// descLayoutParam describes all layout parameters types
  39. //type descLayoutParams struct {
  40. // Expand *float32 // HBox, VBox expand factor
  41. // ColSpan int // Grid layout colspan
  42. // AlignH string // horizontal alignment
  43. // AlignV string // vertical alignment
  44. // Edge string // Dock layout edge: top,right,bottom,left,center
  45. //}
  46. //// descPanel describes all panel attributes
  47. //type descPanel struct {
  48. // Type string // Gui object type: Panel, Label, Edit, etc ...
  49. // Name string // Optional name for identification
  50. // Position string // Optional position as: x y | x,y
  51. // Width *float32 // Optional width (default = 0)
  52. // Height *float32 // Optional height (default = 0)
  53. // AspectWidth *float32 // Optional aspectwidth (default = nil)
  54. // AspectHeight *float32 // Optional aspectwidth (default = nil)
  55. // Margins string // Optional margins as 1 or 4 float values
  56. // Borders string // Optional borders as 1 or 4 float values
  57. // BorderColor string // Optional border color as name or 3 or 4 float values
  58. // Paddings string // Optional paddings as 1 or 4 float values
  59. // Color string // Optional color as 1 or 4 float values
  60. // Enabled *bool // All:
  61. // Visible *bool // All:
  62. // Renderable *bool // All:
  63. // Imagefile string // For Panel, Button
  64. // Layout *descLayout // Optional pointer to layout
  65. // LayoutParams *descLayoutParams // Optional layout parameters
  66. // Text string // Label, Button
  67. // Icons string // Label
  68. // BgColor string // Label
  69. // FontColor string // Label
  70. // FontSize *float32 // Label
  71. // FontDPI *float32 // Label
  72. // LineSpacing *float32 // Label
  73. // PlaceHolder string // Edit
  74. // MaxLength *uint // Edit
  75. // Icon string // Button
  76. // Group string // RadioButton
  77. // Checked bool // CheckBox, RadioButton
  78. // ImageLabel *descPanel // DropDown
  79. // Items []*descPanel // Menu, MenuBar
  80. // Shortcut string // Menu
  81. // Value *float32 // Slider
  82. // ScaleFactor *float32 // Slider
  83. // Title string // Window
  84. // Resizable string // Window resizable borders
  85. // P0 *descPanel // Splitter panel 0
  86. // P1 *descPanel // Splitter panel 1
  87. // Split *float32 // Splitter split value
  88. // parent *descPanel // used internally
  89. //}
  90. // Panel and layout types
  91. const (
  92. TypePanel = "panel"
  93. TypeImagePanel = "imagepanel"
  94. TypeLabel = "label"
  95. TypeImageLabel = "imagelabel"
  96. TypeButton = "button"
  97. TypeCheckBox = "checkbox"
  98. TypeRadioButton = "radiobutton"
  99. TypeEdit = "edit"
  100. TypeVList = "vlist"
  101. TypeHList = "hlist"
  102. TypeDropDown = "dropdown"
  103. TypeHSlider = "hslider"
  104. TypeVSlider = "vslider"
  105. TypeHSplitter = "hsplitter"
  106. TypeVSplitter = "vsplitter"
  107. TypeTree = "tree"
  108. TypeTreeNode = "node"
  109. TypeMenuBar = "menubar"
  110. TypeMenu = "menu"
  111. TypeWindow = "window"
  112. TypeHBoxLayout = "hbox"
  113. TypeVBoxLayout = "vbox"
  114. TypeGridLayout = "grid"
  115. TypeDockLayout = "dock"
  116. )
  117. // Common attribute names
  118. const (
  119. AttribAspectHeight = "aspectheight" // float32
  120. AttribAspectWidth = "aspectwidth" // float32
  121. AttribBgColor = "bgcolor" // Color4
  122. AttribBorders = "borders" // BorderSizes
  123. AttribBorderColor = "bordercolor" // Color4
  124. AttribColor = "color" // Color4
  125. AttribEnabled = "enabled" // bool
  126. AttribFontColor = "fontcolor" // Color4
  127. AttribFontDPI = "fontdpi" // float32
  128. AttribFontSize = "fontsize" // float32
  129. AttribHeight = "height" // float32
  130. AttribIcons = "icons" // string
  131. AttribImageFile = "imagefile" // string
  132. AttribItems = "items" // []map[string]interface{}
  133. AttribLineSpacing = "linespacing" // float32
  134. AttribMargins = "margins" // BorderSizes
  135. AttribName = "name" // string
  136. AttribPaddings = "paddings" // BorderSizes
  137. AttribPlaceHolder = "placeholder" // string
  138. AttribPosition = "position" // []float32
  139. AttribRender = "render" // bool
  140. AttribText = "text" // string
  141. AttribType = "type" // string
  142. AttribWidth = "width" // float32
  143. AttribVisible = "visible" // bool
  144. )
  145. const (
  146. aPOS = 1 << iota // attribute position
  147. aSIZE = 1 << iota // attribute size
  148. aNAME = 1 << iota // attribute name
  149. aMARGINS = 1 << iota // attribute margins widths
  150. aBORDERS = 1 << iota // attribute borders widths
  151. aBORDERCOLOR = 1 << iota // attribute border color
  152. aPADDINGS = 1 << iota // attribute paddings widths
  153. aCOLOR = 1 << iota // attribute panel bgcolor
  154. aENABLED = 1 << iota // attribute enabled for events
  155. aRENDER = 1 << iota // attribute renderable
  156. aVISIBLE = 1 << iota // attribute visible
  157. asPANEL = 0xFF // attribute set for panels
  158. asWIDGET = aPOS | aNAME | aENABLED | aVISIBLE // attribute set for widgets
  159. asBUTTON = aPOS | aSIZE | aNAME | aENABLED | aVISIBLE // attribute set for buttons
  160. )
  161. // maps align name with align parameter
  162. var mapAlignName = map[string]Align{
  163. "none": AlignNone,
  164. "left": AlignLeft,
  165. "right": AlignRight,
  166. "width": AlignWidth,
  167. "top": AlignTop,
  168. "bottom": AlignBottom,
  169. "height": AlignHeight,
  170. "center": AlignCenter,
  171. }
  172. // maps edge name (dock layout) with edge parameter
  173. var mapEdgeName = map[string]int{
  174. "top": DockTop,
  175. "right": DockRight,
  176. "bottom": DockBottom,
  177. "left": DockLeft,
  178. "center": DockCenter,
  179. }
  180. // maps resize border name (window) with parameter value
  181. var mapResizable = map[string]Resizable{
  182. "top": ResizeTop,
  183. "right": ResizeRight,
  184. "bottom": ResizeBottom,
  185. "left": ResizeLeft,
  186. "all": ResizeAll,
  187. }
  188. type AttribCheckFunc func(b *Builder, am map[string]interface{}, fname string) error
  189. // NewBuilder creates and returns a pointer to a new gui Builder object
  190. func NewBuilder() *Builder {
  191. b := new(Builder)
  192. // Sets map of object type to builder function
  193. b.builders = map[string]BuilderFunc{
  194. TypePanel: buildPanel,
  195. TypeImagePanel: buildImagePanel,
  196. TypeLabel: buildLabel,
  197. TypeImageLabel: buildImageLabel,
  198. TypeButton: buildButton,
  199. TypeEdit: buildEdit,
  200. }
  201. // Sets map of attribute name to check function
  202. b.attribs = map[string]AttribCheckFunc{
  203. AttribAspectWidth: AttribCheckFloat,
  204. AttribAspectHeight: AttribCheckFloat,
  205. AttribHeight: AttribCheckFloat,
  206. AttribMargins: AttribCheckBorderSizes,
  207. AttribBgColor: AttribCheckColor,
  208. AttribBorders: AttribCheckBorderSizes,
  209. AttribBorderColor: AttribCheckColor,
  210. AttribColor: AttribCheckColor,
  211. AttribEnabled: AttribCheckBool,
  212. AttribFontColor: AttribCheckColor,
  213. AttribFontDPI: AttribCheckFloat,
  214. AttribFontSize: AttribCheckFloat,
  215. AttribIcons: AttribCheckIcons,
  216. AttribImageFile: AttribCheckString,
  217. AttribItems: AttribCheckListMap,
  218. AttribLineSpacing: AttribCheckFloat,
  219. AttribName: AttribCheckString,
  220. AttribPaddings: AttribCheckBorderSizes,
  221. AttribPlaceHolder: AttribCheckString,
  222. AttribPosition: AttribCheckPosition,
  223. AttribRender: AttribCheckBool,
  224. AttribText: AttribCheckString,
  225. AttribType: AttribCheckString,
  226. AttribVisible: AttribCheckBool,
  227. AttribWidth: AttribCheckFloat,
  228. }
  229. return b
  230. }
  231. // ParseString parses a string with gui objects descriptions in YAML format
  232. // It there was a previously parsed description, it is cleared.
  233. func (b *Builder) ParseString(desc string) error {
  234. // Parses descriptor string in YAML format saving result in
  235. // a map of interface{} to interface{} as YAML allows numeric keys.
  236. var mii map[interface{}]interface{}
  237. err := yaml.Unmarshal([]byte(desc), &mii)
  238. if err != nil {
  239. return err
  240. }
  241. // Internal function which converts map[interface{}]interface{} to
  242. // map[string]interface{} recursively and lower case of all map keys.
  243. // It also sets a field named "_parent", which pointer to the parent map
  244. // This field causes a circular reference in the result map which prevents
  245. // the use of Go's Printf to print the result map.
  246. var visitor func(v, par interface{}) (interface{}, error)
  247. visitor = func(v, par interface{}) (interface{}, error) {
  248. switch vt := v.(type) {
  249. case []interface{}:
  250. ls := []interface{}{}
  251. for _, item := range vt {
  252. ci, err := visitor(item, par)
  253. if err != nil {
  254. return nil, err
  255. }
  256. ls = append(ls, ci)
  257. }
  258. return ls, nil
  259. case map[interface{}]interface{}:
  260. ms := make(map[string]interface{})
  261. for k, v := range vt {
  262. // Checks key
  263. ks, ok := k.(string)
  264. if !ok {
  265. return nil, fmt.Errorf("Keys must be strings")
  266. }
  267. ks = strings.ToLower(ks)
  268. // Checks value
  269. vi, err := visitor(v, ms)
  270. if err != nil {
  271. return nil, err
  272. }
  273. ms[ks] = vi
  274. // If not parent panel, Checks attribute
  275. if par != nil {
  276. // Get attribute check function
  277. acf, ok := b.attribs[ks]
  278. if !ok {
  279. return nil, fmt.Errorf("Invalid attribute:%s", ks)
  280. }
  281. // Checks attribute
  282. err = acf(b, ms, ks)
  283. if err != nil {
  284. return nil, err
  285. }
  286. }
  287. }
  288. if par != nil {
  289. ms["_parent"] = par
  290. }
  291. return ms, nil
  292. default:
  293. return v, nil
  294. }
  295. return nil, nil
  296. }
  297. // Get map[string]interface{} with lower case keys from parsed descritor
  298. res, err := visitor(mii, nil)
  299. if err != nil {
  300. return err
  301. }
  302. msi, ok := res.(map[string]interface{})
  303. if !ok {
  304. return fmt.Errorf("Parsed result is not a map")
  305. }
  306. b.am = msi
  307. b.debugPrint(b.am, 1)
  308. return nil
  309. }
  310. // ParseFile parses a file with gui objects descriptions in YAML format
  311. // It there was a previously parsed description, it is cleared.
  312. func (b *Builder) ParseFile(filepath string) error {
  313. // Reads all file data
  314. f, err := os.Open(filepath)
  315. if err != nil {
  316. return err
  317. }
  318. data, err := ioutil.ReadAll(f)
  319. if err != nil {
  320. return err
  321. }
  322. err = f.Close()
  323. if err != nil {
  324. return err
  325. }
  326. // Parses file data
  327. return b.ParseString(string(data))
  328. }
  329. // Names returns a sorted list of names of top level previously parsed objects.
  330. // Only objects with defined types are returned.
  331. // If there is only a single object with no name, its name is returned
  332. // as an empty string
  333. func (b *Builder) Names() []string {
  334. var objs []string
  335. if b.am[AttribType] != nil {
  336. objs = append(objs, "")
  337. return objs
  338. }
  339. for name, _ := range b.am {
  340. objs = append(objs, name)
  341. }
  342. sort.Strings(objs)
  343. return objs
  344. }
  345. // Build builds a gui object and all its children recursively.
  346. // The specified name should be a top level name from a
  347. // from a previously parsed description
  348. // If the descriptions contains a single object with no name,
  349. // It should be specified the empty string to build this object.
  350. func (b *Builder) Build(name string) (IPanel, error) {
  351. // Only one object
  352. if name == "" {
  353. if b.am[AttribName] != nil {
  354. }
  355. return b.build(b.am, nil)
  356. }
  357. // Map of gui objects
  358. am, ok := b.am[name]
  359. if !ok {
  360. return nil, fmt.Errorf("Object name:%s not found", name)
  361. }
  362. return b.build(am.(map[string]interface{}), nil)
  363. }
  364. // Sets the path for image panels relative image files
  365. func (b *Builder) SetImagepath(path string) {
  366. b.imgpath = path
  367. }
  368. func (b *Builder) AddBuilder(typename string, bf BuilderFunc) {
  369. b.builders[typename] = bf
  370. }
  371. // build builds the gui object from the specified description.
  372. // All its children are also built recursively
  373. // Returns the built object or an error
  374. func (b *Builder) build(pd map[string]interface{}, iparent IPanel) (IPanel, error) {
  375. // Get panel type name
  376. if pd["type"] == nil {
  377. return nil, fmt.Errorf("Type not specified")
  378. }
  379. typename, ok := pd["type"].(string)
  380. if !ok {
  381. return nil, fmt.Errorf("Type must be a string")
  382. }
  383. typename = strings.ToLower(typename)
  384. // Get builder function for this type name
  385. builder := b.builders[typename]
  386. if builder == nil {
  387. return nil, fmt.Errorf("Invalid type:%v", pd["type"])
  388. }
  389. // Builds panel
  390. pan, err := builder(b, pd)
  391. if err != nil {
  392. return nil, err
  393. }
  394. // Adds built panel to parent
  395. if iparent != nil {
  396. iparent.GetPanel().Add(pan)
  397. }
  398. return pan, nil
  399. }
  400. // buildPanel builds an object of type Panel
  401. func buildPanel(b *Builder, am map[string]interface{}) (IPanel, error) {
  402. pan := NewPanel(0, 0)
  403. err := b.setAttribs(am, pan, asPANEL)
  404. if err != nil {
  405. return nil, err
  406. }
  407. // Builds children recursively
  408. if am[AttribItems] != nil {
  409. items := am[AttribItems].([]map[string]interface{})
  410. for i := 0; i < len(items); i++ {
  411. item := items[i]
  412. child, err := b.build(item, pan)
  413. if err != nil {
  414. return nil, err
  415. }
  416. pan.Add(child)
  417. }
  418. }
  419. return pan, nil
  420. }
  421. // buildImagePanel builds a gui object of type ImagePanel
  422. func buildImagePanel(b *Builder, am map[string]interface{}) (IPanel, error) {
  423. // Checks imagefile attribute
  424. if am[AttribImageFile] == nil {
  425. return nil, b.err(am, AttribImageFile, "Must be supplied")
  426. }
  427. // If path is not absolute join with user supplied image base path
  428. imagefile := am[AttribImageFile].(string)
  429. if !filepath.IsAbs(imagefile) {
  430. imagefile = filepath.Join(b.imgpath, imagefile)
  431. }
  432. // Builds panel and set common attributes
  433. panel, err := NewImage(imagefile)
  434. if err != nil {
  435. return nil, err
  436. }
  437. err = b.setAttribs(am, panel, asPANEL)
  438. if err != nil {
  439. return nil, err
  440. }
  441. // Sets optional AspectWidth attribute
  442. if aw := am[AttribAspectWidth]; aw != nil {
  443. panel.SetContentAspectWidth(aw.(float32))
  444. }
  445. // Sets optional AspectHeight attribute
  446. if ah := am[AttribAspectHeight]; ah != nil {
  447. panel.SetContentAspectHeight(ah.(float32))
  448. }
  449. // Builds children recursively
  450. if am[AttribItems] != nil {
  451. items := am[AttribItems].([]map[string]interface{})
  452. for i := 0; i < len(items); i++ {
  453. item := items[i]
  454. child, err := b.build(item, panel)
  455. if err != nil {
  456. return nil, err
  457. }
  458. panel.Add(child)
  459. }
  460. }
  461. return panel, nil
  462. }
  463. // buildLabel builds a gui object of type Label
  464. func buildLabel(b *Builder, am map[string]interface{}) (IPanel, error) {
  465. var label *Label
  466. if am[AttribIcons] != nil {
  467. label = NewLabel(am[AttribIcons].(string), true)
  468. } else if am[AttribText] != nil {
  469. label = NewLabel(am[AttribText].(string))
  470. } else {
  471. label = NewLabel("")
  472. }
  473. // Sets common attributes
  474. err := b.setAttribs(am, label, asPANEL)
  475. if err != nil {
  476. return nil, err
  477. }
  478. // Set optional background color
  479. if bgc := am[AttribBgColor]; bgc != nil {
  480. label.SetBgColor4(bgc.(*math32.Color4))
  481. }
  482. // Set optional font color
  483. if fc := am[AttribFontColor]; fc != nil {
  484. label.SetColor4(fc.(*math32.Color4))
  485. }
  486. // Sets optional font size
  487. if fs := am[AttribFontSize]; fs != nil {
  488. label.SetFontSize(float64(fs.(float32)))
  489. }
  490. // Sets optional font dpi
  491. if fdpi := am[AttribFontDPI]; fdpi != nil {
  492. label.SetFontDPI(float64(fdpi.(float32)))
  493. }
  494. // Sets optional line spacing
  495. if ls := am[AttribLineSpacing]; ls != nil {
  496. label.SetLineSpacing(float64(ls.(float32)))
  497. }
  498. return label, nil
  499. }
  500. // buildImageLabel builds a gui object of type: ImageLabel
  501. func buildImageLabel(b *Builder, am map[string]interface{}) (IPanel, error) {
  502. // Builds image label and set common attributes
  503. var text string
  504. if am[AttribText] != nil {
  505. text = am[AttribText].(string)
  506. }
  507. imglabel := NewImageLabel(text)
  508. err := b.setAttribs(am, imglabel, asPANEL)
  509. if err != nil {
  510. return nil, err
  511. }
  512. // Sets optional icon(s)
  513. if icons := am[AttribIcons]; icons != nil {
  514. imglabel.SetIcon(icons.(string))
  515. }
  516. // Sets optional image from file
  517. // If path is not absolute join with user supplied image base path
  518. if imgf := am[AttribImageFile]; imgf != nil {
  519. path := imgf.(string)
  520. if !filepath.IsAbs(path) {
  521. path = filepath.Join(b.imgpath, path)
  522. }
  523. err := imglabel.SetImageFromFile(path)
  524. if err != nil {
  525. return nil, err
  526. }
  527. }
  528. return imglabel, nil
  529. }
  530. // buildButton builds a gui object of type: Button
  531. func buildButton(b *Builder, am map[string]interface{}) (IPanel, error) {
  532. // Builds button and set commont attributes
  533. var text string
  534. if am[AttribText] != nil {
  535. text = am[AttribText].(string)
  536. }
  537. button := NewButton(text)
  538. err := b.setAttribs(am, button, asBUTTON)
  539. if err != nil {
  540. return nil, err
  541. }
  542. // Sets optional icon
  543. if icons := am[AttribIcons]; icons != nil {
  544. button.SetIcon(icons.(string))
  545. }
  546. // Sets optional image from file
  547. // If path is not absolute join with user supplied image base path
  548. if imgf := am[AttribImageFile]; imgf != nil {
  549. path := imgf.(string)
  550. if !filepath.IsAbs(path) {
  551. path = filepath.Join(b.imgpath, path)
  552. }
  553. err := button.SetImage(path)
  554. if err != nil {
  555. return nil, err
  556. }
  557. }
  558. return button, nil
  559. }
  560. // buildEdit builds a gui object of type: "Edit"
  561. func buildEdit(b *Builder, am map[string]interface{}) (IPanel, error) {
  562. // Builds button and set attributes
  563. var width float32
  564. var placeholder string
  565. if aw := am[AttribWidth]; aw != nil {
  566. width = aw.(float32)
  567. }
  568. if ph := am[AttribPlaceHolder]; ph != nil {
  569. placeholder = ph.(string)
  570. }
  571. edit := NewEdit(int(width), placeholder)
  572. err := b.setAttribs(am, edit, asWIDGET)
  573. if err != nil {
  574. return nil, err
  575. }
  576. return edit, nil
  577. }
  578. func AttribCheckListMap(b *Builder, am map[string]interface{}, fname string) error {
  579. v := am[fname]
  580. if v == nil {
  581. return nil
  582. }
  583. li, ok := v.([]interface{})
  584. if !ok {
  585. return b.err(am, fname, "Not a list")
  586. }
  587. lmsi := make([]map[string]interface{}, 0)
  588. for i := 0; i < len(li); i++ {
  589. item := li[i]
  590. msi, ok := item.(map[string]interface{})
  591. if !ok {
  592. return b.err(am, fname, "Item is not a map")
  593. }
  594. lmsi = append(lmsi, msi)
  595. }
  596. am[fname] = lmsi
  597. return nil
  598. }
  599. func AttribCheckIcons(b *Builder, am map[string]interface{}, fname string) error {
  600. v := am[fname]
  601. if v == nil {
  602. return nil
  603. }
  604. fs, ok := v.(string)
  605. if !ok {
  606. return b.err(am, fname, "Not a string")
  607. }
  608. text := ""
  609. parts := strings.Fields(fs)
  610. for i := 0; i < len(parts); i++ {
  611. // Try name first
  612. cp := icon.Codepoint(parts[i])
  613. if cp != "" {
  614. text += string(cp)
  615. continue
  616. }
  617. // Try to parse as hex value
  618. val, err := strconv.ParseUint(parts[i], 16, 32)
  619. if err != nil {
  620. return b.err(am, fname, fmt.Sprintf("Invalid icon codepoint value/name:%v", parts[i]))
  621. }
  622. text += string(val)
  623. }
  624. am[fname] = text
  625. return nil
  626. }
  627. func AttribCheckColor(b *Builder, am map[string]interface{}, fname string) error {
  628. // Checks if field is nil
  629. v := am[fname]
  630. if v == nil {
  631. return nil
  632. }
  633. // Converts to string
  634. fs, ok := v.(string)
  635. if !ok {
  636. return b.err(am, fname, "Not a string")
  637. }
  638. // Checks if string field is empty
  639. fs = strings.Trim(fs, " ")
  640. if fs == "" {
  641. return nil
  642. }
  643. // If string has 1 or 2 fields it must be a color name and optional alpha
  644. parts := strings.Fields(fs)
  645. if len(parts) == 1 || len(parts) == 2 {
  646. // First part must be a color name
  647. c, ok := math32.IsColorName(parts[0])
  648. if !ok {
  649. return b.err(am, fname, fmt.Sprintf("Invalid color name:%s", parts[0]))
  650. }
  651. c4 := math32.Color4{c.R, c.G, c.B, 1}
  652. if len(parts) == 2 {
  653. val, err := strconv.ParseFloat(parts[1], 32)
  654. if err != nil {
  655. return b.err(am, fname, fmt.Sprintf("Invalid float32 value:%s", parts[1]))
  656. }
  657. c4.A = float32(val)
  658. }
  659. am[fname] = &c4
  660. return nil
  661. }
  662. // Accept 3 or 4 floats values
  663. va, err := b.parseFloats(am, fname, 3, 4)
  664. if err != nil {
  665. return err
  666. }
  667. if len(va) == 3 {
  668. am[fname] = &math32.Color4{va[0], va[1], va[2], 1}
  669. return nil
  670. }
  671. am[fname] = &math32.Color4{va[0], va[1], va[2], va[3]}
  672. return nil
  673. }
  674. func AttribCheckBorderSizes(b *Builder, am map[string]interface{}, fname string) error {
  675. va, err := b.parseFloats(am, fname, 1, 4)
  676. if err != nil {
  677. return err
  678. }
  679. if va == nil {
  680. return nil
  681. }
  682. if len(va) == 1 {
  683. am[fname] = &BorderSizes{va[0], va[0], va[0], va[0]}
  684. return nil
  685. }
  686. am[fname] = &BorderSizes{va[0], va[1], va[2], va[3]}
  687. return nil
  688. }
  689. func AttribCheckPosition(b *Builder, am map[string]interface{}, fname string) error {
  690. v := am[fname]
  691. if v == nil {
  692. return nil
  693. }
  694. af, err := b.parseFloats(am, fname, 2, 2)
  695. if err != nil {
  696. return err
  697. }
  698. am[fname] = af
  699. return nil
  700. }
  701. func AttribCheckFloat(b *Builder, am map[string]interface{}, fname string) error {
  702. v := am[fname]
  703. if v == nil {
  704. return nil
  705. }
  706. switch n := v.(type) {
  707. case int:
  708. am[fname] = float32(n)
  709. return nil
  710. case float64:
  711. am[fname] = float32(n)
  712. return nil
  713. default:
  714. return b.err(am, fname, fmt.Sprintf("Not a number:%T", v))
  715. }
  716. return nil
  717. }
  718. func AttribCheckString(b *Builder, am map[string]interface{}, fname string) error {
  719. v := am[fname]
  720. if v == nil {
  721. return nil
  722. }
  723. s, ok := v.(string)
  724. if !ok {
  725. return b.err(am, fname, "Not a string")
  726. }
  727. am[fname] = s
  728. return nil
  729. }
  730. func AttribCheckBool(b *Builder, am map[string]interface{}, fname string) error {
  731. v := am[fname]
  732. if v == nil {
  733. return nil
  734. }
  735. bv, ok := v.(bool)
  736. if !ok {
  737. return b.err(am, fname, "Not a bool")
  738. }
  739. am[fname] = bv
  740. return nil
  741. }
  742. /***
  743. // buildImageLabel builds a gui object of type: ImageLabel
  744. func (b *Builder) buildImageLabel(pd *descPanel) (IPanel, error) {
  745. // Builds image label and set common attributes
  746. imglabel := NewImageLabel(pd.Text)
  747. err := b.setAttribs(pd, imglabel, asPANEL)
  748. if err != nil {
  749. return nil, err
  750. }
  751. // Sets optional icon(s)
  752. icons, err := b.parseIconNames("icons", pd.Icons)
  753. if err != nil {
  754. return nil, err
  755. }
  756. if icons != "" {
  757. imglabel.SetIcon(icons)
  758. }
  759. // Sets optional image from file
  760. // If path is not absolute join with user supplied image base path
  761. if pd.Imagefile != "" {
  762. path := pd.Imagefile
  763. if !filepath.IsAbs(path) {
  764. path = filepath.Join(b.imgpath, path)
  765. }
  766. err := imglabel.SetImageFromFile(path)
  767. if err != nil {
  768. return nil, err
  769. }
  770. }
  771. return imglabel, nil
  772. }
  773. // buildButton builds a gui object of type: Button
  774. func (b *Builder) buildButton(pd *descPanel) (IPanel, error) {
  775. // Builds button and set commont attributes
  776. button := NewButton(pd.Text)
  777. err := b.setAttribs(pd, button, asBUTTON)
  778. if err != nil {
  779. return nil, err
  780. }
  781. // Sets optional icon
  782. if pd.Icon != "" {
  783. cp, err := b.parseIconName("icon", pd.Icon)
  784. if err != nil {
  785. return nil, err
  786. }
  787. button.SetIcon(cp)
  788. }
  789. // Sets optional image from file
  790. // If path is not absolute join with user supplied image base path
  791. if pd.Imagefile != "" {
  792. path := pd.Imagefile
  793. if !filepath.IsAbs(path) {
  794. path = filepath.Join(b.imgpath, path)
  795. }
  796. err := button.SetImage(path)
  797. if err != nil {
  798. return nil, err
  799. }
  800. }
  801. return button, nil
  802. }
  803. // buildCheckBox builds a gui object of type: CheckBox
  804. func (b *Builder) buildCheckBox(pd *descPanel) (IPanel, error) {
  805. // Builds check box and set commont attributes
  806. cb := NewCheckBox(pd.Text)
  807. err := b.setAttribs(pd, cb, asWIDGET)
  808. if err != nil {
  809. return nil, err
  810. }
  811. cb.SetValue(pd.Checked)
  812. return cb, nil
  813. }
  814. // buildRadioButton builds a gui object of type: RadioButton
  815. func (b *Builder) buildRadioButton(pd *descPanel) (IPanel, error) {
  816. // Builds check box and set commont attributes
  817. rb := NewRadioButton(pd.Text)
  818. err := b.setAttribs(pd, rb, asWIDGET)
  819. if err != nil {
  820. return nil, err
  821. }
  822. // Sets optional radio button group
  823. if pd.Group != "" {
  824. rb.SetGroup(pd.Group)
  825. }
  826. rb.SetValue(pd.Checked)
  827. return rb, nil
  828. }
  829. // buildEdit builds a gui object of type: "Edit"
  830. func (b *Builder) buildEdit(dp *descPanel) (IPanel, error) {
  831. // Builds button and set attributes
  832. width, _ := b.size(dp)
  833. edit := NewEdit(int(width), dp.PlaceHolder)
  834. err := b.setAttribs(dp, edit, asWIDGET)
  835. if err != nil {
  836. return nil, err
  837. }
  838. edit.SetText(dp.Text)
  839. return edit, nil
  840. }
  841. // buildVList builds a gui object of type: VList
  842. func (b *Builder) buildVList(dp *descPanel) (IPanel, error) {
  843. // Builds list and set commont attributes
  844. width, height := b.size(dp)
  845. list := NewVList(width, height)
  846. err := b.setAttribs(dp, list, asWIDGET)
  847. if err != nil {
  848. return nil, err
  849. }
  850. // Builds list children
  851. for i := 0; i < len(dp.Items); i++ {
  852. item := dp.Items[i]
  853. b.objpath.push(item.Name)
  854. child, err := b.build(item, list)
  855. b.objpath.pop()
  856. if err != nil {
  857. return nil, err
  858. }
  859. list.Add(child)
  860. }
  861. return list, nil
  862. }
  863. // buildHList builds a gui object of type: VList
  864. func (b *Builder) buildHList(dp *descPanel) (IPanel, error) {
  865. // Builds list and set commont attributes
  866. width, height := b.size(dp)
  867. list := NewHList(width, height)
  868. err := b.setAttribs(dp, list, asWIDGET)
  869. if err != nil {
  870. return nil, err
  871. }
  872. // Builds list children
  873. for i := 0; i < len(dp.Items); i++ {
  874. item := dp.Items[i]
  875. b.objpath.push(item.Name)
  876. child, err := b.build(item, list)
  877. b.objpath.pop()
  878. if err != nil {
  879. return nil, err
  880. }
  881. list.Add(child)
  882. }
  883. return list, nil
  884. }
  885. // buildDropDown builds a gui object of type: DropDown
  886. func (b *Builder) buildDropDown(pd *descPanel) (IPanel, error) {
  887. // If image label attribute defined use it, otherwise
  888. // uses default value.
  889. var imglabel *ImageLabel
  890. if pd.ImageLabel != nil {
  891. pd.ImageLabel.Type = descTypeImageLabel
  892. ipan, err := b.build(pd.ImageLabel, nil)
  893. if err != nil {
  894. return nil, err
  895. }
  896. imglabel = ipan.(*ImageLabel)
  897. } else {
  898. imglabel = NewImageLabel("")
  899. }
  900. // Builds drop down and set common attributes
  901. width, _ := b.size(pd)
  902. dd := NewDropDown(width, imglabel)
  903. err := b.setAttribs(pd, dd, asWIDGET)
  904. if err != nil {
  905. return nil, err
  906. }
  907. // Builds drop down children
  908. for i := 0; i < len(pd.Items); i++ {
  909. item := pd.Items[i]
  910. item.Type = descTypeImageLabel
  911. b.objpath.push(item.Name)
  912. child, err := b.build(item, dd)
  913. b.objpath.pop()
  914. if err != nil {
  915. return nil, err
  916. }
  917. dd.Add(child.(*ImageLabel))
  918. }
  919. return dd, nil
  920. }
  921. // buildSlider builds a gui object of type: HSlider or VSlider
  922. func (b *Builder) buildSlider(pd *descPanel, horiz bool) (IPanel, error) {
  923. // Builds slider and sets its position
  924. width, height := b.size(pd)
  925. var slider *Slider
  926. if horiz {
  927. slider = NewHSlider(width, height)
  928. } else {
  929. slider = NewVSlider(width, height)
  930. }
  931. err := b.setAttribs(pd, slider, asWIDGET)
  932. if err != nil {
  933. return nil, err
  934. }
  935. // Sets optional text
  936. if pd.Text != "" {
  937. slider.SetText(pd.Text)
  938. }
  939. // Sets optional scale factor
  940. if pd.ScaleFactor != nil {
  941. slider.SetScaleFactor(*pd.ScaleFactor)
  942. }
  943. // Sets optional value
  944. if pd.Value != nil {
  945. slider.SetValue(*pd.Value)
  946. }
  947. return slider, nil
  948. }
  949. // buildSplitter builds a gui object of type: HSplitterr or VSplitter
  950. func (b *Builder) buildSplitter(pd *descPanel, horiz bool) (IPanel, error) {
  951. // Builds splitter and sets its common attributes
  952. width, height := b.size(pd)
  953. var splitter *Splitter
  954. if horiz {
  955. splitter = NewHSplitter(width, height)
  956. } else {
  957. splitter = NewVSplitter(width, height)
  958. }
  959. err := b.setAttribs(pd, splitter, asWIDGET)
  960. if err != nil {
  961. return nil, err
  962. }
  963. // Optional split value
  964. if pd.Split != nil {
  965. splitter.SetSplit(*pd.Split)
  966. }
  967. // Splitter panel 0 attributes and items
  968. if pd.P0 != nil {
  969. err := b.setAttribs(pd.P0, &splitter.P0, asPANEL)
  970. if err != nil {
  971. return nil, err
  972. }
  973. err = b.addPanelItems(pd.P0, &splitter.P0)
  974. if err != nil {
  975. return nil, err
  976. }
  977. }
  978. // Splitter panel 1 attributes and items
  979. if pd.P1 != nil {
  980. err := b.setAttribs(pd.P1, &splitter.P1, asPANEL)
  981. if err != nil {
  982. return nil, err
  983. }
  984. err = b.addPanelItems(pd.P1, &splitter.P1)
  985. if err != nil {
  986. return nil, err
  987. }
  988. }
  989. return splitter, nil
  990. }
  991. // buildTree builds a gui object of type: Tree
  992. func (b *Builder) buildTree(dp *descPanel) (IPanel, error) {
  993. // Builds tree and sets its common attributes
  994. width, height := b.size(dp)
  995. tree := NewTree(width, height)
  996. err := b.setAttribs(dp, tree, asWIDGET)
  997. if err != nil {
  998. return nil, err
  999. }
  1000. // Internal function to build tree nodes recursively
  1001. var buildItems func(dp *descPanel, pnode *TreeNode) error
  1002. buildItems = func(dp *descPanel, pnode *TreeNode) error {
  1003. for i := 0; i < len(dp.Items); i++ {
  1004. item := dp.Items[i]
  1005. // Item is a tree node
  1006. if item.Type == "" || item.Type == descTypeTreeNode {
  1007. var node *TreeNode
  1008. if pnode == nil {
  1009. node = tree.AddNode(item.Text)
  1010. } else {
  1011. node = pnode.AddNode(item.Text)
  1012. }
  1013. err := buildItems(item, node)
  1014. if err != nil {
  1015. return err
  1016. }
  1017. continue
  1018. }
  1019. // Other controls
  1020. ipan, err := b.build(item, nil)
  1021. if err != nil {
  1022. return err
  1023. }
  1024. if pnode == nil {
  1025. tree.Add(ipan)
  1026. } else {
  1027. pnode.Add(ipan)
  1028. }
  1029. }
  1030. return nil
  1031. }
  1032. // Build nodes
  1033. err = buildItems(dp, nil)
  1034. if err != nil {
  1035. return nil, err
  1036. }
  1037. return tree, nil
  1038. }
  1039. // buildMenu builds a gui object of type: Menu or MenuBar from the
  1040. // specified panel descriptor.
  1041. func (b *Builder) buildMenu(pd *descPanel, child, bar bool) (IPanel, error) {
  1042. // Builds menu bar or menu
  1043. var menu *Menu
  1044. if bar {
  1045. menu = NewMenuBar()
  1046. } else {
  1047. menu = NewMenu()
  1048. }
  1049. // Only sets attribs for top level menus
  1050. if !child {
  1051. err := b.setAttribs(pd, menu, asWIDGET)
  1052. if err != nil {
  1053. return nil, err
  1054. }
  1055. }
  1056. // Builds and adds menu items
  1057. for i := 0; i < len(pd.Items); i++ {
  1058. item := pd.Items[i]
  1059. // Item is another menu
  1060. if item.Type == descTypeMenu {
  1061. subm, err := b.buildMenu(item, true, false)
  1062. if err != nil {
  1063. return nil, err
  1064. }
  1065. menu.AddMenu(item.Text, subm.(*Menu))
  1066. continue
  1067. }
  1068. // Item is a separator
  1069. if item.Type == "Separator" {
  1070. menu.AddSeparator()
  1071. continue
  1072. }
  1073. // Item must be a menu option
  1074. mi := menu.AddOption(item.Text)
  1075. // Set item optional icon(s)
  1076. icons, err := b.parseIconNames("icon", item.Icon)
  1077. if err != nil {
  1078. return nil, err
  1079. }
  1080. if icons != "" {
  1081. mi.SetIcon(string(icons))
  1082. }
  1083. // Sets optional menu item shortcut
  1084. err = b.setMenuShortcut(mi, "shortcut", item.Shortcut)
  1085. if err != nil {
  1086. return nil, err
  1087. }
  1088. }
  1089. return menu, nil
  1090. }
  1091. // buildWindow builds a gui object of type: Window from the
  1092. // specified panel descriptor.
  1093. func (b *Builder) buildWindow(dp *descPanel) (IPanel, error) {
  1094. // Builds window and sets its common attributes
  1095. width, height := b.size(dp)
  1096. win := NewWindow(width, height)
  1097. err := b.setAttribs(dp, win, asWIDGET)
  1098. if err != nil {
  1099. return nil, err
  1100. }
  1101. // Title attribute
  1102. win.SetTitle(dp.Title)
  1103. // Parse resizable borders
  1104. if dp.Resizable != "" {
  1105. parts := strings.Fields(dp.Resizable)
  1106. var res Resizable
  1107. for _, name := range parts {
  1108. v, ok := mapResizable[name]
  1109. if !ok {
  1110. return nil, b.err("resizable", "Invalid resizable name:"+name)
  1111. }
  1112. res |= v
  1113. }
  1114. win.SetResizable(res)
  1115. }
  1116. // Builds window client panel children recursively
  1117. for i := 0; i < len(dp.Items); i++ {
  1118. item := dp.Items[i]
  1119. b.objpath.push(item.Name)
  1120. child, err := b.build(item, win)
  1121. b.objpath.pop()
  1122. if err != nil {
  1123. return nil, err
  1124. }
  1125. win.Add(child)
  1126. }
  1127. return win, nil
  1128. }
  1129. // addPanelItems adds the items in the panel descriptor to the specified panel
  1130. func (b *Builder) addPanelItems(dp *descPanel, ipan IPanel) error {
  1131. pan := ipan.GetPanel()
  1132. for i := 0; i < len(dp.Items); i++ {
  1133. item := dp.Items[i]
  1134. b.objpath.push(item.Name)
  1135. child, err := b.build(item, pan)
  1136. b.objpath.pop()
  1137. if err != nil {
  1138. return err
  1139. }
  1140. pan.Add(child)
  1141. }
  1142. return nil
  1143. }
  1144. // setAttribs sets common attributes from the description to the specified panel
  1145. // The attributes which are set can be specified by the specified bitmask.
  1146. func (b *Builder) setAttribs(pd *descPanel, ipan IPanel, attr uint) error {
  1147. panel := ipan.GetPanel()
  1148. // Set optional position
  1149. if attr&aPOS != 0 && pd.Position != "" {
  1150. va, err := b.parseFloats("position", pd.Position, 2, 2)
  1151. if va == nil || err != nil {
  1152. return err
  1153. }
  1154. panel.SetPosition(va[0], va[1])
  1155. }
  1156. // Set optional size
  1157. if attr&aSIZE != 0 {
  1158. if pd.Width != nil {
  1159. panel.SetWidth(*pd.Width)
  1160. }
  1161. if pd.Height != nil {
  1162. panel.SetHeight(*pd.Height)
  1163. }
  1164. }
  1165. // Set optional margin sizes
  1166. if attr&aMARGINS != 0 {
  1167. bs, err := b.parseBorderSizes(fieldMargins, pd.Margins)
  1168. if err != nil {
  1169. return err
  1170. }
  1171. if bs != nil {
  1172. panel.SetMarginsFrom(bs)
  1173. }
  1174. }
  1175. // Set optional border sizes
  1176. if attr&aBORDERS != 0 {
  1177. bs, err := b.parseBorderSizes(fieldBorders, pd.Borders)
  1178. if err != nil {
  1179. return err
  1180. }
  1181. if bs != nil {
  1182. panel.SetBordersFrom(bs)
  1183. }
  1184. }
  1185. // Set optional border color
  1186. if attr&aBORDERCOLOR != 0 {
  1187. c, err := b.parseColor(fieldBorderColor, pd.BorderColor)
  1188. if err != nil {
  1189. return err
  1190. }
  1191. if c != nil {
  1192. panel.SetBordersColor4(c)
  1193. }
  1194. }
  1195. // Set optional paddings sizes
  1196. if attr&aPADDINGS != 0 {
  1197. bs, err := b.parseBorderSizes(fieldPaddings, pd.Paddings)
  1198. if err != nil {
  1199. return err
  1200. }
  1201. if bs != nil {
  1202. panel.SetPaddingsFrom(bs)
  1203. }
  1204. }
  1205. // Set optional color
  1206. if attr&aCOLOR != 0 {
  1207. c, err := b.parseColor(fieldColor, pd.Color)
  1208. if err != nil {
  1209. return err
  1210. }
  1211. if c != nil {
  1212. panel.SetColor4(c)
  1213. }
  1214. }
  1215. if attr&aNAME != 0 && pd.Name != "" {
  1216. panel.SetName(pd.Name)
  1217. }
  1218. if attr&aVISIBLE != 0 && pd.Visible != nil {
  1219. panel.SetVisible(*pd.Visible)
  1220. }
  1221. if attr&aENABLED != 0 && pd.Enabled != nil {
  1222. panel.SetEnabled(*pd.Enabled)
  1223. }
  1224. if attr&aRENDER != 0 && pd.Renderable != nil {
  1225. panel.SetRenderable(*pd.Renderable)
  1226. }
  1227. err := b.setLayoutParams(pd, ipan)
  1228. if err != nil {
  1229. return err
  1230. }
  1231. return b.setLayout(pd, ipan)
  1232. }
  1233. // setLayoutParams sets the optional layout params attribute for specified the panel
  1234. func (b *Builder) setLayoutParams(dp *descPanel, ipan IPanel) error {
  1235. // If layout params not declared, nothing to do
  1236. if dp.LayoutParams == nil {
  1237. return nil
  1238. }
  1239. // Get the parent layout
  1240. if dp.parent == nil {
  1241. return b.err("layoutparams", "No parent defined")
  1242. }
  1243. playout := dp.parent.Layout
  1244. if playout == nil {
  1245. return b.err("layoutparams", "Parent does not have layout")
  1246. }
  1247. panel := ipan.GetPanel()
  1248. dlp := dp.LayoutParams
  1249. // HBoxLayout parameters
  1250. if playout.Type == descTypeHBoxLayout {
  1251. // Creates layout parameter
  1252. params := HBoxLayoutParams{Expand: 0, AlignV: AlignTop}
  1253. // Sets optional expand parameter
  1254. if dlp.Expand != nil {
  1255. params.Expand = *dlp.Expand
  1256. }
  1257. // Sets optional align parameter
  1258. if dlp.AlignV != "" {
  1259. align, ok := mapAlignName[dlp.AlignV]
  1260. if !ok {
  1261. return b.err("align", "Invalid align name:"+dlp.AlignV)
  1262. }
  1263. params.AlignV = align
  1264. }
  1265. panel.SetLayoutParams(&params)
  1266. return nil
  1267. }
  1268. // VBoxLayout parameters
  1269. if playout.Type == descTypeVBoxLayout {
  1270. // Creates layout parameter
  1271. params := VBoxLayoutParams{Expand: 0, AlignH: AlignLeft}
  1272. // Sets optional expand parameter
  1273. if dlp.Expand != nil {
  1274. params.Expand = *dlp.Expand
  1275. }
  1276. // Sets optional align parameter
  1277. if dlp.AlignH != "" {
  1278. align, ok := mapAlignName[dlp.AlignH]
  1279. if !ok {
  1280. return b.err("align", "Invalid align name:"+dlp.AlignH)
  1281. }
  1282. params.AlignH = align
  1283. }
  1284. panel.SetLayoutParams(&params)
  1285. return nil
  1286. }
  1287. // GridLayout parameters
  1288. if playout.Type == descTypeGridLayout {
  1289. // Creates layout parameter
  1290. params := GridLayoutParams{
  1291. ColSpan: 0,
  1292. AlignH: AlignNone,
  1293. AlignV: AlignNone,
  1294. }
  1295. params.ColSpan = dlp.ColSpan
  1296. // Sets optional alignh parameter
  1297. if dlp.AlignH != "" {
  1298. align, ok := mapAlignName[dlp.AlignH]
  1299. if !ok {
  1300. return b.err("alignh", "Invalid align name:"+dlp.AlignH)
  1301. }
  1302. params.AlignH = align
  1303. }
  1304. // Sets optional alignv parameter
  1305. if dlp.AlignV != "" {
  1306. align, ok := mapAlignName[dlp.AlignV]
  1307. if !ok {
  1308. return b.err("alignv", "Invalid align name:"+dlp.AlignV)
  1309. }
  1310. params.AlignV = align
  1311. }
  1312. panel.SetLayoutParams(&params)
  1313. return nil
  1314. }
  1315. // DockLayout parameters
  1316. if playout.Type == descTypeDockLayout {
  1317. if dlp.Edge != "" {
  1318. edge, ok := mapEdgeName[dlp.Edge]
  1319. if !ok {
  1320. return b.err("edge", "Invalid edge name:"+dlp.Edge)
  1321. }
  1322. params := DockLayoutParams{Edge: edge}
  1323. panel.SetLayoutParams(&params)
  1324. return nil
  1325. }
  1326. }
  1327. return b.err("layoutparams", "Invalid parent layout:"+playout.Type)
  1328. }
  1329. // setLayout sets the optional panel layout and layout parameters
  1330. func (b *Builder) setLayout(dp *descPanel, ipan IPanel) error {
  1331. // If layout types not declared, nothing to do
  1332. if dp.Layout == nil {
  1333. return nil
  1334. }
  1335. dl := dp.Layout
  1336. // HBox layout
  1337. if dl.Type == descTypeHBoxLayout {
  1338. hbl := NewHBoxLayout()
  1339. hbl.SetSpacing(dl.Spacing)
  1340. if dl.AlignH != "" {
  1341. align, ok := mapAlignName[dl.AlignH]
  1342. if !ok {
  1343. return b.err("align", "Invalid align name:"+dl.AlignV)
  1344. }
  1345. hbl.SetAlignH(align)
  1346. }
  1347. hbl.SetMinHeight(dl.MinHeight)
  1348. hbl.SetMinWidth(dl.MinWidth)
  1349. ipan.SetLayout(hbl)
  1350. return nil
  1351. }
  1352. // VBox layout
  1353. if dl.Type == descTypeVBoxLayout {
  1354. vbl := NewVBoxLayout()
  1355. vbl.SetSpacing(dl.Spacing)
  1356. if dl.AlignV != "" {
  1357. align, ok := mapAlignName[dl.AlignV]
  1358. if !ok {
  1359. return b.err("align", "Invalid align name:"+dl.AlignV)
  1360. }
  1361. vbl.SetAlignV(align)
  1362. }
  1363. vbl.SetMinHeight(dl.MinHeight)
  1364. vbl.SetMinWidth(dl.MinWidth)
  1365. ipan.SetLayout(vbl)
  1366. return nil
  1367. }
  1368. // Grid layout
  1369. if dl.Type == descTypeGridLayout {
  1370. // Number of columns
  1371. if dl.Cols == 0 {
  1372. return b.err("cols", "Invalid number of columns:"+dl.AlignH)
  1373. }
  1374. grl := NewGridLayout(dl.Cols)
  1375. // Global horizontal alignment
  1376. if dl.AlignH != "" {
  1377. alignh, ok := mapAlignName[dl.AlignH]
  1378. if !ok {
  1379. return b.err("alignh", "Invalid horizontal align:"+dl.AlignH)
  1380. }
  1381. grl.SetAlignH(alignh)
  1382. }
  1383. // Global vertical alignment
  1384. if dl.AlignV != "" {
  1385. alignv, ok := mapAlignName[dl.AlignV]
  1386. if !ok {
  1387. return b.err("alignv", "Invalid vertical align:"+dl.AlignH)
  1388. }
  1389. grl.SetAlignV(alignv)
  1390. }
  1391. // Expansion flags
  1392. grl.SetExpandH(dl.ExpandH)
  1393. grl.SetExpandV(dl.ExpandV)
  1394. ipan.SetLayout(grl)
  1395. return nil
  1396. }
  1397. // Dock layout
  1398. if dl.Type == descTypeDockLayout {
  1399. dockl := NewDockLayout()
  1400. ipan.SetLayout(dockl)
  1401. return nil
  1402. }
  1403. return b.err("layout", "Invalid layout type:"+dl.Type)
  1404. }
  1405. ****/
  1406. // setAttribs sets common attributes from the description to the specified panel
  1407. // The attributes which are set can be specified by the specified bitmask.
  1408. func (b *Builder) setAttribs(am map[string]interface{}, ipan IPanel, attr uint) error {
  1409. panel := ipan.GetPanel()
  1410. // Set optional position
  1411. if attr&aPOS != 0 && am[AttribPosition] != nil {
  1412. va := am[AttribPosition].([]float32)
  1413. panel.SetPosition(va[0], va[1])
  1414. }
  1415. // Set optional panel width
  1416. if attr&aSIZE != 0 && am[AttribWidth] != nil {
  1417. panel.SetWidth(am[AttribWidth].(float32))
  1418. }
  1419. // Sets optional panel height
  1420. if attr&aSIZE != 0 && am[AttribHeight] != nil {
  1421. panel.SetHeight(am[AttribHeight].(float32))
  1422. }
  1423. // Set optional margin sizes
  1424. if attr&aMARGINS != 0 && am[AttribMargins] != nil {
  1425. panel.SetMarginsFrom(am[AttribMargins].(*BorderSizes))
  1426. }
  1427. // Set optional border sizes
  1428. if attr&aBORDERS != 0 && am[AttribBorders] != nil {
  1429. panel.SetBordersFrom(am[AttribBorders].(*BorderSizes))
  1430. }
  1431. // Set optional border color
  1432. if attr&aBORDERCOLOR != 0 && am[AttribBorderColor] != nil {
  1433. panel.SetBordersColor4(am[AttribBorderColor].(*math32.Color4))
  1434. }
  1435. // Set optional paddings sizes
  1436. if attr&aPADDINGS != 0 && am[AttribPaddings] != nil {
  1437. panel.SetPaddingsFrom(am[AttribPaddings].(*BorderSizes))
  1438. }
  1439. // Set optional panel color
  1440. if attr&aCOLOR != 0 && am[AttribColor] != nil {
  1441. panel.SetColor4(am[AttribColor].(*math32.Color4))
  1442. }
  1443. if attr&aNAME != 0 && am[AttribName] != nil {
  1444. panel.SetName(am[AttribName].(string))
  1445. }
  1446. if attr&aVISIBLE != 0 && am[AttribVisible] != nil {
  1447. panel.SetVisible(am[AttribVisible].(bool))
  1448. }
  1449. if attr&aENABLED != 0 && am[AttribEnabled] != nil {
  1450. panel.SetEnabled(am[AttribEnabled].(bool))
  1451. }
  1452. if attr&aRENDER != 0 && am[AttribRender] != nil {
  1453. panel.SetRenderable(am[AttribRender].(bool))
  1454. }
  1455. // err := b.setLayoutParams(am, ipan)
  1456. // if err != nil {
  1457. // return err
  1458. // }
  1459. // return b.setLayout(am, ipan)
  1460. return nil
  1461. }
  1462. //func (b *Builder) setMenuShortcut(mi *MenuItem, fname, field string) error {
  1463. //
  1464. // field = strings.Trim(field, " ")
  1465. // if field == "" {
  1466. // return nil
  1467. // }
  1468. // parts := strings.Split(field, "+")
  1469. // var mods window.ModifierKey
  1470. // for i := 0; i < len(parts)-1; i++ {
  1471. // switch parts[i] {
  1472. // case "Shift":
  1473. // mods |= window.ModShift
  1474. // case "Ctrl":
  1475. // mods |= window.ModControl
  1476. // case "Alt":
  1477. // mods |= window.ModAlt
  1478. // default:
  1479. // return b.err(am, fname, "Invalid shortcut:"+field)
  1480. // }
  1481. // }
  1482. // // The last part must be a key
  1483. // key := parts[len(parts)-1]
  1484. // for kcode, kname := range mapKeyText {
  1485. // if kname == key {
  1486. // mi.SetShortcut(mods, kcode)
  1487. // return nil
  1488. // }
  1489. // }
  1490. // return b.err(fname, "Invalid shortcut:"+field)
  1491. //}
  1492. // parseFloats parses a string with a list of floats with the specified size
  1493. // and returns a slice. The specified size is 0 any number of floats is allowed.
  1494. // The individual values can be separated by spaces or commas
  1495. func (b *Builder) parseFloats(am map[string]interface{}, fname string, min, max int) ([]float32, error) {
  1496. // Checks if field is empty
  1497. v := am[fname]
  1498. if v == nil {
  1499. return nil, nil
  1500. }
  1501. // If field has only one value, it is an int or a float64
  1502. switch ft := v.(type) {
  1503. case int:
  1504. return []float32{float32(ft)}, nil
  1505. case float64:
  1506. return []float32{float32(ft)}, nil
  1507. }
  1508. // Converts to string
  1509. fs, ok := v.(string)
  1510. if !ok {
  1511. return nil, b.err(am, fname, "Not a string")
  1512. }
  1513. // Checks if string field is empty
  1514. fs = strings.Trim(fs, " ")
  1515. if fs == "" {
  1516. return nil, nil
  1517. }
  1518. // Separate individual fields
  1519. var parts []string
  1520. if strings.Index(fs, ",") < 0 {
  1521. parts = strings.Fields(fs)
  1522. } else {
  1523. parts = strings.Split(fs, ",")
  1524. }
  1525. if len(parts) < min || len(parts) > max {
  1526. return nil, b.err(am, fname, "Invalid number of float32 values")
  1527. }
  1528. // Parse each field value and appends to slice
  1529. var values []float32
  1530. for i := 0; i < len(parts); i++ {
  1531. val, err := strconv.ParseFloat(strings.Trim(parts[i], " "), 32)
  1532. if err != nil {
  1533. return nil, b.err(am, fname, err.Error())
  1534. }
  1535. values = append(values, float32(val))
  1536. }
  1537. return values, nil
  1538. }
  1539. // err creates and returns an error for the current object, field name and with the specified message
  1540. func (b *Builder) err(am map[string]interface{}, fname, msg string) error {
  1541. return fmt.Errorf("Error in object:%s field:%s -> %s", am[AttribName], fname, msg)
  1542. }
  1543. // debugPrint prints the internal attribute map of the builder for debugging.
  1544. // This map cannot be printed by fmt.Printf() because it has cycles.
  1545. // A map contains a key: _parent, which pointer to is parent map, if any.
  1546. func (b *Builder) debugPrint(v interface{}, level int) {
  1547. switch vt := v.(type) {
  1548. case map[string]interface{}:
  1549. level += 3
  1550. fmt.Printf("\n")
  1551. for mk, mv := range vt {
  1552. if mk == "_parent" {
  1553. continue
  1554. }
  1555. fmt.Printf("%s%s:", strings.Repeat(" ", level), mk)
  1556. b.debugPrint(mv, level)
  1557. }
  1558. case []map[string]interface{}:
  1559. for _, v := range vt {
  1560. b.debugPrint(v, level)
  1561. }
  1562. default:
  1563. fmt.Printf(" %v (%T)\n", vt, vt)
  1564. }
  1565. }