ClickableWidgets.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. package giu
  2. import (
  3. "fmt"
  4. "image"
  5. "image/color"
  6. "github.com/AllenDang/imgui-go"
  7. "golang.org/x/image/colornames"
  8. )
  9. var _ Widget = &ButtonWidget{}
  10. // ButtonWidget represents a ImGui button widget.
  11. type ButtonWidget struct {
  12. id string
  13. width float32
  14. height float32
  15. disabled bool
  16. onClick func()
  17. }
  18. // Button creates a new button widget.
  19. func Button(label string) *ButtonWidget {
  20. return &ButtonWidget{
  21. id: GenAutoID(label),
  22. width: 0,
  23. height: 0,
  24. onClick: nil,
  25. }
  26. }
  27. // Buttonf creates button with formated label
  28. // NOTE: works like fmt.Sprintf (see `go doc fmt`).
  29. func Buttonf(format string, args ...any) *ButtonWidget {
  30. return Button(fmt.Sprintf(format, args...))
  31. }
  32. // OnClick sets callback called when button is clicked
  33. // NOTE: to set double click, see EventHandler.go.
  34. func (b *ButtonWidget) OnClick(onClick func()) *ButtonWidget {
  35. b.onClick = onClick
  36. return b
  37. }
  38. // Disabled sets button's disabled state
  39. // NOTE: same effect as Style().SetDisabled.
  40. func (b *ButtonWidget) Disabled(d bool) *ButtonWidget {
  41. b.disabled = d
  42. return b
  43. }
  44. // Size sets button's size.
  45. func (b *ButtonWidget) Size(width, height float32) *ButtonWidget {
  46. b.width, b.height = width, height
  47. return b
  48. }
  49. // Build implements Widget interface.
  50. func (b *ButtonWidget) Build() {
  51. if b.disabled {
  52. imgui.BeginDisabled(true)
  53. defer imgui.EndDisabled()
  54. }
  55. if imgui.ButtonV(tStr(b.id), imgui.Vec2{X: b.width, Y: b.height}) && b.onClick != nil {
  56. b.onClick()
  57. }
  58. }
  59. var _ Widget = &ArrowButtonWidget{}
  60. // ArrowButtonWidget represents a square button with an arrow.
  61. type ArrowButtonWidget struct {
  62. id string
  63. dir Direction
  64. onClick func()
  65. }
  66. // ArrowButton creates ArrowButtonWidget.
  67. func ArrowButton(dir Direction) *ArrowButtonWidget {
  68. return &ArrowButtonWidget{
  69. id: GenAutoID("ArrowButton"),
  70. dir: dir,
  71. onClick: nil,
  72. }
  73. }
  74. // OnClick adds callback called when button is clicked.
  75. func (b *ArrowButtonWidget) OnClick(onClick func()) *ArrowButtonWidget {
  76. b.onClick = onClick
  77. return b
  78. }
  79. // ID allows to manually set widget's id.
  80. func (b *ArrowButtonWidget) ID(id string) *ArrowButtonWidget {
  81. b.id = id
  82. return b
  83. }
  84. // Build implements Widget interface.
  85. func (b *ArrowButtonWidget) Build() {
  86. if imgui.ArrowButton(b.id, uint8(b.dir)) && b.onClick != nil {
  87. b.onClick()
  88. }
  89. }
  90. var _ Widget = &SmallButtonWidget{}
  91. // SmallButtonWidget is like a button but without frame padding.
  92. type SmallButtonWidget struct {
  93. id string
  94. onClick func()
  95. }
  96. // SmallButton constructs a new small button widget.
  97. func SmallButton(id string) *SmallButtonWidget {
  98. return &SmallButtonWidget{
  99. id: GenAutoID(id),
  100. onClick: nil,
  101. }
  102. }
  103. // SmallButtonf allows to set formated label for small button.
  104. // It calls SmallButton(fmt.Sprintf(label, args...)).
  105. func SmallButtonf(format string, args ...any) *SmallButtonWidget {
  106. return SmallButton(fmt.Sprintf(format, args...))
  107. }
  108. // OnClick adds OnClick event.
  109. func (b *SmallButtonWidget) OnClick(onClick func()) *SmallButtonWidget {
  110. b.onClick = onClick
  111. return b
  112. }
  113. // Build implements Widget interface.
  114. func (b *SmallButtonWidget) Build() {
  115. if imgui.SmallButton(tStr(b.id)) && b.onClick != nil {
  116. b.onClick()
  117. }
  118. }
  119. var _ Widget = &InvisibleButtonWidget{}
  120. // InvisibleButtonWidget is a clickable region.
  121. // NOTE: you may want to display other widgets on this button.
  122. // to do so, you may move drawing cursor back by Get/SetCursor(Screen)Pos.
  123. type InvisibleButtonWidget struct {
  124. id string
  125. width float32
  126. height float32
  127. onClick func()
  128. }
  129. // InvisibleButton constructs a new invisible button widget.
  130. func InvisibleButton() *InvisibleButtonWidget {
  131. return &InvisibleButtonWidget{
  132. id: GenAutoID("InvisibleButton"),
  133. width: 0,
  134. height: 0,
  135. onClick: nil,
  136. }
  137. }
  138. // Size sets button's size.
  139. func (b *InvisibleButtonWidget) Size(width, height float32) *InvisibleButtonWidget {
  140. b.width, b.height = width, height
  141. return b
  142. }
  143. // OnClick sets click event.
  144. func (b *InvisibleButtonWidget) OnClick(onClick func()) *InvisibleButtonWidget {
  145. b.onClick = onClick
  146. return b
  147. }
  148. // ID allows to manually set widget's id (no need to use in normal conditions).
  149. func (b *InvisibleButtonWidget) ID(id string) *InvisibleButtonWidget {
  150. b.id = id
  151. return b
  152. }
  153. // Build implements Widget interface.
  154. func (b *InvisibleButtonWidget) Build() {
  155. if imgui.InvisibleButton(tStr(b.id), imgui.Vec2{X: b.width, Y: b.height}) && b.onClick != nil {
  156. b.onClick()
  157. }
  158. }
  159. var _ Widget = &ImageButtonWidget{}
  160. // ImageButtonWidget is similar to ButtonWidget but with image texture instead of text label.
  161. type ImageButtonWidget struct {
  162. texture *Texture
  163. width float32
  164. height float32
  165. uv0 image.Point
  166. uv1 image.Point
  167. framePadding int
  168. bgColor color.Color
  169. tintColor color.Color
  170. onClick func()
  171. }
  172. // ImageButton constructs image buton widget.
  173. func ImageButton(texture *Texture) *ImageButtonWidget {
  174. return &ImageButtonWidget{
  175. texture: texture,
  176. width: 50,
  177. height: 50,
  178. uv0: image.Point{X: 0, Y: 0},
  179. uv1: image.Point{X: 1, Y: 1},
  180. framePadding: -1,
  181. bgColor: colornames.Black,
  182. tintColor: colornames.White,
  183. onClick: nil,
  184. }
  185. }
  186. // Build implements Widget interface.
  187. func (b *ImageButtonWidget) Build() {
  188. if b.texture == nil || b.texture.id == 0 {
  189. return
  190. }
  191. if imgui.ImageButtonV(
  192. b.texture.id,
  193. imgui.Vec2{X: b.width, Y: b.height},
  194. ToVec2(b.uv0), ToVec2(b.uv1),
  195. b.framePadding, ToVec4Color(b.bgColor),
  196. ToVec4Color(b.tintColor),
  197. ) && b.onClick != nil {
  198. b.onClick()
  199. }
  200. }
  201. // Size sets BUTTONS size.
  202. // NOTE: image size is button size - 2 * frame padding.
  203. func (b *ImageButtonWidget) Size(width, height float32) *ImageButtonWidget {
  204. b.width, b.height = width, height
  205. return b
  206. }
  207. // OnClick sets click event.
  208. func (b *ImageButtonWidget) OnClick(onClick func()) *ImageButtonWidget {
  209. b.onClick = onClick
  210. return b
  211. }
  212. // UV sets image's uv.
  213. func (b *ImageButtonWidget) UV(uv0, uv1 image.Point) *ImageButtonWidget {
  214. b.uv0, b.uv1 = uv0, uv1
  215. return b
  216. }
  217. // BgColor sets button's background color.
  218. func (b *ImageButtonWidget) BgColor(bgColor color.Color) *ImageButtonWidget {
  219. b.bgColor = bgColor
  220. return b
  221. }
  222. // TintColor sets tit color for image.
  223. func (b *ImageButtonWidget) TintColor(tintColor color.Color) *ImageButtonWidget {
  224. b.tintColor = tintColor
  225. return b
  226. }
  227. // FramePadding sets button's frame padding (set 0 to fit image to the frame).
  228. func (b *ImageButtonWidget) FramePadding(padding int) *ImageButtonWidget {
  229. b.framePadding = padding
  230. return b
  231. }
  232. var _ Widget = &ImageButtonWithRgbaWidget{}
  233. // ImageButtonWithRgbaWidget does similar to ImageButtonWIdget,
  234. // but implements image.Image instead of giu.Texture. It is probably
  235. // more useful than the original ImageButtonWIdget.
  236. type ImageButtonWithRgbaWidget struct {
  237. *ImageButtonWidget
  238. rgba image.Image
  239. id string
  240. }
  241. // ImageButtonWithRgba creates a new widget.
  242. func ImageButtonWithRgba(rgba image.Image) *ImageButtonWithRgbaWidget {
  243. return &ImageButtonWithRgbaWidget{
  244. id: GenAutoID("ImageButtonWithRgba"),
  245. ImageButtonWidget: ImageButton(nil),
  246. rgba: rgba,
  247. }
  248. }
  249. // Size sets button's size.
  250. func (b *ImageButtonWithRgbaWidget) Size(width, height float32) *ImageButtonWithRgbaWidget {
  251. b.ImageButtonWidget.Size(width, height)
  252. return b
  253. }
  254. // OnClick sets click events.
  255. func (b *ImageButtonWithRgbaWidget) OnClick(onClick func()) *ImageButtonWithRgbaWidget {
  256. b.ImageButtonWidget.OnClick(onClick)
  257. return b
  258. }
  259. // UV sets image's uv color.
  260. func (b *ImageButtonWithRgbaWidget) UV(uv0, uv1 image.Point) *ImageButtonWithRgbaWidget {
  261. b.ImageButtonWidget.UV(uv0, uv1)
  262. return b
  263. }
  264. // BgColor sets button's background color.
  265. func (b *ImageButtonWithRgbaWidget) BgColor(bgColor color.Color) *ImageButtonWithRgbaWidget {
  266. b.ImageButtonWidget.BgColor(bgColor)
  267. return b
  268. }
  269. // TintColor sets image's tint color.
  270. func (b *ImageButtonWithRgbaWidget) TintColor(tintColor color.Color) *ImageButtonWithRgbaWidget {
  271. b.ImageButtonWidget.TintColor(tintColor)
  272. return b
  273. }
  274. // FramePadding sets frame padding (see (*ImageButtonWidget).TintColor).
  275. func (b *ImageButtonWithRgbaWidget) FramePadding(padding int) *ImageButtonWithRgbaWidget {
  276. b.ImageButtonWidget.FramePadding(padding)
  277. return b
  278. }
  279. // Build implements Widget interface.
  280. func (b *ImageButtonWithRgbaWidget) Build() {
  281. if state := Context.GetState(b.id); state == nil {
  282. Context.SetState(b.id, &imageState{})
  283. NewTextureFromRgba(b.rgba, func(tex *Texture) {
  284. Context.SetState(b.id, &imageState{texture: tex})
  285. })
  286. } else {
  287. var isOk bool
  288. imgState, isOk := state.(*imageState)
  289. Assert(isOk, "ImageButtonWithRgbaWidget", "Build", "got unexpected type of widget's state")
  290. b.ImageButtonWidget.texture = imgState.texture
  291. }
  292. b.ImageButtonWidget.Build()
  293. }
  294. var _ Widget = &CheckboxWidget{}
  295. // CheckboxWidget adds a checkbox.
  296. type CheckboxWidget struct {
  297. text string
  298. selected *bool
  299. onChange func()
  300. }
  301. // Checkbox creates a new CheckboxWidget.
  302. func Checkbox(text string, selected *bool) *CheckboxWidget {
  303. return &CheckboxWidget{
  304. text: GenAutoID(text),
  305. selected: selected,
  306. onChange: nil,
  307. }
  308. }
  309. // OnChange adds callback called when checkbox's state was changed.
  310. func (c *CheckboxWidget) OnChange(onChange func()) *CheckboxWidget {
  311. c.onChange = onChange
  312. return c
  313. }
  314. // Build implements Widget interface.
  315. func (c *CheckboxWidget) Build() {
  316. if imgui.Checkbox(tStr(c.text), c.selected) && c.onChange != nil {
  317. c.onChange()
  318. }
  319. }
  320. var _ Widget = &RadioButtonWidget{}
  321. // RadioButtonWidget is a small, round button.
  322. // It is common to use it for single-choice questions.
  323. // see examples/widgets.
  324. type RadioButtonWidget struct {
  325. text string
  326. active bool
  327. onChange func()
  328. }
  329. // RadioButton creates a radio buton.
  330. func RadioButton(text string, active bool) *RadioButtonWidget {
  331. return &RadioButtonWidget{
  332. text: GenAutoID(text),
  333. active: active,
  334. onChange: nil,
  335. }
  336. }
  337. // OnChange adds callback when button's state gets changed.
  338. func (r *RadioButtonWidget) OnChange(onChange func()) *RadioButtonWidget {
  339. r.onChange = onChange
  340. return r
  341. }
  342. // Build implements Widget interface.
  343. func (r *RadioButtonWidget) Build() {
  344. if imgui.RadioButton(tStr(r.text), r.active) && r.onChange != nil {
  345. r.onChange()
  346. }
  347. }
  348. var _ Widget = &SelectableWidget{}
  349. // SelectableWidget is a window-width button with a label which can get selected (highlighted).
  350. // useful for certain lists.
  351. type SelectableWidget struct {
  352. label string
  353. selected bool
  354. flags SelectableFlags
  355. width float32
  356. height float32
  357. onClick func()
  358. onDClick func()
  359. }
  360. // Selectable constructs a selectable widget.
  361. func Selectable(label string) *SelectableWidget {
  362. return &SelectableWidget{
  363. label: GenAutoID(label),
  364. selected: false,
  365. flags: 0,
  366. width: 0,
  367. height: 0,
  368. onClick: nil,
  369. }
  370. }
  371. // Selectablef creates a selectable widget with formated label.
  372. func Selectablef(format string, args ...any) *SelectableWidget {
  373. return Selectable(fmt.Sprintf(format, args...))
  374. }
  375. // Selected sets if selectable widget is selected.
  376. func (s *SelectableWidget) Selected(selected bool) *SelectableWidget {
  377. s.selected = selected
  378. return s
  379. }
  380. // Flags add flags.
  381. func (s *SelectableWidget) Flags(flags SelectableFlags) *SelectableWidget {
  382. s.flags = flags
  383. return s
  384. }
  385. // Size sets selectable's size.
  386. func (s *SelectableWidget) Size(width, height float32) *SelectableWidget {
  387. s.width, s.height = width, height
  388. return s
  389. }
  390. // OnClick sets on click event.
  391. func (s *SelectableWidget) OnClick(onClick func()) *SelectableWidget {
  392. s.onClick = onClick
  393. return s
  394. }
  395. // OnDClick handles mouse left button's double click event.
  396. // SelectableFlagsAllowDoubleClick will set once tonDClick callback is notnull.
  397. // NOTE: IT IS DEPRECATED and could be removed. Use EventHandler instead.
  398. func (s *SelectableWidget) OnDClick(onDClick func()) *SelectableWidget {
  399. s.onDClick = onDClick
  400. return s
  401. }
  402. // Build implements Widget interface.
  403. func (s *SelectableWidget) Build() {
  404. // If onDClick is set, check flags and set related flag when necessary
  405. if s.onDClick != nil && s.flags&SelectableFlagsAllowDoubleClick != 0 {
  406. s.flags |= SelectableFlagsAllowDoubleClick
  407. }
  408. if imgui.SelectableV(tStr(s.label), s.selected, int(s.flags), imgui.Vec2{X: s.width, Y: s.height}) && s.onClick != nil {
  409. s.onClick()
  410. }
  411. if s.onDClick != nil && IsItemActive() && IsMouseDoubleClicked(MouseButtonLeft) {
  412. s.onDClick()
  413. }
  414. }
  415. var _ Widget = &TreeNodeWidget{}
  416. // TreeNodeWidget is a a wide button with open/close state.
  417. // if is opened, the `layout` is displayed below the widget.
  418. // It can be used to create certain lists, advanced settings sections e.t.c.
  419. type TreeNodeWidget struct {
  420. label string
  421. flags TreeNodeFlags
  422. layout Layout
  423. eventHandler func()
  424. }
  425. // TreeNode creates a new tree node widget.
  426. func TreeNode(label string) *TreeNodeWidget {
  427. return &TreeNodeWidget{
  428. label: tStr(label),
  429. flags: 0,
  430. layout: nil,
  431. eventHandler: nil,
  432. }
  433. }
  434. // TreeNodef adds TreeNode with formatted label.
  435. func TreeNodef(format string, args ...any) *TreeNodeWidget {
  436. return TreeNode(fmt.Sprintf(format, args...))
  437. }
  438. // Flags sets flags.
  439. func (t *TreeNodeWidget) Flags(flags TreeNodeFlags) *TreeNodeWidget {
  440. t.flags = flags
  441. return t
  442. }
  443. // Event create TreeNode with eventHandler
  444. // You could detect events (e.g. IsItemClicked IsMouseDoubleClicked etc...) and handle them for TreeNode inside eventHandler.
  445. // Deprecated: Use EventHandler instead!
  446. func (t *TreeNodeWidget) Event(handler func()) *TreeNodeWidget {
  447. t.eventHandler = handler
  448. return t
  449. }
  450. // Layout sets layout to be displayed when tree node is opened.
  451. func (t *TreeNodeWidget) Layout(widgets ...Widget) *TreeNodeWidget {
  452. t.layout = Layout(widgets)
  453. return t
  454. }
  455. // Build implements Widget interface.
  456. func (t *TreeNodeWidget) Build() {
  457. open := imgui.TreeNodeV(t.label, int(t.flags))
  458. if t.eventHandler != nil {
  459. t.eventHandler()
  460. }
  461. if open {
  462. t.layout.Build()
  463. if (t.flags & imgui.TreeNodeFlagsNoTreePushOnOpen) == 0 {
  464. imgui.TreePop()
  465. }
  466. }
  467. }