| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858 |
- // Copyright 2016 The G3N Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package gui
- import (
- "path/filepath"
- "github.com/g3n/engine/math32"
- "github.com/g3n/engine/window"
- )
- // buildPanel builds an object of type Panel
- func buildPanel(b *Builder, am map[string]interface{}) (IPanel, error) {
- pan := NewPanel(0, 0)
- err := b.SetAttribs(am, pan, asPANEL)
- if err != nil {
- return nil, err
- }
- // Builds children recursively
- if am[AttribItems] != nil {
- items := am[AttribItems].([]map[string]interface{})
- for i := 0; i < len(items); i++ {
- item := items[i]
- child, err := b.build(item, pan)
- if err != nil {
- return nil, err
- }
- pan.Add(child)
- }
- }
- return pan, nil
- }
- // buildImagePanel builds a gui object of type ImagePanel
- func buildImagePanel(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Checks imagefile attribute
- if am[AttribImageFile] == nil {
- return nil, b.err(am, AttribImageFile, "Must be supplied")
- }
- // If path is not absolute join with user supplied image base path
- imagefile := am[AttribImageFile].(string)
- if !filepath.IsAbs(imagefile) {
- imagefile = filepath.Join(b.imgpath, imagefile)
- }
- // Builds panel and set common attributes
- panel, err := NewImage(imagefile)
- if err != nil {
- return nil, err
- }
- err = b.SetAttribs(am, panel, asPANEL)
- if err != nil {
- return nil, err
- }
- // Sets optional AspectWidth attribute
- if aw := am[AttribAspectWidth]; aw != nil {
- panel.SetContentAspectWidth(aw.(float32))
- }
- // Sets optional AspectHeight attribute
- if ah := am[AttribAspectHeight]; ah != nil {
- panel.SetContentAspectHeight(ah.(float32))
- }
- // Builds children recursively
- if am[AttribItems] != nil {
- items := am[AttribItems].([]map[string]interface{})
- for i := 0; i < len(items); i++ {
- item := items[i]
- child, err := b.build(item, panel)
- if err != nil {
- return nil, err
- }
- panel.Add(child)
- }
- }
- return panel, nil
- }
- // buildLabel builds a gui object of type Label
- func buildLabel(b *Builder, am map[string]interface{}) (IPanel, error) {
- var label *Label
- if am[AttribIcon] != nil {
- label = NewLabel(am[AttribIcon].(string), true)
- } else if am[AttribText] != nil {
- label = NewLabel(am[AttribText].(string))
- } else {
- label = NewLabel("")
- }
- // Sets common attributes
- err := b.SetAttribs(am, label, asPANEL)
- if err != nil {
- return nil, err
- }
- // Set optional background color
- if bgc := am[AttribBgColor]; bgc != nil {
- label.SetBgColor4(bgc.(*math32.Color4))
- }
- // Set optional font color
- if fc := am[AttribFontColor]; fc != nil {
- label.SetColor4(fc.(*math32.Color4))
- }
- // Sets optional font size
- if fs := am[AttribFontSize]; fs != nil {
- label.SetFontSize(float64(fs.(float32)))
- }
- // Sets optional font dpi
- if fdpi := am[AttribFontDPI]; fdpi != nil {
- label.SetFontDPI(float64(fdpi.(float32)))
- }
- // Sets optional line spacing
- if ls := am[AttribLineSpacing]; ls != nil {
- label.SetLineSpacing(float64(ls.(float32)))
- }
- return label, nil
- }
- // buildImageLabel builds a gui object of type: ImageLabel
- func buildImageLabel(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds image label and set common attributes
- var text string
- if am[AttribText] != nil {
- text = am[AttribText].(string)
- }
- imglabel := NewImageLabel(text)
- err := b.SetAttribs(am, imglabel, asPANEL)
- if err != nil {
- return nil, err
- }
- // Sets optional icon(s)
- if icon := am[AttribIcon]; icon != nil {
- imglabel.SetIcon(icon.(string))
- }
- // Sets optional image from file
- // If path is not absolute join with user supplied image base path
- if imgf := am[AttribImageFile]; imgf != nil {
- path := imgf.(string)
- if !filepath.IsAbs(path) {
- path = filepath.Join(b.imgpath, path)
- }
- err := imglabel.SetImageFromFile(path)
- if err != nil {
- return nil, err
- }
- }
- return imglabel, nil
- }
- // buildButton builds a gui object of type: Button
- func buildButton(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds button and set commont attributes
- var text string
- if am[AttribText] != nil {
- text = am[AttribText].(string)
- }
- button := NewButton(text)
- err := b.SetAttribs(am, button, asWIDGET)
- if err != nil {
- return nil, err
- }
- // Sets optional icon(s)
- if icon := am[AttribIcon]; icon != nil {
- button.SetIcon(icon.(string))
- }
- // Sets optional image from file
- // If path is not absolute join with user supplied image base path
- if imgf := am[AttribImageFile]; imgf != nil {
- path := imgf.(string)
- if !filepath.IsAbs(path) {
- path = filepath.Join(b.imgpath, path)
- }
- err := button.SetImage(path)
- if err != nil {
- return nil, err
- }
- }
- return button, nil
- }
- // buildEdit builds a gui object of type: "Edit"
- func buildEdit(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds button and set attributes
- var width float32
- var placeholder string
- if aw := am[AttribWidth]; aw != nil {
- width = aw.(float32)
- }
- if ph := am[AttribPlaceHolder]; ph != nil {
- placeholder = ph.(string)
- }
- edit := NewEdit(int(width), placeholder)
- err := b.SetAttribs(am, edit, asWIDGET)
- if err != nil {
- return nil, err
- }
- return edit, nil
- }
- // buildCheckBox builds a gui object of type: CheckBox
- func buildCheckBox(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds check box and set commont attributes
- var text string
- if am[AttribText] != nil {
- text = am[AttribText].(string)
- }
- cb := NewCheckBox(text)
- err := b.SetAttribs(am, cb, asWIDGET)
- if err != nil {
- return nil, err
- }
- // Sets optional checked value
- if checked := am[AttribChecked]; checked != nil {
- cb.SetValue(checked.(bool))
- }
- return cb, nil
- }
- // buildRadioButton builds a gui object of type: RadioButton
- func buildRadioButton(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds check box and set commont attributes
- var text string
- if am[AttribText] != nil {
- text = am[AttribText].(string)
- }
- rb := NewRadioButton(text)
- err := b.SetAttribs(am, rb, asWIDGET)
- if err != nil {
- return nil, err
- }
- // Sets optional radio button group
- if gr := am[AttribGroup]; gr != nil {
- rb.SetGroup(gr.(string))
- }
- // Sets optional checked value
- if checked := am[AttribChecked]; checked != nil {
- rb.SetValue(checked.(bool))
- }
- return rb, nil
- }
- // buildVList builds a gui object of type: VList
- func buildVList(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds list and set commont attributes
- list := NewVList(0, 0)
- err := b.SetAttribs(am, list, asWIDGET)
- if err != nil {
- return nil, err
- }
- // Builds children
- if am[AttribItems] != nil {
- items := am[AttribItems].([]map[string]interface{})
- for i := 0; i < len(items); i++ {
- item := items[i]
- child, err := b.build(item, list)
- if err != nil {
- return nil, err
- }
- list.Add(child)
- }
- }
- return list, nil
- }
- // buildHList builds a gui object of type: VList
- func buildHList(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds list and set commont attributes
- list := NewHList(0, 0)
- err := b.SetAttribs(am, list, asWIDGET)
- if err != nil {
- return nil, err
- }
- // Builds children
- if am[AttribItems] != nil {
- items := am[AttribItems].([]map[string]interface{})
- for i := 0; i < len(items); i++ {
- item := items[i]
- child, err := b.build(item, list)
- if err != nil {
- return nil, err
- }
- list.Add(child)
- }
- }
- return list, nil
- }
- // buildDropDown builds a gui object of type: DropDown
- func buildDropDown(b *Builder, am map[string]interface{}) (IPanel, error) {
- // If image label attribute defined use it, otherwise
- // uses default value.
- var imglabel *ImageLabel
- if iv := am[AttribImageLabel]; iv != nil {
- imgl := iv.(map[string]interface{})
- imgl[AttribType] = TypeImageLabel
- ipan, err := b.build(imgl, nil)
- if err != nil {
- return nil, err
- }
- imglabel = ipan.(*ImageLabel)
- } else {
- imglabel = NewImageLabel("")
- }
- // Builds drop down and set common attributes
- dd := NewDropDown(0, imglabel)
- err := b.SetAttribs(am, dd, asWIDGET)
- if err != nil {
- return nil, err
- }
- // Builds children
- if am[AttribItems] != nil {
- items := am[AttribItems].([]map[string]interface{})
- for i := 0; i < len(items); i++ {
- item := items[i]
- child, err := b.build(item, dd)
- if err != nil {
- return nil, err
- }
- dd.Add(child.(*ImageLabel))
- }
- }
- return dd, nil
- }
- // buildMenu builds a gui object of type: Menu or MenuBar from the
- // specified panel descriptor.
- func buildMenu(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds menu bar or menu
- var menu *Menu
- if am[AttribType].(string) == TypeMenuBar {
- menu = NewMenuBar()
- } else {
- menu = NewMenu()
- }
- // Only sets attribs for top level menus
- if pi := am[AttribParentInternal]; pi != nil {
- par := pi.(map[string]interface{})
- ptype := ""
- if ti := par[AttribType]; ti != nil {
- ptype = ti.(string)
- }
- if ptype != TypeMenu && ptype != TypeMenuBar {
- err := b.SetAttribs(am, menu, asWIDGET)
- if err != nil {
- return nil, err
- }
- }
- }
- // Builds and adds menu items
- if am[AttribItems] != nil {
- items := am[AttribItems].([]map[string]interface{})
- for i := 0; i < len(items); i++ {
- // Get the item optional type and text
- item := items[i]
- itype := ""
- itext := ""
- if iv := item[AttribType]; iv != nil {
- itype = iv.(string)
- }
- if iv := item[AttribText]; iv != nil {
- itext = iv.(string)
- }
- // Item is another menu
- if itype == TypeMenu {
- subm, err := buildMenu(b, item)
- if err != nil {
- return nil, err
- }
- menu.AddMenu(itext, subm.(*Menu))
- continue
- }
- // Item is a separator
- if itext == TypeSeparator {
- menu.AddSeparator()
- continue
- }
- // Item must be a menu option
- mi := menu.AddOption(itext)
- // Set item optional icon(s)
- if icon := item[AttribIcon]; icon != nil {
- mi.SetIcon(icon.(string))
- }
- // Sets optional menu item shortcut
- if sci := item[AttribShortcut]; sci != nil {
- sc := sci.([]int)
- mi.SetShortcut(window.ModifierKey(sc[0]), window.Key(sc[1]))
- }
- }
- }
- return menu, nil
- }
- // buildSlider builds a gui object of type: HSlider or VSlider
- func buildSlider(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds horizontal or vertical slider
- var slider *Slider
- if am[AttribType].(string) == TypeHSlider {
- slider = NewHSlider(0, 0)
- } else {
- slider = NewVSlider(0, 0)
- }
- // Sets common attributes
- err := b.SetAttribs(am, slider, asWIDGET)
- if err != nil {
- return nil, err
- }
- // Sets optional text
- if itext := am[AttribText]; itext != nil {
- slider.SetText(itext.(string))
- }
- // Sets optional scale factor
- if isf := am[AttribScaleFactor]; isf != nil {
- slider.SetScaleFactor(isf.(float32))
- }
- // Sets optional value
- if iv := am[AttribValue]; iv != nil {
- slider.SetValue(iv.(float32))
- }
- return slider, nil
- }
- // buildSplitter builds a gui object of type: HSplitterr or VSplitter
- func buildSplitter(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds horizontal or vertical splitter
- var splitter *Splitter
- if am[AttribType].(string) == TypeHSplitter {
- splitter = NewHSplitter(0, 0)
- } else {
- splitter = NewVSplitter(0, 0)
- }
- // Sets common attributes
- err := b.SetAttribs(am, splitter, asWIDGET)
- if err != nil {
- return nil, err
- }
- // Sets optional split value
- if v := am[AttribSplit]; v != nil {
- splitter.SetSplit(v.(float32))
- }
- // Internal function to set each of the splitter's panel attributes and items
- setpan := func(attrib string, pan *Panel) error {
- // Get internal panel attributes
- ipattribs := am[attrib]
- if ipattribs == nil {
- return nil
- }
- pattr := ipattribs.(map[string]interface{})
- // Set panel attributes
- err := b.SetAttribs(pattr, pan, asPANEL)
- if err != nil {
- return nil
- }
- // Builds panel children
- if pattr[AttribItems] != nil {
- items := pattr[AttribItems].([]map[string]interface{})
- for i := 0; i < len(items); i++ {
- item := items[i]
- child, err := b.build(item, pan)
- if err != nil {
- return err
- }
- pan.Add(child)
- }
- }
- return nil
- }
- // Set optional splitter panel's attributes
- err = setpan(AttribPanel0, &splitter.P0)
- if err != nil {
- return nil, err
- }
- err = setpan(AttribPanel1, &splitter.P1)
- if err != nil {
- return nil, err
- }
- return splitter, nil
- }
- // buildTree builds a gui object of type: Tree
- func buildTree(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds tree and sets its common attributes
- tree := NewTree(0, 0)
- err := b.SetAttribs(am, tree, asWIDGET)
- if err != nil {
- return nil, err
- }
- // Internal function to build tree nodes recursively
- var buildItems func(am map[string]interface{}, pnode *TreeNode) error
- buildItems = func(am map[string]interface{}, pnode *TreeNode) error {
- v := am[AttribItems]
- if v == nil {
- return nil
- }
- items := v.([]map[string]interface{})
- for i := 0; i < len(items); i++ {
- // Get the item type
- item := items[i]
- itype := ""
- if v := item[AttribType]; v != nil {
- itype = v.(string)
- }
- itext := ""
- if v := item[AttribText]; v != nil {
- itext = v.(string)
- }
- // Item is a tree node
- if itype == "" || itype == TypeTreeNode {
- var node *TreeNode
- if pnode == nil {
- node = tree.AddNode(itext)
- } else {
- node = pnode.AddNode(itext)
- }
- err := buildItems(item, node)
- if err != nil {
- return err
- }
- continue
- }
- // Other controls
- ipan, err := b.build(item, nil)
- if err != nil {
- return err
- }
- if pnode == nil {
- tree.Add(ipan)
- } else {
- pnode.Add(ipan)
- }
- }
- return nil
- }
- // Build nodes
- err = buildItems(am, nil)
- if err != nil {
- return nil, err
- }
- return tree, nil
- }
- // buildWindow builds a gui object of type: Window
- func buildWindow(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds window and sets its common attributes
- win := NewWindow(0, 0)
- err := b.SetAttribs(am, win, asWIDGET)
- if err != nil {
- return nil, err
- }
- // Sets optional title
- if title := am[AttribTitle]; title != nil {
- win.SetTitle(title.(string))
- }
- // Set optional resizable borders
- if resiz := am[AttribResizeBorders]; resiz != nil {
- win.SetResizable(resiz.(ResizeBorders))
- }
- // Builds window children
- if v := am[AttribItems]; v != nil {
- items := v.([]map[string]interface{})
- for i := 0; i < len(items); i++ {
- item := items[i]
- child, err := b.build(item, win)
- if err != nil {
- return nil, err
- }
- win.Add(child)
- }
- }
- return win, nil
- }
- // buildChart builds a gui object of type: Chart
- func buildChart(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Builds window and sets its common attributes
- chart := NewChart(0, 0)
- err := b.SetAttribs(am, chart, asPANEL)
- if err != nil {
- return nil, err
- }
- // Sets optional title
- if title := am[AttribTitle]; title != nil {
- chart.SetTitle(title.(string), 14)
- }
- // Sets x scale attibutes
- if v := am[AttribScalex]; v != nil {
- sx := v.(map[string]interface{})
- // Sets optional x scale margin
- if mx := sx[AttribMargin]; mx != nil {
- chart.SetMarginX(mx.(float32))
- }
- // Sets optional x scale format
- if fx := sx[AttribFormat]; fx != nil {
- chart.SetFormatX(fx.(string))
- }
- // Sets optional x scale font size
- if fsize := sx[AttribFontSize]; fsize != nil {
- chart.SetFontSizeX(float64(fsize.(float32)))
- }
- // Number of lines
- lines := 4
- if v := sx[AttribLines]; v != nil {
- lines = v.(int)
- }
- // Lines color
- color4 := math32.NewColor4("Black")
- if v := sx[AttribColor]; v != nil {
- color4 = v.(*math32.Color4)
- }
- color := color4.ToColor()
- chart.SetScaleX(lines, &color)
- // Range first
- firstX := float32(0)
- if v := sx[AttribFirstx]; v != nil {
- firstX = v.(float32)
- }
- // Range step
- stepX := float32(1)
- if v := sx[AttribStepx]; v != nil {
- stepX = v.(float32)
- }
- // Range count step
- countStepX := float32(1)
- if v := sx[AttribStepx]; v != nil {
- countStepX = v.(float32)
- }
- chart.SetRangeX(firstX, stepX, countStepX)
- }
- // Sets y scale attibutes
- if v := am[AttribScaley]; v != nil {
- sy := v.(map[string]interface{})
- // Sets optional y scale margin
- if my := sy[AttribMargin]; my != nil {
- chart.SetMarginY(my.(float32))
- }
- // Sets optional y scale format
- if fy := sy[AttribFormat]; fy != nil {
- chart.SetFormatY(fy.(string))
- }
- // Sets optional y scale font size
- if fsize := sy[AttribFontSize]; fsize != nil {
- chart.SetFontSizeY(float64(fsize.(float32)))
- }
- // Number of lines
- lines := 4
- if v := sy[AttribLines]; v != nil {
- lines = v.(int)
- }
- // Lines color
- color4 := math32.NewColor4("Black")
- if v := sy[AttribColor]; v != nil {
- color4 = v.(*math32.Color4)
- }
- color := color4.ToColor()
- chart.SetScaleY(lines, &color)
- // Range min
- rmin := float32(-10)
- if v := sy[AttribRangeMin]; v != nil {
- rmin = v.(float32)
- }
- // Range max
- rmax := float32(10)
- if v := sy[AttribRangeMax]; v != nil {
- rmax = v.(float32)
- }
- chart.SetRangeY(rmin, rmax)
- // Range auto
- if rauto := sy[AttribRangeAuto]; rauto != nil {
- chart.SetRangeYauto(v.(bool))
- }
- }
- return chart, nil
- }
- // buildTable builds a gui object of type: Table
- func buildTable(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Internal function to build a TableColumn from its attribute map
- buildTableCol := func(b *Builder, am map[string]interface{}) (*TableColumn, error) {
- tc := &TableColumn{}
- if iv := am[AttribId]; iv != nil {
- tc.Id = iv.(string)
- }
- if iv := am[AttribHeader]; iv != nil {
- tc.Header = iv.(string)
- }
- if iv := am[AttribWidth]; iv != nil {
- tc.Width = iv.(float32)
- }
- if iv := am[AttribMinwidth]; iv != nil {
- tc.Minwidth = iv.(float32)
- }
- if iv := am[AttribHidden]; iv != nil {
- tc.Hidden = iv.(bool)
- }
- if iv := am[AttribFormat]; iv != nil {
- tc.Format = iv.(string)
- }
- if iv := am[AttribExpand]; iv != nil {
- tc.Expand = iv.(float32)
- }
- if iv := am[AttribResize]; iv != nil {
- tc.Resize = iv.(bool)
- }
- if iv := am[AttribSortType]; iv != nil {
- tc.Sort = iv.(TableSortType)
- }
- return tc, nil
- }
- // Builds table columns array
- tableCols := []TableColumn{}
- if iv := am[AttribColumns]; iv != nil {
- cols := iv.([]map[string]interface{})
- for _, c := range cols {
- tc, err := buildTableCol(b, c)
- if err != nil {
- return nil, err
- }
- tableCols = append(tableCols, *tc)
- }
- }
- // Creates table and set common attributes
- table, err := NewTable(0, 0, tableCols)
- if err != nil {
- return nil, err
- }
- err = b.SetAttribs(am, table, asPANEL)
- if err != nil {
- return nil, err
- }
- // Sets optional show header attribute
- if show := am[AttribShowHeader]; show != nil {
- table.ShowHeader(show.(bool))
- }
- return table, nil
- }
- // buildTabBar builds a gui object of type: TabBare
- func buildTabBar(b *Builder, am map[string]interface{}) (IPanel, error) {
- // Creates TabBar and set common attributes
- tabbar := NewTabBar(0, 0)
- err := b.SetAttribs(am, tabbar, asPANEL)
- if err != nil {
- return nil, err
- }
- v := am[AttribItems]
- // For each tab
- if v != nil {
- items := v.([]map[string]interface{})
- for _, item := range items {
- // Creates Tab
- text := ""
- if v := item[AttribText]; v != nil {
- text = v.(string)
- }
- tab := tabbar.AddTab(text)
- // Sets optional icon
- if v := item[AttribIcon]; v != nil {
- tab.SetIcon(v.(string))
- }
- // Sets optional image
- if v := item[AttribImageFile]; v != nil {
- // If path is not absolute join with user supplied image base path
- imagefile := v.(string)
- if !filepath.IsAbs(imagefile) {
- imagefile = filepath.Join(b.imgpath, imagefile)
- }
- err := tab.SetImage(imagefile)
- if err != nil {
- return nil, err
- }
- }
- // Sets content panel
- if v := item[AttribContent]; v != nil {
- am := v.(map[string]interface{})
- content, err := b.build(am, nil)
- if err != nil {
- return nil, err
- }
- tab.SetContent(content)
- }
- // Sets pinned state
- if v := item[AttribPinned]; v != nil {
- tab.SetPinned(v.(bool))
- }
- }
- }
- return tabbar, nil
- }
|