forked from Bastion/Bastion
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
6.3 KiB
267 lines
6.3 KiB
2 years ago
|
--- @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);
|