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.
283 lines
6.4 KiB
283 lines
6.4 KiB
2 years ago
|
--- @type StdUi
|
||
|
local StdUi = LibStub and LibStub('StdUi', true);
|
||
|
if not StdUi then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local module, version = 'Dropdown', 4;
|
||
|
if not StdUi:UpgradeNeeded(module, version) then return end;
|
||
|
|
||
|
local TableInsert = tinsert;
|
||
|
|
||
|
-- reference to all other dropdowns to close them when new one opens
|
||
|
local dropdowns = StdUiDropdowns or {};
|
||
|
StdUiDropdowns = dropdowns;
|
||
|
|
||
|
local DropdownItemOnClick = function(self)
|
||
|
self.dropdown:SetValue(self.value, self:GetText());
|
||
|
self.dropdown.optsFrame:Hide();
|
||
|
end
|
||
|
|
||
|
local DropdownItemOnValueChanged = function(checkbox, isChecked)
|
||
|
checkbox.dropdown:ToggleValue(checkbox.value, isChecked);
|
||
|
end
|
||
|
|
||
|
local DropdownMethods = {
|
||
|
buttonCreate = function(parent)
|
||
|
local dropdown = parent.dropdown;
|
||
|
local optionButton;
|
||
|
|
||
|
if dropdown.multi then
|
||
|
optionButton = dropdown.stdUi:Checkbox(parent, '', parent:GetWidth(), 20);
|
||
|
else
|
||
|
optionButton = dropdown.stdUi:HighlightButton(parent, parent:GetWidth(), 20, '');
|
||
|
optionButton.text:SetJustifyH('LEFT');
|
||
|
end
|
||
|
|
||
|
optionButton.dropdown = dropdown;
|
||
|
optionButton:SetFrameLevel(parent:GetFrameLevel() + 2);
|
||
|
if not dropdown.multi then
|
||
|
optionButton:SetScript('OnClick', DropdownItemOnClick);
|
||
|
else
|
||
|
optionButton.OnValueChanged = DropdownItemOnValueChanged;
|
||
|
end
|
||
|
|
||
|
return optionButton;
|
||
|
end,
|
||
|
|
||
|
buttonUpdate = function(parent, itemFrame, data)
|
||
|
itemFrame:SetWidth(parent:GetWidth());
|
||
|
itemFrame:SetText(data.text);
|
||
|
|
||
|
if itemFrame.dropdown.multi then
|
||
|
itemFrame:SetValue(data.value);
|
||
|
else
|
||
|
itemFrame.value = data.value;
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
ShowOptions = function(self)
|
||
|
for i = 1, #dropdowns do
|
||
|
dropdowns[i]:HideOptions();
|
||
|
end
|
||
|
|
||
|
self.optsFrame:UpdateSize(self:GetWidth(), self.optsFrame:GetHeight());
|
||
|
self.optsFrame:Show();
|
||
|
self.optsFrame:Update();
|
||
|
self:RepaintOptions();
|
||
|
end,
|
||
|
|
||
|
HideOptions = function(self)
|
||
|
self.optsFrame:Hide();
|
||
|
end,
|
||
|
|
||
|
ToggleOptions = function(self)
|
||
|
if self.optsFrame:IsShown() then
|
||
|
self:HideOptions();
|
||
|
else
|
||
|
self:ShowOptions();
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
SetPlaceholder = function(self, placeholderText)
|
||
|
if self:GetText() == '' or self:GetText() == self.placeholder then
|
||
|
self:SetText(placeholderText);
|
||
|
end
|
||
|
|
||
|
self.placeholder = placeholderText;
|
||
|
end,
|
||
|
|
||
|
RepaintOptions = function(self)
|
||
|
local scrollChild = self.optsFrame.scrollChild;
|
||
|
self.stdUi:ObjectList(
|
||
|
scrollChild,
|
||
|
scrollChild.items,
|
||
|
self.buttonCreate,
|
||
|
self.buttonUpdate,
|
||
|
self.options
|
||
|
);
|
||
|
self.optsFrame:UpdateItemsCount(#self.options);
|
||
|
end,
|
||
|
|
||
|
SetOptions = function(self, newOptions)
|
||
|
self.options = newOptions;
|
||
|
local optionsHeight = #newOptions * 20;
|
||
|
local scrollChild = self.optsFrame.scrollChild;
|
||
|
if not scrollChild.items then
|
||
|
scrollChild.items = {};
|
||
|
end
|
||
|
|
||
|
self.optsFrame:SetHeight(math.min(optionsHeight + 4, 200));
|
||
|
scrollChild:SetHeight(optionsHeight);
|
||
|
|
||
|
self:RepaintOptions();
|
||
|
end,
|
||
|
|
||
|
ToggleValue = function(self, value, state)
|
||
|
assert(self.multi, 'Single dropdown cannot have more than one value!');
|
||
|
|
||
|
-- Treat is as associative array
|
||
|
if self.assoc then
|
||
|
self.value[value] = state;
|
||
|
else
|
||
|
if state then
|
||
|
-- we are toggling it on
|
||
|
if not tContains(self.value, value) then
|
||
|
TableInsert(self.value, value);
|
||
|
end
|
||
|
else
|
||
|
-- we are removing it from table
|
||
|
if tContains(self.value, value) then
|
||
|
tDeleteItem(self.value, value);
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
self:SetValue(self.value);
|
||
|
end,
|
||
|
|
||
|
SetValue = function(self, value, text)
|
||
|
self.value = value;
|
||
|
|
||
|
if text then
|
||
|
self:SetText(text);
|
||
|
else
|
||
|
self:SetText(self:FindValueText(value));
|
||
|
end
|
||
|
|
||
|
if self.multi then
|
||
|
for _, checkbox in pairs(self.optsFrame.scrollChild.items) do
|
||
|
local isChecked = false;
|
||
|
if self.assoc then
|
||
|
isChecked = self.value[checkbox.value];
|
||
|
else
|
||
|
isChecked = tContains(self.value, checkbox.value);
|
||
|
end
|
||
|
|
||
|
checkbox:SetChecked(isChecked, true);
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if self.OnValueChanged then
|
||
|
self.OnValueChanged(self, value, self:GetText());
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
GetValue = function(self)
|
||
|
return self.value;
|
||
|
end,
|
||
|
|
||
|
FindValueText = function(self, value)
|
||
|
if type(value) ~= 'table' then
|
||
|
for i = 1, #self.options do
|
||
|
local opt = self.options[i];
|
||
|
|
||
|
if opt.value == value then
|
||
|
return opt.text;
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return self.placeholder or '';
|
||
|
else
|
||
|
local result = '';
|
||
|
|
||
|
for i = 1, #self.options do
|
||
|
local opt = self.options[i];
|
||
|
|
||
|
if self.assoc then
|
||
|
for key, checked in pairs(value) do
|
||
|
if checked and key == opt.value then
|
||
|
if result == '' then
|
||
|
result = opt.text;
|
||
|
else
|
||
|
result = result .. ', ' .. opt.text;
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
for x = 1, #value do
|
||
|
if value[x] == opt.value then
|
||
|
if result == '' then
|
||
|
result = opt.text;
|
||
|
else
|
||
|
result = result .. ', ' .. opt.text;
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if result ~= '' then
|
||
|
return result
|
||
|
else
|
||
|
return self.placeholder or '';
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
};
|
||
|
|
||
|
local DropdownEvents = {
|
||
|
OnClick = function(self)
|
||
|
self:ToggleOptions();
|
||
|
end
|
||
|
};
|
||
|
|
||
|
--- Creates a single level dropdown menu
|
||
|
--- local options = {
|
||
|
--- {text = 'some text', value = 10},
|
||
|
--- {text = 'some text2', value = 11},
|
||
|
--- {text = 'some text3', value = 12},
|
||
|
--- }
|
||
|
--- @return Dropdown
|
||
|
function StdUi:Dropdown(parent, width, height, options, value, multi, assoc)
|
||
|
--- @class Dropdown
|
||
|
local dropdown = self:Button(parent, width, height, '');
|
||
|
dropdown.stdUi = self;
|
||
|
|
||
|
dropdown.text:SetJustifyH('LEFT');
|
||
|
-- make it shorter because of arrow
|
||
|
dropdown.text:ClearAllPoints();
|
||
|
self:GlueAcross(dropdown.text, dropdown, 2, -2, -16, 2);
|
||
|
|
||
|
local dropTex = self:Texture(dropdown, 15, 15, [[Interface\Buttons\SquareButtonTextures]]);
|
||
|
dropTex:SetTexCoord(0.45312500, 0.64062500, 0.20312500, 0.01562500);
|
||
|
self:GlueRight(dropTex, dropdown, -2, 0, true);
|
||
|
|
||
|
local optsFrame = self:FauxScrollFrame(dropdown, dropdown:GetWidth(), 200, 10, 20);
|
||
|
optsFrame:Hide();
|
||
|
self:GlueBelow(optsFrame, dropdown, 0, 1, 'LEFT');
|
||
|
dropdown:SetFrameLevel(optsFrame:GetFrameLevel() + 1);
|
||
|
|
||
|
dropdown.multi = multi;
|
||
|
dropdown.assoc = assoc;
|
||
|
|
||
|
dropdown.optsFrame = optsFrame;
|
||
|
dropdown.dropTex = dropTex;
|
||
|
dropdown.options = options;
|
||
|
|
||
|
optsFrame.scrollChild.dropdown = dropdown;
|
||
|
|
||
|
for k, v in pairs(DropdownMethods) do
|
||
|
dropdown[k] = v;
|
||
|
end
|
||
|
|
||
|
if options then
|
||
|
dropdown:SetOptions(options);
|
||
|
end
|
||
|
|
||
|
if value then
|
||
|
dropdown:SetValue(value);
|
||
|
elseif multi then
|
||
|
dropdown.value = {};
|
||
|
end
|
||
|
|
||
|
for k, v in pairs(DropdownEvents) do
|
||
|
dropdown:SetScript(k, v);
|
||
|
end
|
||
|
|
||
|
TableInsert(dropdowns, dropdown);
|
||
|
|
||
|
return dropdown;
|
||
|
end
|
||
|
|
||
|
StdUi:RegisterModule(module, version);
|