Widgets.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. package giu
  2. import (
  3. "fmt"
  4. "image/color"
  5. "github.com/AllenDang/imgui-go"
  6. )
  7. // GenAutoID automatically generates fidget's id.
  8. func GenAutoID(id string) string {
  9. return fmt.Sprintf("%s##%d", id, Context.GetWidgetIndex())
  10. }
  11. var _ Widget = &RowWidget{}
  12. // RowWidget joins a layout into one line
  13. // calls imgui.SameLine().
  14. type RowWidget struct {
  15. widgets Layout
  16. }
  17. // Row creates RowWidget.
  18. func Row(widgets ...Widget) *RowWidget {
  19. return &RowWidget{
  20. widgets: widgets,
  21. }
  22. }
  23. // Build implements Widget interface.
  24. func (l *RowWidget) Build() {
  25. isFirst := true
  26. l.widgets.Range(func(w Widget) {
  27. switch w.(type) {
  28. case *TooltipWidget,
  29. *ContextMenuWidget, *PopupModalWidget,
  30. *PopupWidget:
  31. // noop
  32. default:
  33. if _, isLabel := w.(*LabelWidget); isLabel {
  34. AlignTextToFramePadding()
  35. }
  36. if !isFirst {
  37. imgui.SameLine()
  38. } else {
  39. isFirst = false
  40. }
  41. }
  42. w.Build()
  43. })
  44. }
  45. // SameLine wrapps imgui.SomeLine
  46. // Don't use if you don't have to (use RowWidget instead).
  47. func SameLine() {
  48. imgui.SameLine()
  49. }
  50. var _ Widget = &ChildWidget{}
  51. type ChildWidget struct {
  52. id string
  53. width float32
  54. height float32
  55. border bool
  56. flags WindowFlags
  57. layout Layout
  58. }
  59. // Build implements Widget interface.
  60. func (c *ChildWidget) Build() {
  61. if imgui.BeginChildV(c.id, imgui.Vec2{X: c.width, Y: c.height}, c.border, int(c.flags)) {
  62. c.layout.Build()
  63. }
  64. imgui.EndChild()
  65. }
  66. func (c *ChildWidget) Border(border bool) *ChildWidget {
  67. c.border = border
  68. return c
  69. }
  70. func (c *ChildWidget) Size(width, height float32) *ChildWidget {
  71. c.width, c.height = width, height
  72. return c
  73. }
  74. func (c *ChildWidget) Flags(flags WindowFlags) *ChildWidget {
  75. c.flags = flags
  76. return c
  77. }
  78. func (c *ChildWidget) Layout(widgets ...Widget) *ChildWidget {
  79. c.layout = Layout(widgets)
  80. return c
  81. }
  82. // ID sets the interval id of child widgets.
  83. func (c *ChildWidget) ID(id string) *ChildWidget {
  84. c.id = id
  85. return c
  86. }
  87. func Child() *ChildWidget {
  88. return &ChildWidget{
  89. id: GenAutoID("Child"),
  90. width: 0,
  91. height: 0,
  92. border: true,
  93. flags: 0,
  94. layout: nil,
  95. }
  96. }
  97. var _ Widget = &ComboCustomWidget{}
  98. // ComboCustomWidget represents a combo with custom layout when opened.
  99. type ComboCustomWidget struct {
  100. label string
  101. previewValue string
  102. width float32
  103. flags ComboFlags
  104. layout Layout
  105. }
  106. // ComboCustom creates a new combo custom widget.
  107. func ComboCustom(label, previewValue string) *ComboCustomWidget {
  108. return &ComboCustomWidget{
  109. label: GenAutoID(label),
  110. previewValue: tStr(previewValue),
  111. width: 0,
  112. flags: 0,
  113. layout: nil,
  114. }
  115. }
  116. // Layout add combo's layout.
  117. func (cc *ComboCustomWidget) Layout(widgets ...Widget) *ComboCustomWidget {
  118. cc.layout = Layout(widgets)
  119. return cc
  120. }
  121. // Flags allows to set combo flags (see Flags.go).
  122. func (cc *ComboCustomWidget) Flags(flags ComboFlags) *ComboCustomWidget {
  123. cc.flags = flags
  124. return cc
  125. }
  126. // Size sets combo preiview width.
  127. func (cc *ComboCustomWidget) Size(width float32) *ComboCustomWidget {
  128. cc.width = width
  129. return cc
  130. }
  131. // Build implements Widget interface.
  132. func (cc *ComboCustomWidget) Build() {
  133. if cc.width > 0 {
  134. imgui.PushItemWidth(cc.width)
  135. defer imgui.PopItemWidth()
  136. }
  137. if imgui.BeginComboV(tStr(cc.label), cc.previewValue, int(cc.flags)) {
  138. cc.layout.Build()
  139. imgui.EndCombo()
  140. }
  141. }
  142. var _ Widget = &ComboWidget{}
  143. // ComboWidget is a wrapper of ComboCustomWidget.
  144. // It creates a combo of selectables. (it is the most frequently used).
  145. type ComboWidget struct {
  146. label string
  147. previewValue string
  148. items []string
  149. selected *int32
  150. width float32
  151. flags ComboFlags
  152. onChange func()
  153. }
  154. // Combo creates a new ComboWidget.
  155. func Combo(label, previewValue string, items []string, selected *int32) *ComboWidget {
  156. return &ComboWidget{
  157. label: GenAutoID(label),
  158. previewValue: tStr(previewValue),
  159. items: tStrSlice(items),
  160. selected: selected,
  161. flags: 0,
  162. width: 0,
  163. onChange: nil,
  164. }
  165. }
  166. // Build implements Widget interface.
  167. func (c *ComboWidget) Build() {
  168. if c.width > 0 {
  169. imgui.PushItemWidth(c.width)
  170. defer imgui.PopItemWidth()
  171. }
  172. if imgui.BeginComboV(tStr(c.label), c.previewValue, int(c.flags)) {
  173. for i, item := range c.items {
  174. if imgui.Selectable(item) {
  175. *c.selected = int32(i)
  176. if c.onChange != nil {
  177. c.onChange()
  178. }
  179. }
  180. }
  181. imgui.EndCombo()
  182. }
  183. }
  184. // Flags allows to set combo flags (see Flags.go).
  185. func (c *ComboWidget) Flags(flags ComboFlags) *ComboWidget {
  186. c.flags = flags
  187. return c
  188. }
  189. // Size sets combo's width.
  190. func (c *ComboWidget) Size(width float32) *ComboWidget {
  191. c.width = width
  192. return c
  193. }
  194. // OnChange sets callback when combo value gets changed.
  195. func (c *ComboWidget) OnChange(onChange func()) *ComboWidget {
  196. c.onChange = onChange
  197. return c
  198. }
  199. var _ Widget = &ContextMenuWidget{}
  200. type ContextMenuWidget struct {
  201. id string
  202. mouseButton MouseButton
  203. layout Layout
  204. }
  205. func ContextMenu() *ContextMenuWidget {
  206. return &ContextMenuWidget{
  207. mouseButton: MouseButtonRight,
  208. layout: nil,
  209. id: GenAutoID("ContextMenu"),
  210. }
  211. }
  212. func (c *ContextMenuWidget) Layout(widgets ...Widget) *ContextMenuWidget {
  213. c.layout = Layout(widgets)
  214. return c
  215. }
  216. func (c *ContextMenuWidget) MouseButton(mouseButton MouseButton) *ContextMenuWidget {
  217. c.mouseButton = mouseButton
  218. return c
  219. }
  220. func (c *ContextMenuWidget) ID(id string) *ContextMenuWidget {
  221. c.id = id
  222. return c
  223. }
  224. // Build implements Widget interface.
  225. func (c *ContextMenuWidget) Build() {
  226. if imgui.BeginPopupContextItemV(c.id, int(c.mouseButton)) {
  227. c.layout.Build()
  228. imgui.EndPopup()
  229. }
  230. }
  231. var _ Widget = &DragIntWidget{}
  232. type DragIntWidget struct {
  233. label string
  234. value *int32
  235. speed float32
  236. min int32
  237. max int32
  238. format string
  239. }
  240. func DragInt(label string, value *int32, min, max int32) *DragIntWidget {
  241. return &DragIntWidget{
  242. label: GenAutoID(label),
  243. value: value,
  244. speed: 1.0,
  245. min: min,
  246. max: max,
  247. format: "%d",
  248. }
  249. }
  250. func (d *DragIntWidget) Speed(speed float32) *DragIntWidget {
  251. d.speed = speed
  252. return d
  253. }
  254. func (d *DragIntWidget) Format(format string) *DragIntWidget {
  255. d.format = format
  256. return d
  257. }
  258. // Build implements Widget interface.
  259. func (d *DragIntWidget) Build() {
  260. imgui.DragIntV(tStr(d.label), d.value, d.speed, d.min, d.max, d.format)
  261. }
  262. var _ Widget = &ColumnWidget{}
  263. // ColumnWidget will place all widgets one by one vertically.
  264. type ColumnWidget struct {
  265. widgets Layout
  266. }
  267. // Column creates a new ColumnWidget.
  268. func Column(widgets ...Widget) *ColumnWidget {
  269. return &ColumnWidget{
  270. widgets: widgets,
  271. }
  272. }
  273. // Build implements Widget interface.
  274. func (g *ColumnWidget) Build() {
  275. imgui.BeginGroup()
  276. g.widgets.Build()
  277. imgui.EndGroup()
  278. }
  279. var _ Widget = &MainMenuBarWidget{}
  280. type MainMenuBarWidget struct {
  281. layout Layout
  282. }
  283. func MainMenuBar() *MainMenuBarWidget {
  284. return &MainMenuBarWidget{
  285. layout: nil,
  286. }
  287. }
  288. func (m *MainMenuBarWidget) Layout(widgets ...Widget) *MainMenuBarWidget {
  289. m.layout = Layout(widgets)
  290. return m
  291. }
  292. // Build implements Widget interface.
  293. func (m *MainMenuBarWidget) Build() {
  294. if imgui.BeginMainMenuBar() {
  295. m.layout.Build()
  296. imgui.EndMainMenuBar()
  297. }
  298. }
  299. var _ Widget = &MenuBarWidget{}
  300. type MenuBarWidget struct {
  301. layout Layout
  302. }
  303. func MenuBar() *MenuBarWidget {
  304. return &MenuBarWidget{
  305. layout: nil,
  306. }
  307. }
  308. func (m *MenuBarWidget) Layout(widgets ...Widget) *MenuBarWidget {
  309. m.layout = Layout(widgets)
  310. return m
  311. }
  312. // Build implements Widget interface.
  313. func (m *MenuBarWidget) Build() {
  314. if imgui.BeginMenuBar() {
  315. m.layout.Build()
  316. imgui.EndMenuBar()
  317. }
  318. }
  319. var _ Widget = &MenuItemWidget{}
  320. type MenuItemWidget struct {
  321. label string
  322. selected bool
  323. enabled bool
  324. onClick func()
  325. }
  326. func MenuItem(label string) *MenuItemWidget {
  327. return &MenuItemWidget{
  328. label: GenAutoID(label),
  329. selected: false,
  330. enabled: true,
  331. onClick: nil,
  332. }
  333. }
  334. func MenuItemf(format string, args ...any) *MenuItemWidget {
  335. return MenuItem(fmt.Sprintf(format, args...))
  336. }
  337. func (m *MenuItemWidget) Selected(s bool) *MenuItemWidget {
  338. m.selected = s
  339. return m
  340. }
  341. func (m *MenuItemWidget) Enabled(e bool) *MenuItemWidget {
  342. m.enabled = e
  343. return m
  344. }
  345. func (m *MenuItemWidget) OnClick(onClick func()) *MenuItemWidget {
  346. m.onClick = onClick
  347. return m
  348. }
  349. // Build implements Widget interface.
  350. func (m *MenuItemWidget) Build() {
  351. if imgui.MenuItemV(tStr(m.label), "", m.selected, m.enabled) && m.onClick != nil {
  352. m.onClick()
  353. }
  354. }
  355. var _ Widget = &MenuWidget{}
  356. type MenuWidget struct {
  357. label string
  358. enabled bool
  359. layout Layout
  360. }
  361. func Menu(label string) *MenuWidget {
  362. return &MenuWidget{
  363. label: GenAutoID(label),
  364. enabled: true,
  365. layout: nil,
  366. }
  367. }
  368. func Menuf(format string, args ...any) *MenuWidget {
  369. return Menu(fmt.Sprintf(format, args...))
  370. }
  371. func (m *MenuWidget) Enabled(e bool) *MenuWidget {
  372. m.enabled = e
  373. return m
  374. }
  375. func (m *MenuWidget) Layout(widgets ...Widget) *MenuWidget {
  376. m.layout = Layout(widgets)
  377. return m
  378. }
  379. // Build implements Widget interface.
  380. func (m *MenuWidget) Build() {
  381. if imgui.BeginMenuV(tStr(m.label), m.enabled) {
  382. m.layout.Build()
  383. imgui.EndMenu()
  384. }
  385. }
  386. var _ Widget = &ProgressBarWidget{}
  387. type ProgressBarWidget struct {
  388. fraction float32
  389. width float32
  390. height float32
  391. overlay string
  392. }
  393. func ProgressBar(fraction float32) *ProgressBarWidget {
  394. return &ProgressBarWidget{
  395. fraction: fraction,
  396. width: 0,
  397. height: 0,
  398. overlay: "",
  399. }
  400. }
  401. func (p *ProgressBarWidget) Size(width, height float32) *ProgressBarWidget {
  402. p.width, p.height = width, height
  403. return p
  404. }
  405. func (p *ProgressBarWidget) Overlay(overlay string) *ProgressBarWidget {
  406. p.overlay = tStr(overlay)
  407. return p
  408. }
  409. func (p *ProgressBarWidget) Overlayf(format string, args ...any) *ProgressBarWidget {
  410. return p.Overlay(fmt.Sprintf(format, args...))
  411. }
  412. // Build implements Widget interface.
  413. func (p *ProgressBarWidget) Build() {
  414. imgui.ProgressBarV(p.fraction, imgui.Vec2{X: p.width, Y: p.height}, p.overlay)
  415. }
  416. var _ Widget = &SeparatorWidget{}
  417. type SeparatorWidget struct{}
  418. // Build implements Widget interface.
  419. func (s *SeparatorWidget) Build() {
  420. imgui.Separator()
  421. }
  422. func Separator() *SeparatorWidget {
  423. return &SeparatorWidget{}
  424. }
  425. var _ Widget = &DummyWidget{}
  426. type DummyWidget struct {
  427. width float32
  428. height float32
  429. }
  430. // Build implements Widget interface.
  431. func (d *DummyWidget) Build() {
  432. w, h := GetAvailableRegion()
  433. if d.width < 0 {
  434. d.width = w + d.width
  435. }
  436. if d.height < 0 {
  437. d.height = h + d.height
  438. }
  439. imgui.Dummy(imgui.Vec2{X: d.width, Y: d.height})
  440. }
  441. func Dummy(width, height float32) *DummyWidget {
  442. return &DummyWidget{
  443. width: width,
  444. height: height,
  445. }
  446. }
  447. type TabItemWidget struct {
  448. label string
  449. open *bool
  450. flags TabItemFlags
  451. layout Layout
  452. }
  453. func TabItem(label string) *TabItemWidget {
  454. return &TabItemWidget{
  455. label: tStr(label),
  456. open: nil,
  457. flags: 0,
  458. layout: nil,
  459. }
  460. }
  461. func TabItemf(format string, args ...any) *TabItemWidget {
  462. return TabItem(fmt.Sprintf(format, args...))
  463. }
  464. func (t *TabItemWidget) IsOpen(open *bool) *TabItemWidget {
  465. t.open = open
  466. return t
  467. }
  468. func (t *TabItemWidget) Flags(flags TabItemFlags) *TabItemWidget {
  469. t.flags = flags
  470. return t
  471. }
  472. func (t *TabItemWidget) Layout(widgets ...Widget) *TabItemWidget {
  473. t.layout = Layout(widgets)
  474. return t
  475. }
  476. // BuildTabItem executes tab item build steps.
  477. func (t *TabItemWidget) BuildTabItem() {
  478. if imgui.BeginTabItemV(t.label, t.open, int(t.flags)) {
  479. t.layout.Build()
  480. imgui.EndTabItem()
  481. }
  482. }
  483. var _ Widget = &TabBarWidget{}
  484. type TabBarWidget struct {
  485. id string
  486. flags TabBarFlags
  487. tabItems []*TabItemWidget
  488. }
  489. func TabBar() *TabBarWidget {
  490. return &TabBarWidget{
  491. id: GenAutoID("TabBar"),
  492. flags: 0,
  493. }
  494. }
  495. func (t *TabBarWidget) Flags(flags TabBarFlags) *TabBarWidget {
  496. t.flags = flags
  497. return t
  498. }
  499. func (t *TabBarWidget) ID(id string) *TabBarWidget {
  500. t.id = id
  501. return t
  502. }
  503. func (t *TabBarWidget) TabItems(items ...*TabItemWidget) *TabBarWidget {
  504. t.tabItems = items
  505. return t
  506. }
  507. // Build implements Widget interface.
  508. func (t *TabBarWidget) Build() {
  509. if imgui.BeginTabBarV(t.id, int(t.flags)) {
  510. for _, ti := range t.tabItems {
  511. ti.BuildTabItem()
  512. }
  513. imgui.EndTabBar()
  514. }
  515. }
  516. var _ Widget = &TooltipWidget{}
  517. type TooltipWidget struct {
  518. tip string
  519. layout Layout
  520. }
  521. // Build implements Widget interface.
  522. func (t *TooltipWidget) Build() {
  523. if imgui.IsItemHovered() {
  524. if t.layout != nil {
  525. imgui.BeginTooltip()
  526. t.layout.Build()
  527. imgui.EndTooltip()
  528. } else {
  529. imgui.SetTooltip(t.tip)
  530. }
  531. }
  532. }
  533. func Tooltip(tip string) *TooltipWidget {
  534. return &TooltipWidget{
  535. tip: tStr(tip),
  536. layout: nil,
  537. }
  538. }
  539. func Tooltipf(format string, args ...any) *TooltipWidget {
  540. return Tooltip(fmt.Sprintf(format, args...))
  541. }
  542. func (t *TooltipWidget) Layout(widgets ...Widget) *TooltipWidget {
  543. t.layout = Layout(widgets)
  544. return t
  545. }
  546. var _ Widget = &SpacingWidget{}
  547. type SpacingWidget struct{}
  548. // Build implements Widget interface.
  549. func (s *SpacingWidget) Build() {
  550. imgui.Spacing()
  551. }
  552. func Spacing() *SpacingWidget {
  553. return &SpacingWidget{}
  554. }
  555. var _ Widget = &ColorEditWidget{}
  556. type ColorEditWidget struct {
  557. label string
  558. color *color.RGBA
  559. flags ColorEditFlags
  560. width float32
  561. onChange func()
  562. }
  563. func ColorEdit(label string, c *color.RGBA) *ColorEditWidget {
  564. return &ColorEditWidget{
  565. label: GenAutoID(label),
  566. color: c,
  567. flags: ColorEditFlagsNone,
  568. }
  569. }
  570. func (ce *ColorEditWidget) OnChange(cb func()) *ColorEditWidget {
  571. ce.onChange = cb
  572. return ce
  573. }
  574. func (ce *ColorEditWidget) Flags(f ColorEditFlags) *ColorEditWidget {
  575. ce.flags = f
  576. return ce
  577. }
  578. func (ce *ColorEditWidget) Size(width float32) *ColorEditWidget {
  579. ce.width = width
  580. return ce
  581. }
  582. // Build implements Widget interface.
  583. func (ce *ColorEditWidget) Build() {
  584. c := ToVec4Color(*ce.color)
  585. col := [4]float32{
  586. c.X,
  587. c.Y,
  588. c.Z,
  589. c.W,
  590. }
  591. if ce.width > 0 {
  592. imgui.PushItemWidth(ce.width)
  593. }
  594. if imgui.ColorEdit4V(
  595. tStr(ce.label),
  596. &col,
  597. int(ce.flags),
  598. ) {
  599. *ce.color = Vec4ToRGBA(imgui.Vec4{
  600. X: col[0],
  601. Y: col[1],
  602. Z: col[2],
  603. W: col[3],
  604. })
  605. if ce.onChange != nil {
  606. ce.onChange()
  607. }
  608. }
  609. if ce.width > 0 {
  610. imgui.PopItemWidth()
  611. }
  612. }