menu.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  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. "github.com/g3n/engine/gui/assets"
  7. "github.com/g3n/engine/math32"
  8. "github.com/g3n/engine/window"
  9. )
  10. type Menu struct {
  11. Panel // embedded panel
  12. styles *MenuStyles // pointer to current styles
  13. bar bool // true for menu bar
  14. items []*MenuItem // menu items
  15. autoOpen bool // open sub menus when mouse over if true
  16. mitem *MenuItem // parent menu item for sub menu
  17. }
  18. // MenuBodyStyle describes the style of the menu body
  19. type MenuBodyStyle struct {
  20. Border BorderSizes
  21. Paddings BorderSizes
  22. BorderColor math32.Color4
  23. BgColor math32.Color
  24. FgColor math32.Color
  25. }
  26. // MenuBodyStyles describes all styles of the menu body
  27. type MenuBodyStyles struct {
  28. Normal MenuBodyStyle
  29. Over MenuBodyStyle
  30. Focus MenuBodyStyle
  31. Disabled MenuBodyStyle
  32. }
  33. // MenuStyles describes all styles of the menu body and menu item
  34. type MenuStyles struct {
  35. Body *MenuBodyStyles // Menu body styles
  36. Item *MenuItemStyles // Menu item styles
  37. }
  38. // MenuItem is an option of a Menu
  39. type MenuItem struct {
  40. Panel // embedded panel
  41. styles *MenuItemStyles // pointer to current styles
  42. menu *Menu // pointer to parent menu
  43. licon *Label // optional left icon label
  44. label *Label // optional text label (nil for separators)
  45. shortcut *Label // optional shorcut text label
  46. ricon *Label // optional right internal icon label for submenu
  47. id string // optional text id
  48. icode int // icon code (if icon is set)
  49. submenu *Menu // pointer to optional associated sub menu
  50. keyModifier window.ModifierKey // shortcut key modifier
  51. keyCode window.Key // shortcut key code
  52. disabled bool // item disabled state
  53. selected bool // selection state
  54. }
  55. // MenuItemStyle describes the style of a menu item
  56. type MenuItemStyle struct {
  57. Border BorderSizes
  58. Paddings BorderSizes
  59. BorderColor math32.Color4
  60. BgColor math32.Color
  61. FgColor math32.Color
  62. IconPaddings BorderSizes
  63. ShortcutPaddings BorderSizes
  64. RiconPaddings BorderSizes
  65. }
  66. // MenuItemStyles describes all the menu item styles
  67. type MenuItemStyles struct {
  68. Normal MenuItemStyle
  69. Over MenuItemStyle
  70. Disabled MenuItemStyle
  71. Separator MenuItemStyle
  72. }
  73. var mapKeyModifier = map[window.ModifierKey]string{
  74. window.ModShift: "Shift",
  75. window.ModControl: "Ctrl",
  76. window.ModAlt: "Alt",
  77. }
  78. var mapKeyText = map[window.Key]string{
  79. window.KeyApostrophe: "'",
  80. window.KeyComma: ",",
  81. window.KeyMinus: "-",
  82. window.KeyPeriod: ".",
  83. window.KeySlash: "/",
  84. window.Key0: "0",
  85. window.Key1: "1",
  86. window.Key2: "2",
  87. window.Key3: "3",
  88. window.Key4: "4",
  89. window.Key5: "5",
  90. window.Key6: "6",
  91. window.Key7: "7",
  92. window.Key8: "8",
  93. window.Key9: "9",
  94. window.KeySemicolon: ";",
  95. window.KeyEqual: "=",
  96. window.KeyA: "A",
  97. window.KeyB: "B",
  98. window.KeyC: "C",
  99. window.KeyD: "D",
  100. window.KeyE: "E",
  101. window.KeyF: "F",
  102. window.KeyG: "G",
  103. window.KeyH: "H",
  104. window.KeyI: "I",
  105. window.KeyJ: "J",
  106. window.KeyK: "K",
  107. window.KeyL: "L",
  108. window.KeyM: "M",
  109. window.KeyN: "N",
  110. window.KeyO: "O",
  111. window.KeyP: "P",
  112. window.KeyQ: "Q",
  113. window.KeyR: "R",
  114. window.KeyS: "S",
  115. window.KeyT: "T",
  116. window.KeyU: "U",
  117. window.KeyV: "V",
  118. window.KeyW: "W",
  119. window.KeyX: "X",
  120. window.KeyY: "Y",
  121. window.KeyZ: "Z",
  122. window.KeyF1: "F1",
  123. window.KeyF2: "F2",
  124. window.KeyF3: "F3",
  125. window.KeyF4: "F4",
  126. window.KeyF5: "F5",
  127. window.KeyF6: "F6",
  128. window.KeyF7: "F7",
  129. window.KeyF8: "F8",
  130. window.KeyF9: "F9",
  131. window.KeyF10: "F10",
  132. window.KeyF11: "F11",
  133. window.KeyF12: "F12",
  134. }
  135. // NewMenuBar creates and returns a pointer to a new empty menu bar
  136. func NewMenuBar() *Menu {
  137. m := NewMenu()
  138. m.bar = true
  139. return m
  140. }
  141. // NewMenu creates and returns a pointer to a new empty vertical menu
  142. func NewMenu() *Menu {
  143. m := new(Menu)
  144. m.Panel.Initialize(0, 0)
  145. m.styles = &StyleDefault.Menu
  146. m.items = make([]*MenuItem, 0)
  147. m.Panel.Subscribe(OnCursorEnter, m.onCursor)
  148. m.Panel.Subscribe(OnCursorLeave, m.onCursor)
  149. m.Panel.Subscribe(OnKeyDown, m.onKey)
  150. m.Panel.Subscribe(OnMouseOut, m.onMouse)
  151. m.update()
  152. return m
  153. }
  154. // AddOption creates and adds a new menu item to this menu with the
  155. // specified text and returns the pointer to the created menu item.
  156. func (m *Menu) AddOption(text string) *MenuItem {
  157. mi := newMenuItem(text, m.styles.Item)
  158. m.Panel.Add(mi)
  159. m.items = append(m.items, mi)
  160. mi.menu = m
  161. m.recalc()
  162. return mi
  163. }
  164. // AddSeparator creates and adds a new separator to the menu
  165. func (m *Menu) AddSeparator() *MenuItem {
  166. mi := newMenuItem("", m.styles.Item)
  167. m.Panel.Add(mi)
  168. m.items = append(m.items, mi)
  169. mi.menu = m
  170. m.recalc()
  171. return mi
  172. }
  173. // AddMenu creates and adds a new menu item to this menu with the
  174. // specified text and sub menu.
  175. // Returns the pointer to the created menu item.
  176. func (m *Menu) AddMenu(text string, subm *Menu) *MenuItem {
  177. mi := newMenuItem(text, m.styles.Item)
  178. m.Panel.Add(mi)
  179. m.items = append(m.items, mi)
  180. mi.submenu = subm
  181. mi.submenu.SetVisible(false)
  182. mi.submenu.SetBounded(false)
  183. mi.submenu.mitem = mi
  184. mi.submenu.autoOpen = true
  185. mi.menu = m
  186. mi.ricon = NewIconLabel(string(assets.ChevronRight))
  187. mi.Panel.Add(mi.ricon)
  188. mi.Panel.Add(mi.submenu)
  189. mi.update()
  190. m.recalc()
  191. return mi
  192. }
  193. // RemoveItem removes the specified menu item from this menu
  194. func (m *Menu) RemoveItem(mi *MenuItem) {
  195. }
  196. // onCursor process subscribed cursor events
  197. func (m *Menu) onCursor(evname string, ev interface{}) {
  198. if evname == OnCursorEnter {
  199. m.root.SetKeyFocus(m)
  200. }
  201. m.root.StopPropagation(StopAll)
  202. }
  203. // onKey process subscribed key events
  204. func (m *Menu) onKey(evname string, ev interface{}) {
  205. sel := m.selectedPos()
  206. if sel < 0 {
  207. return
  208. }
  209. mi := m.items[sel]
  210. kev := ev.(*window.KeyEvent)
  211. switch kev.Keycode {
  212. // Select next enabled menu item
  213. case window.KeyDown:
  214. // Select next enabled menu item
  215. if m.bar {
  216. // If selected item is not a sub menu, ignore
  217. if mi.submenu == nil {
  218. return
  219. }
  220. // Sets autoOpen and selects sub menu
  221. m.autoOpen = true
  222. mi.update()
  223. m.root.SetKeyFocus(mi.submenu)
  224. mi.submenu.setSelectedPos(0)
  225. return
  226. }
  227. // Select next enabled menu item for vertical menu
  228. next := m.nextItem(sel)
  229. m.setSelectedPos(next)
  230. // Up -> Previous item for vertical menus
  231. case window.KeyUp:
  232. if m.bar {
  233. return
  234. }
  235. prev := m.prevItem(sel)
  236. m.setSelectedPos(prev)
  237. // Left -> Previous menu item for menu bar
  238. case window.KeyLeft:
  239. // For menu bar, select previous menu item
  240. if m.bar {
  241. prev := m.prevItem(sel)
  242. m.setSelectedPos(prev)
  243. return
  244. }
  245. // If menu has parent menu item
  246. if m.mitem != nil {
  247. if m.mitem.menu.bar {
  248. sel := m.mitem.menu.selectedPos()
  249. prev := m.mitem.menu.prevItem(sel)
  250. m.mitem.menu.setSelectedPos(prev)
  251. } else {
  252. m.mitem.menu.setSelectedItem(m.mitem)
  253. }
  254. m.root.SetKeyFocus(m.mitem.menu)
  255. return
  256. }
  257. // Right -> Next menu bar item || Next sub menu
  258. case window.KeyRight:
  259. mi := m.items[sel]
  260. // For menu bar, select next menu item
  261. if m.bar {
  262. next := m.nextItem(sel)
  263. m.setSelectedPos(next)
  264. return
  265. }
  266. // Enter into sub menu
  267. if mi.submenu != nil {
  268. m.root.SetKeyFocus(mi.submenu)
  269. mi.submenu.setSelectedPos(0)
  270. return
  271. }
  272. // If parent menu of this menu item is bar menu
  273. if m.mitem != nil && m.mitem.menu.bar {
  274. sel := m.mitem.menu.selectedPos()
  275. next := m.mitem.menu.nextItem(sel)
  276. m.mitem.menu.setSelectedPos(next)
  277. m.root.SetKeyFocus(m.mitem.menu)
  278. }
  279. // Enter -> Select menu option
  280. case window.KeyEnter:
  281. rm := mi.rootMenu()
  282. if rm.bar {
  283. rm.autoOpen = false
  284. rm.setSelectedPos(-1)
  285. }
  286. mi.dispatchAll(OnClick, mi)
  287. default:
  288. return
  289. }
  290. }
  291. // onMouse process subscribed mouse events for the menu
  292. func (m *Menu) onMouse(evname string, ev interface{}) {
  293. log.Error("Menu onMouse:%s", evname)
  294. //if evname == OnMouseOut {
  295. // if m.bar {
  296. // m.autoOpen = false
  297. // m.setSelectedPos(-1)
  298. // }
  299. //}
  300. }
  301. // setSelectedPos sets the menu item at the specified position as selected
  302. // and all others as not selected.
  303. func (m *Menu) setSelectedPos(pos int) {
  304. for i := 0; i < len(m.items); i++ {
  305. mi := m.items[i]
  306. if i == pos {
  307. mi.selected = true
  308. } else {
  309. mi.selected = false
  310. }
  311. // If menu item has a sub menu, unselects the sub menu options recursively
  312. if mi.submenu != nil {
  313. mi.submenu.setSelectedPos(-1)
  314. }
  315. mi.update()
  316. }
  317. }
  318. // setSelectedItem sets the specified menu item as selected
  319. // and all others as not selected
  320. func (m *Menu) setSelectedItem(mitem *MenuItem) {
  321. for i := 0; i < len(m.items); i++ {
  322. mi := m.items[i]
  323. if mi == mitem {
  324. mi.selected = true
  325. } else {
  326. mi.selected = false
  327. }
  328. // If menu item has a sub menu, unselects the sub menu options recursively
  329. if mi.submenu != nil {
  330. mi.submenu.setSelectedItem(nil)
  331. }
  332. mi.update()
  333. }
  334. }
  335. // selectedPos returns the position of the current selected menu item
  336. // Returns -1 if no item selected
  337. func (m *Menu) selectedPos() int {
  338. for i := 0; i < len(m.items); i++ {
  339. mi := m.items[i]
  340. if mi.selected {
  341. return i
  342. }
  343. }
  344. return -1
  345. }
  346. // nextItem returns the position of the next enabled option from the
  347. // specified position
  348. func (m *Menu) nextItem(pos int) int {
  349. res := 0
  350. for i := pos + 1; i < len(m.items); i++ {
  351. mi := m.items[i]
  352. if mi.disabled || mi.label == nil {
  353. continue
  354. }
  355. res = i
  356. break
  357. }
  358. return res
  359. }
  360. // prevItem returns the position of previous enabled menu item from
  361. // the specified position
  362. func (m *Menu) prevItem(pos int) int {
  363. res := len(m.items) - 1
  364. for i := pos - 1; i >= 0 && i < len(m.items); i-- {
  365. mi := m.items[i]
  366. if mi.disabled || mi.label == nil {
  367. continue
  368. }
  369. res = i
  370. break
  371. }
  372. return res
  373. }
  374. // update updates the menu visual state
  375. func (m *Menu) update() {
  376. m.applyStyle(&m.styles.Body.Normal)
  377. }
  378. // applyStyle applies the specified menu body style
  379. func (m *Menu) applyStyle(mbs *MenuBodyStyle) {
  380. m.SetBordersFrom(&mbs.Border)
  381. m.SetBordersColor4(&mbs.BorderColor)
  382. m.SetPaddingsFrom(&mbs.Paddings)
  383. m.SetColor(&mbs.BgColor)
  384. }
  385. // recalc recalculates the positions of this menu internal items
  386. // and the content width and height of the menu
  387. func (m *Menu) recalc() {
  388. if m.bar {
  389. m.recalcBar()
  390. return
  391. }
  392. // Find the maximum icon and label widths
  393. minWidth := float32(0)
  394. iconWidth := float32(0)
  395. labelWidth := float32(0)
  396. shortcutWidth := float32(0)
  397. riconWidth := float32(0)
  398. for i := 0; i < len(m.items); i++ {
  399. mi := m.items[i]
  400. minWidth = mi.MinWidth()
  401. // Separator
  402. if mi.label == nil {
  403. continue
  404. }
  405. // Left icon width
  406. if mi.licon != nil && mi.licon.width > iconWidth {
  407. iconWidth = mi.licon.width
  408. }
  409. // Option label width
  410. if mi.label.width > labelWidth {
  411. labelWidth = mi.label.width
  412. }
  413. // Shortcut label width
  414. if mi.shortcut != nil && mi.shortcut.width > shortcutWidth {
  415. shortcutWidth = mi.shortcut.width
  416. }
  417. // Right icon (submenu indicator) width
  418. if mi.ricon != nil && mi.ricon.width > riconWidth {
  419. riconWidth = mi.ricon.width
  420. }
  421. }
  422. width := minWidth + iconWidth + labelWidth + shortcutWidth + riconWidth
  423. // Sets the position and width of the menu items
  424. // The height is defined by the menu item itself
  425. px := float32(0)
  426. py := float32(0)
  427. for i := 0; i < len(m.items); i++ {
  428. mi := m.items[i]
  429. mi.SetPosition(px, py)
  430. mh := mi.minHeight()
  431. py += mh
  432. mi.SetSize(width, mh)
  433. mi.recalc(iconWidth, labelWidth, shortcutWidth)
  434. }
  435. m.SetContentSize(width, py)
  436. }
  437. // recalcBar recalculates the positions of this MenuBar internal items
  438. // and the content width and height of the menu
  439. func (m *Menu) recalcBar() {
  440. height := float32(0)
  441. for i := 0; i < len(m.items); i++ {
  442. mi := m.items[i]
  443. if mi.minHeight() > height {
  444. height = mi.minHeight()
  445. }
  446. }
  447. px := float32(0)
  448. for i := 0; i < len(m.items); i++ {
  449. mi := m.items[i]
  450. mi.SetPosition(px, 0)
  451. width := float32(0)
  452. width = mi.minWidth()
  453. mi.SetSize(width, height)
  454. px += mi.Width()
  455. }
  456. m.SetContentSize(px, height)
  457. }
  458. // newMenuItem creates and returns a pointer to a new menu item
  459. // with the specified text.
  460. func newMenuItem(text string, styles *MenuItemStyles) *MenuItem {
  461. mi := new(MenuItem)
  462. mi.Panel.Initialize(0, 0)
  463. mi.styles = styles
  464. if text != "" {
  465. mi.label = NewLabel(text)
  466. mi.Panel.Add(mi.label)
  467. mi.Panel.Subscribe(OnCursorEnter, mi.onCursor)
  468. mi.Panel.Subscribe(OnCursorLeave, mi.onCursor)
  469. mi.Panel.Subscribe(OnMouseDown, mi.onMouse)
  470. }
  471. mi.update()
  472. return mi
  473. }
  474. // SetIcon sets the left icon of this menu item
  475. // If an image was previously set it is replaced by this icon
  476. func (mi *MenuItem) SetIcon(icode int) *MenuItem {
  477. mi.licon = NewIconLabel(string(icode))
  478. mi.Panel.Add(mi.licon)
  479. mi.update()
  480. return mi
  481. }
  482. // SetImage sets the left image of this menu item
  483. // If an icon was previously set it is replaced by this image
  484. func (mi *MenuItem) SetImage(img *Image) {
  485. }
  486. // SetText sets the text of this menu item
  487. func (mi *MenuItem) SetText(text string) *MenuItem {
  488. if mi.label == nil {
  489. return mi
  490. }
  491. mi.label.SetText(text)
  492. mi.update()
  493. mi.menu.recalc()
  494. return mi
  495. }
  496. // SetShortcut sets the keyboard shortcut of this menu item
  497. func (mi *MenuItem) SetShortcut(mod window.ModifierKey, key window.Key) *MenuItem {
  498. if mapKeyText[key] == "" {
  499. panic("Invalid menu shortcut key")
  500. }
  501. mi.keyModifier = mod
  502. mi.keyCode = key
  503. text := ""
  504. if mi.keyModifier&window.ModShift != 0 {
  505. text = mapKeyModifier[window.ModShift]
  506. }
  507. if mi.keyModifier&window.ModControl != 0 {
  508. if text != "" {
  509. text += "+"
  510. }
  511. text += mapKeyModifier[window.ModControl]
  512. }
  513. if mi.keyModifier&window.ModAlt != 0 {
  514. if text != "" {
  515. text += "+"
  516. }
  517. text += mapKeyModifier[window.ModAlt]
  518. }
  519. if text != "" {
  520. text += "+"
  521. }
  522. text += mapKeyText[key]
  523. mi.shortcut = NewLabel(text)
  524. mi.Panel.Add(mi.shortcut)
  525. mi.update()
  526. mi.menu.recalc()
  527. return mi
  528. }
  529. // SetSubmenu sets an associated sub menu item for this menu item
  530. func (mi *MenuItem) SetSubmenu(smi *MenuItem) *MenuItem {
  531. return mi
  532. }
  533. // SetEnabled sets the enabled state of this menu item
  534. func (mi *MenuItem) SetEnabled(enabled bool) *MenuItem {
  535. mi.disabled = !enabled
  536. mi.update()
  537. return mi
  538. }
  539. // SetId sets this menu item string id which can be used to identify
  540. // the selected menu option.
  541. func (mi *MenuItem) SetId(id string) *MenuItem {
  542. mi.id = id
  543. return mi
  544. }
  545. // Id returns this menu item current id
  546. func (mi *MenuItem) Id() string {
  547. return mi.id
  548. }
  549. // IdPath returns a slice with the path of menu items ids to this menu item
  550. func (mi *MenuItem) IdPath() []string {
  551. // Builds lists of menu items ids
  552. path := []string{mi.id}
  553. menu := mi.menu
  554. for menu.mitem != nil {
  555. path = append(path, menu.mitem.id)
  556. menu = menu.mitem.menu
  557. }
  558. // Reverse and returns id list
  559. res := make([]string, 0, len(path))
  560. for i := len(path) - 1; i >= 0; i-- {
  561. res = append(res, path[i])
  562. }
  563. return res
  564. }
  565. // onCursor processes subscribed cursor events over the menu item
  566. func (mi *MenuItem) onCursor(evname string, ev interface{}) {
  567. switch evname {
  568. case OnCursorEnter:
  569. mi.menu.setSelectedItem(mi)
  570. case OnCursorLeave:
  571. }
  572. }
  573. // onMouse processes subscribed mouse events over the menu item
  574. func (mi *MenuItem) onMouse(evname string, ev interface{}) {
  575. switch evname {
  576. case OnMouseDown:
  577. // MenuBar option
  578. if mi.menu.bar {
  579. mi.menu.autoOpen = !mi.menu.autoOpen
  580. if mi.submenu != nil && mi.submenu.Visible() {
  581. mi.submenu.SetVisible(false)
  582. mi.root.SetKeyFocus(mi.menu)
  583. } else {
  584. mi.update()
  585. }
  586. }
  587. if mi.submenu != nil {
  588. return
  589. }
  590. rm := mi.rootMenu()
  591. rm.setSelectedPos(-1)
  592. mi.root.SetKeyFocus(rm)
  593. mi.dispatchAll(OnClick, mi)
  594. }
  595. }
  596. // rootMenu returns the root menu for this menu item
  597. func (mi *MenuItem) rootMenu() *Menu {
  598. root := mi.menu
  599. for root.mitem != nil {
  600. root = root.mitem.menu
  601. }
  602. return root
  603. }
  604. // dispatchAll dispatch the specified event for this menu item
  605. // and all its parents.to all parents
  606. func (mi *MenuItem) dispatchAll(evname string, ev interface{}) {
  607. mi.Dispatch(evname, ev)
  608. pmenu := mi.menu
  609. for {
  610. pmenu.Dispatch(evname, ev)
  611. if pmenu.mitem == nil {
  612. break
  613. }
  614. pmenu = pmenu.mitem.menu
  615. }
  616. }
  617. // update updates the menu item visual state
  618. func (mi *MenuItem) update() {
  619. // Separator
  620. if mi.label == nil {
  621. mi.applyStyle(&mi.styles.Separator)
  622. return
  623. }
  624. // Disabled item
  625. if mi.disabled {
  626. mi.applyStyle(&mi.styles.Disabled)
  627. return
  628. }
  629. // Selected item
  630. if mi.selected {
  631. mi.applyStyle(&mi.styles.Over)
  632. if mi.submenu != nil && mi.menu.autoOpen {
  633. mi.menu.SetTopChild(mi)
  634. mi.submenu.SetVisible(true)
  635. if mi.menu != nil && mi.menu.bar {
  636. mi.submenu.SetPosition(0, mi.Height()-2)
  637. } else {
  638. mi.submenu.SetPosition(mi.Width()-2, 0)
  639. }
  640. }
  641. return
  642. }
  643. // If this menu item has a sub menu and the sub menu is not active,
  644. // hides the sub menu
  645. if mi.submenu != nil {
  646. mi.submenu.SetVisible(false)
  647. }
  648. mi.applyStyle(&mi.styles.Normal)
  649. }
  650. // applyStyle applies the specified menu item style
  651. func (mi *MenuItem) applyStyle(mis *MenuItemStyle) {
  652. mi.SetBordersFrom(&mis.Border)
  653. mi.SetBordersColor4(&mis.BorderColor)
  654. mi.SetPaddingsFrom(&mis.Paddings)
  655. mi.SetColor(&mis.BgColor)
  656. if mi.licon != nil {
  657. mi.licon.SetPaddingsFrom(&mis.IconPaddings)
  658. }
  659. if mi.label != nil {
  660. mi.label.SetColor(&mis.FgColor)
  661. }
  662. if mi.shortcut != nil {
  663. mi.shortcut.SetPaddingsFrom(&mis.ShortcutPaddings)
  664. }
  665. if mi.ricon != nil {
  666. mi.ricon.SetPaddingsFrom(&mis.RiconPaddings)
  667. }
  668. }
  669. // recalc recalculates the positions of this menu item internal panels
  670. func (mi *MenuItem) recalc(iconWidth, labelWidth, shortcutWidth float32) {
  671. // Separator
  672. if mi.label == nil {
  673. return
  674. }
  675. if mi.licon != nil {
  676. py := (mi.label.height - mi.licon.height) / 2
  677. mi.licon.SetPosition(0, py)
  678. }
  679. mi.label.SetPosition(iconWidth, 0)
  680. if mi.shortcut != nil {
  681. mi.shortcut.SetPosition(iconWidth+labelWidth, 0)
  682. }
  683. if mi.ricon != nil {
  684. mi.ricon.SetPosition(iconWidth+labelWidth+shortcutWidth, 0)
  685. }
  686. }
  687. // minHeight returns the minimum height of this menu item
  688. func (mi *MenuItem) minHeight() float32 {
  689. mh := mi.MinHeight()
  690. if mi.label == nil {
  691. return mh + 1
  692. }
  693. mh += mi.label.height
  694. return mh
  695. }
  696. // minWidth returns the minimum width of this menu item
  697. func (mi *MenuItem) minWidth() float32 {
  698. mw := mi.MinWidth()
  699. if mi.label == nil {
  700. return mw + 1
  701. }
  702. mw += mi.label.width
  703. return mw
  704. }