Msgbox.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package giu
  2. import "fmt"
  3. // DialogResult represents dialog result
  4. // dialog resullt is bool. if OK/Yes it is true, else (Cancel/No) - false.
  5. type DialogResult bool
  6. // dialog results.
  7. const (
  8. DialogResultOK DialogResult = true
  9. DialogResultCancel DialogResult = false
  10. DialogResultYes = DialogResultOK
  11. DialogResultNo = DialogResultCancel
  12. )
  13. // MsgboxButtons determines which buttons are in the dialog.
  14. type MsgboxButtons uint8
  15. // button sets.
  16. const (
  17. // Yes-No question.
  18. MsgboxButtonsYesNo MsgboxButtons = 1 << iota
  19. // Ok / Cancel dialog.
  20. MsgboxButtonsOkCancel
  21. // info.
  22. MsgboxButtonsOk
  23. )
  24. // DialogResultCallback is a callback for dialogs.
  25. type DialogResultCallback func(DialogResult)
  26. var _ Disposable = &msgboxState{}
  27. type msgboxState struct {
  28. title string
  29. content string
  30. resultCallback DialogResultCallback
  31. buttons MsgboxButtons
  32. open bool
  33. }
  34. // Dispose implements disposable interface.
  35. func (ms *msgboxState) Dispose() {
  36. // Nothing to do here.
  37. }
  38. func msgboxInvokeCallback(result DialogResult, callback DialogResultCallback) {
  39. CloseCurrentPopup()
  40. if callback != nil {
  41. callback(result)
  42. }
  43. }
  44. func buildMsgboxButtons(buttons MsgboxButtons, callback DialogResultCallback) Layout {
  45. switch buttons {
  46. case MsgboxButtonsOk:
  47. return Layout{
  48. Button(" Ok ").OnClick(func() {
  49. msgboxInvokeCallback(DialogResultOK, callback)
  50. }),
  51. }
  52. case MsgboxButtonsOkCancel:
  53. return Layout{
  54. Row(
  55. Button(" Ok ").OnClick(func() {
  56. msgboxInvokeCallback(DialogResultOK, callback)
  57. }),
  58. Button("Cancel").OnClick(func() {
  59. msgboxInvokeCallback(DialogResultCancel, callback)
  60. }),
  61. ),
  62. }
  63. case MsgboxButtonsYesNo:
  64. return Layout{
  65. Row(
  66. Button(" Yes ").OnClick(func() {
  67. msgboxInvokeCallback(DialogResultYes, callback)
  68. }),
  69. Button(" No ").OnClick(func() {
  70. msgboxInvokeCallback(DialogResultNo, callback)
  71. }),
  72. ),
  73. }
  74. default:
  75. return Layout{
  76. Button(" Ok ").OnClick(func() {
  77. msgboxInvokeCallback(DialogResultOK, callback)
  78. }),
  79. }
  80. }
  81. }
  82. const msgboxID string = "###Msgbox"
  83. // PrepareMsgbox should be invoked in function in the same layout level where you call g.Msgbox.
  84. // BUG: calling this more than 1 time per frame causes unexpected
  85. // merging msgboxes layouts (see https://github.com/AllenDang/giu/issues/290)
  86. func PrepareMsgbox() Layout {
  87. return Layout{
  88. Custom(func() {
  89. var state *msgboxState
  90. // Register state.
  91. stateRaw := Context.GetState(msgboxID)
  92. if stateRaw == nil {
  93. state = &msgboxState{title: "Info", content: "Content", buttons: MsgboxButtonsOk, resultCallback: nil, open: false}
  94. Context.SetState(msgboxID, state)
  95. } else {
  96. var isOk bool
  97. state, isOk = stateRaw.(*msgboxState)
  98. Assert(isOk, "MsgboxWidget", "PrepareMsgbox", "got state of unexpected type")
  99. }
  100. if state.open {
  101. OpenPopup(msgboxID)
  102. state.open = false
  103. }
  104. SetNextWindowSize(300, 0)
  105. PopupModal(fmt.Sprintf("%s%s", state.title, msgboxID)).Layout(
  106. Custom(func() {
  107. // Ensure the state is valid.
  108. Context.GetState(msgboxID)
  109. }),
  110. Label(state.content).Wrapped(true),
  111. buildMsgboxButtons(state.buttons, state.resultCallback),
  112. ).Build()
  113. }),
  114. }
  115. }
  116. // MsgboxWidget represents message dialog.
  117. type MsgboxWidget struct{}
  118. func (m *MsgboxWidget) getState() *msgboxState {
  119. stateRaw := Context.GetState(msgboxID)
  120. if stateRaw == nil {
  121. panic("Msgbox is not prepared. Invoke giu.PrepareMsgbox in the end of the layout.")
  122. }
  123. result, isOk := stateRaw.(*msgboxState)
  124. Assert(isOk, "MsgboxWidget", "getState", "unexpected type of widget's state recovered")
  125. return result
  126. }
  127. // Msgbox opens message box.
  128. // call it whenever you want to open popup with
  129. // question / info.
  130. func Msgbox(title, content string) *MsgboxWidget {
  131. result := &MsgboxWidget{}
  132. state := result.getState()
  133. state.title = title
  134. state.content = content
  135. state.buttons = MsgboxButtonsOk
  136. state.resultCallback = nil
  137. state.open = true
  138. return result
  139. }
  140. // Buttons sets which buttons should be possible.
  141. func (m *MsgboxWidget) Buttons(buttons MsgboxButtons) *MsgboxWidget {
  142. s := m.getState()
  143. s.buttons = buttons
  144. return m
  145. }
  146. // ResultCallback sets result callback.
  147. func (m *MsgboxWidget) ResultCallback(cb DialogResultCallback) *MsgboxWidget {
  148. s := m.getState()
  149. s.resultCallback = cb
  150. return m
  151. }