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.
bastion/libs/StdUi/widgets/Dropdown.lua

283 lines
6.4 KiB

--- @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);