TextWidgets.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. package giu
  2. import (
  3. "fmt"
  4. "math"
  5. "github.com/AllenDang/imgui-go"
  6. "github.com/sahilm/fuzzy"
  7. )
  8. var _ Widget = &InputTextMultilineWidget{}
  9. // InputTextMultilineWidget is a large (multiline) text input
  10. // see examples/widgets/.
  11. type InputTextMultilineWidget struct {
  12. label string
  13. text *string
  14. width, height float32
  15. flags InputTextFlags
  16. cb imgui.InputTextCallback
  17. onChange func()
  18. }
  19. // InputTextMultiline creates InputTextMultilineWidget.
  20. func InputTextMultiline(text *string) *InputTextMultilineWidget {
  21. return &InputTextMultilineWidget{
  22. text: text,
  23. width: 0,
  24. height: 0,
  25. flags: 0,
  26. cb: nil,
  27. onChange: nil,
  28. label: GenAutoID("##InputTextMultiline"),
  29. }
  30. }
  31. // Label sets input field label.
  32. func (i *InputTextMultilineWidget) Label(label string) *InputTextMultilineWidget {
  33. i.label = label
  34. return i
  35. }
  36. // Labelf is formatting version of Label.
  37. func (i *InputTextMultilineWidget) Labelf(format string, args ...any) *InputTextMultilineWidget {
  38. return i.Label(fmt.Sprintf(format, args...))
  39. }
  40. // Flags sets InputTextFlags (see Flags.go).
  41. func (i *InputTextMultilineWidget) Flags(flags InputTextFlags) *InputTextMultilineWidget {
  42. i.flags = flags
  43. return i
  44. }
  45. // Callback sets imgui.InputTextCallback.
  46. func (i *InputTextMultilineWidget) Callback(cb imgui.InputTextCallback) *InputTextMultilineWidget {
  47. i.cb = cb
  48. return i
  49. }
  50. // OnChange set callback called when user action taken on input text field (when text was changed).
  51. func (i *InputTextMultilineWidget) OnChange(onChange func()) *InputTextMultilineWidget {
  52. i.onChange = onChange
  53. return i
  54. }
  55. // Size sets input field size.
  56. func (i *InputTextMultilineWidget) Size(width, height float32) *InputTextMultilineWidget {
  57. i.width, i.height = width, height
  58. return i
  59. }
  60. // Build implements Widget interface.
  61. func (i *InputTextMultilineWidget) Build() {
  62. if imgui.InputTextMultilineV(
  63. tStr(i.label),
  64. tStrPtr(i.text),
  65. imgui.Vec2{
  66. X: i.width,
  67. Y: i.height,
  68. },
  69. int(i.flags), i.cb,
  70. ) && i.onChange != nil {
  71. i.onChange()
  72. }
  73. }
  74. var _ Widget = &BulletWidget{}
  75. // BulletWidget adds a small, white dot (bullet).
  76. // useful in enumerations.
  77. type BulletWidget struct{}
  78. // Bullet creates a bullet widget.
  79. func Bullet() *BulletWidget {
  80. return &BulletWidget{}
  81. }
  82. // Build implements Widget interface.
  83. func (b *BulletWidget) Build() {
  84. imgui.Bullet()
  85. }
  86. var _ Widget = &BulletTextWidget{}
  87. // BulletTextWidget does similar to BulletWidget, but allows
  88. // to add a text after a bullet. Very useful to create lists.
  89. type BulletTextWidget struct {
  90. text string
  91. }
  92. // BulletText creates bulletTextWidget.
  93. func BulletText(text string) *BulletTextWidget {
  94. return &BulletTextWidget{
  95. text: tStr(text),
  96. }
  97. }
  98. // BulletTextf is a formatting version of BulletText.
  99. func BulletTextf(format string, args ...any) *BulletTextWidget {
  100. return BulletText(fmt.Sprintf(format, args...))
  101. }
  102. // Build implements Widget interface.
  103. func (bt *BulletTextWidget) Build() {
  104. imgui.BulletText(bt.text)
  105. }
  106. var _ Disposable = &inputTextState{}
  107. type inputTextState struct {
  108. autoCompleteCandidates fuzzy.Matches
  109. }
  110. // Dispose implements disposable interface.
  111. func (s *inputTextState) Dispose() {
  112. s.autoCompleteCandidates = nil
  113. }
  114. var _ Widget = &InputTextWidget{}
  115. // InputTextWidget is a single-line text iinput.
  116. type InputTextWidget struct {
  117. label string
  118. hint string
  119. value *string
  120. width float32
  121. candidates []string
  122. flags InputTextFlags
  123. cb imgui.InputTextCallback
  124. onChange func()
  125. }
  126. // InputText creates new input text widget.
  127. func InputText(value *string) *InputTextWidget {
  128. return &InputTextWidget{
  129. label: GenAutoID("##InputText"),
  130. hint: "",
  131. value: value,
  132. width: 0,
  133. flags: 0,
  134. cb: nil,
  135. onChange: nil,
  136. }
  137. }
  138. // Label adds label (alternatively you can use it to set widget's id).
  139. func (i *InputTextWidget) Label(label string) *InputTextWidget {
  140. i.label = tStr(label)
  141. return i
  142. }
  143. // Labelf adds formatted label.
  144. func (i *InputTextWidget) Labelf(format string, args ...any) *InputTextWidget {
  145. return i.Label(fmt.Sprintf(format, args...))
  146. }
  147. // AutoComplete enables auto complete popup by using fuzzy search of current value against candidates
  148. // Press enter to confirm the first candidate.
  149. func (i *InputTextWidget) AutoComplete(candidates []string) *InputTextWidget {
  150. i.candidates = candidates
  151. return i
  152. }
  153. // Hint sets hint text.
  154. func (i *InputTextWidget) Hint(hint string) *InputTextWidget {
  155. i.hint = tStr(hint)
  156. return i
  157. }
  158. // Size sets field's width.
  159. func (i *InputTextWidget) Size(width float32) *InputTextWidget {
  160. i.width = width
  161. return i
  162. }
  163. // Flags sets flags.
  164. func (i *InputTextWidget) Flags(flags InputTextFlags) *InputTextWidget {
  165. i.flags = flags
  166. return i
  167. }
  168. // Callback sets input text callback.
  169. func (i *InputTextWidget) Callback(cb imgui.InputTextCallback) *InputTextWidget {
  170. i.cb = cb
  171. return i
  172. }
  173. // OnChange sets callback when text was changed.
  174. func (i *InputTextWidget) OnChange(onChange func()) *InputTextWidget {
  175. i.onChange = onChange
  176. return i
  177. }
  178. // Build implements Widget interface.
  179. func (i *InputTextWidget) Build() {
  180. // Get state
  181. var state *inputTextState
  182. if s := Context.GetState(i.label); s == nil {
  183. state = &inputTextState{}
  184. Context.SetState(i.label, state)
  185. } else {
  186. var isOk bool
  187. state, isOk = s.(*inputTextState)
  188. Assert(isOk, "InputTextWidget", "Build", "wrong state type recovered.")
  189. }
  190. if i.width != 0 {
  191. PushItemWidth(i.width)
  192. defer PopItemWidth()
  193. }
  194. isChanged := imgui.InputTextWithHint(i.label, i.hint, tStrPtr(i.value), int(i.flags), i.cb)
  195. if isChanged && i.onChange != nil {
  196. i.onChange()
  197. }
  198. if isChanged {
  199. // Enable auto complete
  200. if len(i.candidates) > 0 {
  201. matches := fuzzy.Find(*i.value, i.candidates)
  202. if matches.Len() > 0 {
  203. size := int(math.Min(5, float64(matches.Len())))
  204. matches = matches[:size]
  205. state.autoCompleteCandidates = matches
  206. }
  207. }
  208. }
  209. // Draw autocomplete list
  210. if len(state.autoCompleteCandidates) > 0 {
  211. labels := make(Layout, len(state.autoCompleteCandidates))
  212. for i, m := range state.autoCompleteCandidates {
  213. labels[i] = Label(m.Str)
  214. }
  215. SetNextWindowPos(imgui.GetItemRectMin().X, imgui.GetItemRectMax().Y)
  216. imgui.BeginTooltip()
  217. labels.Build()
  218. imgui.EndTooltip()
  219. // Press enter will replace value string with first match candidate
  220. if IsKeyPressed(KeyEnter) {
  221. *i.value = state.autoCompleteCandidates[0].Str
  222. state.autoCompleteCandidates = nil
  223. }
  224. }
  225. }
  226. var _ Widget = &InputIntWidget{}
  227. // InputIntWidget is an input text field acceptiong intager values only.
  228. type InputIntWidget struct {
  229. label string
  230. value *int32
  231. width float32
  232. flags InputTextFlags
  233. onChange func()
  234. }
  235. // InputInt creates input int widget
  236. // NOTE: value is int32, so its size is up to 10^32-1.
  237. // to process greater values, you need to use InputTextWidget
  238. // with InputTextFlagsCharsDecimal and strconv.ParseInt in OnChange callback.
  239. func InputInt(value *int32) *InputIntWidget {
  240. return &InputIntWidget{
  241. label: GenAutoID("##InputInt"),
  242. value: value,
  243. width: 0,
  244. flags: 0,
  245. onChange: nil,
  246. }
  247. }
  248. // Label sets label (id).
  249. func (i *InputIntWidget) Label(label string) *InputIntWidget {
  250. i.label = tStr(label)
  251. return i
  252. }
  253. // Labelf sets formatted label.
  254. func (i *InputIntWidget) Labelf(format string, args ...any) *InputIntWidget {
  255. return i.Label(fmt.Sprintf(format, args...))
  256. }
  257. // Size sets input's width.
  258. func (i *InputIntWidget) Size(width float32) *InputIntWidget {
  259. i.width = width
  260. return i
  261. }
  262. // Flags sets flags.
  263. func (i *InputIntWidget) Flags(flags InputTextFlags) *InputIntWidget {
  264. i.flags = flags
  265. return i
  266. }
  267. // OnChange adds on change callback.
  268. func (i *InputIntWidget) OnChange(onChange func()) *InputIntWidget {
  269. i.onChange = onChange
  270. return i
  271. }
  272. // Build implements Widget interface.
  273. func (i *InputIntWidget) Build() {
  274. if i.width != 0 {
  275. PushItemWidth(i.width)
  276. defer PopItemWidth()
  277. }
  278. if imgui.InputIntV(i.label, i.value, 0, 100, int(i.flags)) && i.onChange != nil {
  279. i.onChange()
  280. }
  281. }
  282. var _ Widget = &InputFloatWidget{}
  283. // InputFloatWidget does similar to InputIntWIdget, but accepts float numbers.
  284. type InputFloatWidget struct {
  285. label string
  286. value *float32
  287. width float32
  288. flags InputTextFlags
  289. format string
  290. onChange func()
  291. }
  292. // InputFloat constructs InputFloatWidget.
  293. func InputFloat(value *float32) *InputFloatWidget {
  294. return &InputFloatWidget{
  295. label: GenAutoID("##InputFloatWidget"),
  296. width: 0,
  297. value: value,
  298. format: "%.3f",
  299. flags: 0,
  300. onChange: nil,
  301. }
  302. }
  303. // Label sets label of input field.
  304. func (i *InputFloatWidget) Label(label string) *InputFloatWidget {
  305. i.label = tStr(label)
  306. return i
  307. }
  308. // Labelf sets formatted label.
  309. func (i *InputFloatWidget) Labelf(format string, args ...any) *InputFloatWidget {
  310. return i.Label(fmt.Sprintf(format, args...))
  311. }
  312. // Size sets input field's width.
  313. func (i *InputFloatWidget) Size(width float32) *InputFloatWidget {
  314. i.width = width
  315. return i
  316. }
  317. // Flags sets flags.
  318. func (i *InputFloatWidget) Flags(flags InputTextFlags) *InputFloatWidget {
  319. i.flags = flags
  320. return i
  321. }
  322. // Format sets data format (e.g. %.3f).
  323. func (i *InputFloatWidget) Format(format string) *InputFloatWidget {
  324. i.format = format
  325. return i
  326. }
  327. // OnChange sets callback called when text is changed.
  328. func (i *InputFloatWidget) OnChange(onChange func()) *InputFloatWidget {
  329. i.onChange = onChange
  330. return i
  331. }
  332. // Build implements Widget interface.
  333. func (i *InputFloatWidget) Build() {
  334. if i.width != 0 {
  335. PushItemWidth(i.width)
  336. defer PopItemWidth()
  337. }
  338. if imgui.InputFloatV(i.label, i.value, 0, 0, i.format, int(i.flags)) && i.onChange != nil {
  339. i.onChange()
  340. }
  341. }
  342. var _ Widget = &LabelWidget{}
  343. // LabelWidget is a plain text label.
  344. type LabelWidget struct {
  345. label string
  346. fontInfo *FontInfo
  347. wrapped bool
  348. }
  349. // Label constructs label widget.
  350. func Label(label string) *LabelWidget {
  351. return &LabelWidget{
  352. label: tStr(label),
  353. wrapped: false,
  354. }
  355. }
  356. // Labelf allows to add formatted label.
  357. func Labelf(format string, args ...any) *LabelWidget {
  358. return Label(fmt.Sprintf(format, args...))
  359. }
  360. // Wrapped determinates if label is frapped.
  361. func (l *LabelWidget) Wrapped(wrapped bool) *LabelWidget {
  362. l.wrapped = wrapped
  363. return l
  364. }
  365. // Font sets specific font (does like Style().SetFont).
  366. func (l *LabelWidget) Font(font *FontInfo) *LabelWidget {
  367. l.fontInfo = font
  368. return l
  369. }
  370. // Build implements Widget interface.
  371. func (l *LabelWidget) Build() {
  372. if l.wrapped {
  373. PushTextWrapPos()
  374. defer PopTextWrapPos()
  375. }
  376. if l.fontInfo != nil {
  377. if PushFont(l.fontInfo) {
  378. defer PopFont()
  379. }
  380. }
  381. imgui.Text(l.label)
  382. }