-
Notifications
You must be signed in to change notification settings - Fork 26
/
StdUiBuilder.lua
267 lines (229 loc) · 6.25 KB
/
StdUiBuilder.lua
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
--- @type StdUi
local StdUi = LibStub and LibStub('StdUi', true);
if not StdUi then
return
end
local module, version = 'Builder', 6;
if not StdUi:UpgradeNeeded(module, version) then
return
end
local util = StdUi.Util;
local function setDatabaseValue(db, key, value)
if key:find('.') then
local accessor = StdUi.Util.stringSplit('.', key);
local startPos = db;
for i, subKey in pairs(accessor) do
if i == #accessor then
startPos[subKey] = value;
return
end
startPos = startPos[subKey];
end
else
db[key] = value;
end
end
local function getDatabaseValue(db, key)
if key:find('.') then
local accessor = StdUi.Util.stringSplit('.', key);
local startPos = db;
for i, subKey in pairs(accessor) do
if i == #accessor then
return startPos[subKey];
end
startPos = startPos[subKey];
end
else
return db[key];
end
end
---BuildElement
---@param frame Frame
---@param row EasyLayoutRow
---@param info table
---@param dataKey string
---@param db table
function StdUi:BuildElement(frame, row, info, dataKey, db)
local element;
local genericChangeEvent = function(el, value)
setDatabaseValue(el.dbReference, el.dataKey, value);
if el.onChange then
el:onChange(value);
end
end
local hasLabel = false;
if info.type == 'checkbox' then
element = self:Checkbox(frame, info.label);
elseif info.type == 'editBox' then
element = self:EditBox(frame, nil, 20);
elseif info.type == 'multiLineBox' then
element = self:MultiLineBox(frame, 300, 20);
elseif info.type == 'dropdown' then
element = self:Dropdown(frame, 300, 20, info.options or {}, nil, info.multi or nil, info.assoc or false);
elseif info.type == 'autocomplete' then
element = self:Autocomplete(frame, 300, 20, '');
if info.validator then
element.validator = info.validator;
end
if info.transformer then
element.transformer = info.transformer;
end
if info.buttonCreate then
element.buttonCreate = info.buttonCreate;
end
if info.buttonUpdate then
element.buttonUpdate = info.buttonUpdate;
end
if info.items then
element:SetItems(info.items);
end
elseif info.type == 'slider' or info.type == 'sliderWithBox' then
element = self:SliderWithBox(frame, nil, 32, 0, info.min or 0, info.max or 2);
if info.precision then
element:SetPrecision(info.precision);
end
elseif info.type == 'color' then
element = self:ColorInput(frame, info.label, 100, 20, info.color);
elseif info.type == 'button' then
element = self:Button(frame, nil, 20, info.text or '');
if info.onClick then
element:SetScript('OnClick', info.onClick);
end
elseif info.type == 'header' then
element = self:Header(frame, info.label);
elseif info.type == 'label' then
element = self:Label(frame, info.label);
elseif info.type == 'texture' then
element = self:Texture(frame, info.width or 24, info.height or 24, info.texture);
elseif info.type == 'panel' then -- Containers
element = self:Panel(frame, 300, 20);
elseif info.type == 'scroll' then
element = self:ScrollFrame(
frame,
300,
20,
type(info.scrollChild) == 'table' and info.scrollChild or nil
);
if type(info.scrollChild) == 'function' then
info.scrollChild(element);
end
elseif info.type == 'fauxScroll' then
element = self:FauxScrollFrame(
frame,
300,
20,
info.displayCount or 5,
info.lineHeight or 22,
type(info.scrollChild) == 'table' and info.scrollChild or nil
);
if type(info.scrollChild) == 'function' then
info.scrollChild(element);
end
elseif info.type == 'tab' then
element = self:TabPanel(
frame,
300,
20,
info.tabs or {},
info.vertical or false,
info.buttonWidth,
info.buttonHeight
);
elseif info.type == 'custom' then
element = info.createFunction(frame, row, info, dataKey, db);
end
if not element then
print('Could not build element with type: ', info.type);
end
-- Widgets can have initialization code
if info.init then
info.init(element);
end
element.dbReference = db;
element.dataKey = dataKey;
if info.onChange then
element.onChange = info.onChange;
end
if element.hasLabel then
hasLabel = true;
end
local canHaveLabel = info.type ~= 'checkbox' and
info.type ~= 'header' and
info.type ~= 'label' and
info.type ~= 'color';
if info.label and canHaveLabel then
self:AddLabel(frame, element, info.label);
hasLabel = true;
end
if info.initialValue then
if element.SetChecked then
element:SetChecked(info.initialValue);
elseif element.SetColor then
element:SetColor(info.initialValue);
elseif element.SetValue then
element:SetValue(info.initialValue);
end
end
-- Setting onValueChanged disqualifies from any writes to database
if info.onValueChanged then
element.OnValueChanged = info.onValueChanged;
elseif db then
local iVal = getDatabaseValue(db, dataKey);
if info.type == 'checkbox' then
element:SetChecked(iVal)
elseif element.SetColor then
element:SetColor(iVal);
elseif element.SetValue then
element:SetValue(iVal);
end
element.OnValueChanged = genericChangeEvent;
end
-- Technically, every frame can be a container
if info.children then
self:BuildWindow(element, info.children);
self:EasyLayout(element, { padding = { top = 10 } });
element:SetScript('OnShow', function(of)
of:DoLayout();
end);
end
row:AddElement(element, {
column = info.column or 12,
fullSize = info.fullSize or false,
fullHeight = info.fullHeight or false,
margin = info.layoutMargins or {
top = (hasLabel and 20 or 0)
}
});
return element;
end
---BuildRow
---@param frame Frame
---@param info table
---@param db table
function StdUi:BuildRow(frame, info, db)
local row = frame:AddRow();
for key, element in util.orderedPairs(info) do
local dataKey = element.key or key or nil;
local el = self:BuildElement(frame, row, element, dataKey, db);
if element then
if not frame.elements then
frame.elements = {};
end
frame.elements[key] = el;
end
end
end
---BuildWindow
---@param frame Frame
---@param info table
function StdUi:BuildWindow(frame, info)
local db = info.database or nil;
assert(info.rows, 'Rows are required in order to build table');
local rows = info.rows;
self:EasyLayout(frame, info.layoutConfig);
for _, row in util.orderedPairs(rows) do
self:BuildRow(frame, row, db);
end
frame:DoLayout();
end
StdUi:RegisterModule(module, version);