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