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.
285 lines
6.5 KiB
285 lines
6.5 KiB
2 years ago
|
--- @type StdUi
|
||
|
local StdUi = LibStub and LibStub('StdUi', true);
|
||
|
if not StdUi then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local module, version = 'Autocomplete', 4;
|
||
|
if not StdUi:UpgradeNeeded(module, version) then return end;
|
||
|
|
||
|
local TableInsert = tinsert;
|
||
|
|
||
|
StdUi.Util.autocompleteTransformer = function(_, value)
|
||
|
return value;
|
||
|
end
|
||
|
|
||
|
StdUi.Util.autocompleteValidator = function(self)
|
||
|
self.stdUi:MarkAsValid(self, true);
|
||
|
return true;
|
||
|
end
|
||
|
|
||
|
StdUi.Util.autocompleteItemTransformer = function(_, value)
|
||
|
if not value or value == '' then
|
||
|
return value;
|
||
|
end
|
||
|
|
||
|
local itemName = GetItemInfo(value);
|
||
|
return itemName;
|
||
|
end
|
||
|
|
||
|
StdUi.Util.autocompleteItemValidator = function(ac)
|
||
|
local itemName, itemId;
|
||
|
local t = ac:GetText();
|
||
|
local v = ac:GetValue();
|
||
|
|
||
|
if tonumber(t) ~= nil then
|
||
|
-- it's a number
|
||
|
itemName = GetItemInfo(tonumber(t));
|
||
|
if itemName then
|
||
|
itemId = tonumber(t);
|
||
|
end
|
||
|
elseif v then
|
||
|
itemName = GetItemInfo(v);
|
||
|
if itemName == t then
|
||
|
itemId = v;
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if itemId then
|
||
|
ac.value = itemId;
|
||
|
ac:SetText(itemName);
|
||
|
self.stdUi:MarkAsValid(ac, true);
|
||
|
|
||
|
return true;
|
||
|
else
|
||
|
self.stdUi:MarkAsValid(ac, false);
|
||
|
return false;
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local AutocompleteMethods = {
|
||
|
--- Private methods
|
||
|
buttonCreate = function(panel)
|
||
|
local optionButton;
|
||
|
|
||
|
optionButton = StdUi:HighlightButton(panel, panel:GetWidth(), 20, '');
|
||
|
optionButton.highlight = StdUi:HighlightButtonTexture(optionButton);
|
||
|
optionButton.highlight:Hide();
|
||
|
optionButton.text:SetJustifyH('LEFT');
|
||
|
optionButton.autocomplete = panel.autocomplete;
|
||
|
optionButton:SetFrameLevel(panel:GetFrameLevel() + 2);
|
||
|
|
||
|
optionButton:SetScript('OnClick', function(b)
|
||
|
local ac = b.autocomplete;
|
||
|
if b.boundItem then
|
||
|
b.autocomplete.selectedItem = b.boundItem;
|
||
|
end
|
||
|
|
||
|
ac.indexChosen = 0;
|
||
|
ac:SetValue(b.value, b:GetText());
|
||
|
b.autocomplete.dropdown:Hide();
|
||
|
ac:SetFocus();
|
||
|
end);
|
||
|
|
||
|
return optionButton;
|
||
|
end,
|
||
|
|
||
|
buttonUpdate = function(panel, optionButton, data)
|
||
|
optionButton.boundItem = data;
|
||
|
optionButton.value = data.value;
|
||
|
|
||
|
optionButton:SetWidth(panel:GetWidth());
|
||
|
optionButton:SetText(data.text);
|
||
|
end,
|
||
|
|
||
|
filterItems = function(ac, search, itemsToSearch)
|
||
|
local result = {};
|
||
|
|
||
|
for _, item in pairs(itemsToSearch) do
|
||
|
local valueString = tostring(item.value);
|
||
|
if
|
||
|
item.text:lower():find(search:lower(), nil, true) or
|
||
|
valueString:lower():find(search:lower(), nil, true)
|
||
|
then
|
||
|
TableInsert(result, item);
|
||
|
end
|
||
|
|
||
|
if #result >= ac.itemLimit then
|
||
|
break;
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return result;
|
||
|
end,
|
||
|
|
||
|
--- Public methods
|
||
|
SetItems = function(self, newItems)
|
||
|
self.items = newItems;
|
||
|
self:RenderItems();
|
||
|
self.dropdown:Hide();
|
||
|
end,
|
||
|
|
||
|
RenderItems = function(self)
|
||
|
local dropdownHeight = 20 * #self.filteredItems;
|
||
|
self.dropdown:SetHeight(dropdownHeight);
|
||
|
|
||
|
self.stdUi:ObjectList(
|
||
|
self.dropdown,
|
||
|
self.itemTable,
|
||
|
self.buttonCreate,
|
||
|
self.buttonUpdate,
|
||
|
self.filteredItems
|
||
|
);
|
||
|
end,
|
||
|
|
||
|
ToggleHighlightItems = function(self, flag)
|
||
|
for _, button in pairs(self.itemTable) do
|
||
|
if flag then
|
||
|
button.highlight:Show();
|
||
|
else
|
||
|
button.highlight:Hide();
|
||
|
end
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
ToggleHighlight = function(self, index, flag)
|
||
|
if flag then
|
||
|
self.itemTable[index].highlight:Show();
|
||
|
else
|
||
|
self.itemTable[index].highlight:Hide();
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
ValueToText = function(self, value)
|
||
|
return self.transformer(value)
|
||
|
end,
|
||
|
|
||
|
SetValue = function(self, value, t)
|
||
|
self.value = value;
|
||
|
self:SetText(t or self:ValueToText(value) or '');
|
||
|
self:Validate();
|
||
|
self.button:Hide();
|
||
|
end,
|
||
|
|
||
|
Validate = function(self)
|
||
|
self.isValidated = true;
|
||
|
self.isValid = self:validator();
|
||
|
|
||
|
if self.isValid then
|
||
|
if self.OnValueChanged then
|
||
|
self:OnValueChanged(self.value, self:GetText());
|
||
|
end
|
||
|
end
|
||
|
self.isValidated = false;
|
||
|
end,
|
||
|
};
|
||
|
|
||
|
local AutocompleteEvents = {
|
||
|
OnEditFocusLost = function(s)
|
||
|
C_Timer.After(0.1, function() s.dropdown:Hide(); end);
|
||
|
end,
|
||
|
|
||
|
OnEnterPressed = function(ac)
|
||
|
ac.dropdown:Hide();
|
||
|
|
||
|
-- User was using arrows to select item
|
||
|
if ac.indexChosen > 0 and ac.indexChosen <= #ac.filteredItems then
|
||
|
local item = ac.filteredItems[ac.indexChosen];
|
||
|
ac:SetValue(item.value, item.text);
|
||
|
ac.indexChosen = 0;
|
||
|
ac:ToggleHighlightItems(false);
|
||
|
end
|
||
|
ac:Validate();
|
||
|
|
||
|
if ac.CustomEnterPressed then
|
||
|
ac:CustomEnterPressed(ac);
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
OnTextChanged = function(ac, isUserInput)
|
||
|
local plainText = StdUi.Util.stripColors(ac:GetText());
|
||
|
ac.selectedItem = nil;
|
||
|
|
||
|
if isUserInput then
|
||
|
-- reset value if user changed something
|
||
|
ac.value = nil;
|
||
|
|
||
|
if type(ac.items) == 'function' then
|
||
|
-- We ensure to pass whole autocomplete as well
|
||
|
ac.filteredItems = ac:items(plainText);
|
||
|
elseif type(ac.items) == 'table' then
|
||
|
ac.filteredItems = ac:filterItems(plainText, ac.items);
|
||
|
end
|
||
|
|
||
|
if not ac.filteredItems or #ac.filteredItems == 0 then
|
||
|
ac.dropdown:Hide();
|
||
|
else
|
||
|
ac:RenderItems();
|
||
|
ac.dropdown:Show();
|
||
|
end
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
OnKeyUp = function(ac, key)
|
||
|
local arrowKeyPressed = false;
|
||
|
if key == 'UP' then
|
||
|
ac.indexChosen = ac.indexChosen - 1;
|
||
|
arrowKeyPressed = true;
|
||
|
elseif key == 'DOWN' then
|
||
|
ac.indexChosen = ac.indexChosen + 1;
|
||
|
arrowKeyPressed = true;
|
||
|
end
|
||
|
|
||
|
if arrowKeyPressed then
|
||
|
if ac.indexChosen < 1 then
|
||
|
ac.indexChosen = #ac.filteredItems;
|
||
|
end
|
||
|
|
||
|
if ac.indexChosen > #ac.filteredItems then
|
||
|
ac.indexChosen = 1;
|
||
|
end
|
||
|
|
||
|
ac:ToggleHighlightItems(false);
|
||
|
ac:ToggleHighlight(ac.indexChosen, true);
|
||
|
end
|
||
|
end
|
||
|
}
|
||
|
|
||
|
--- Very similar to dropdown except it has the ability to create new records and filters results
|
||
|
--- @return EditBox
|
||
|
function StdUi:Autocomplete(parent, width, height, text, validator, transformer, items)
|
||
|
transformer = transformer or StdUi.Util.autocompleteTransformer;
|
||
|
validator = validator or StdUi.Util.autocompleteValidator;
|
||
|
|
||
|
local autocomplete = self:EditBox(parent, width, height, text, validator);
|
||
|
---@type StdUi
|
||
|
autocomplete.stdUi = self;
|
||
|
autocomplete.transformer = transformer;
|
||
|
autocomplete.items = items;
|
||
|
autocomplete.filteredItems = {};
|
||
|
autocomplete.selectedItem = nil;
|
||
|
autocomplete.itemLimit = 8;
|
||
|
autocomplete.itemTable = {};
|
||
|
autocomplete.indexChosen = 0;
|
||
|
|
||
|
autocomplete.dropdown = self:Panel(parent, width, 20);
|
||
|
autocomplete.dropdown:SetPoint('TOPLEFT', autocomplete, 'BOTTOMLEFT', 0, 0);
|
||
|
autocomplete.dropdown:SetPoint('TOPRIGHT', autocomplete, 'BOTTOMRIGHT', 0, 0);
|
||
|
autocomplete.dropdown:Hide();
|
||
|
autocomplete.dropdown:SetFrameLevel(autocomplete:GetFrameLevel() + 10);
|
||
|
|
||
|
-- keep back reference
|
||
|
autocomplete.dropdown.autocomplete = autocomplete;
|
||
|
|
||
|
for k, v in pairs(AutocompleteMethods) do
|
||
|
autocomplete[k] = v;
|
||
|
end
|
||
|
|
||
|
for k, v in pairs(AutocompleteEvents) do
|
||
|
autocomplete:SetScript(k, v);
|
||
|
end
|
||
|
|
||
|
return autocomplete;
|
||
|
end
|
||
|
|
||
|
StdUi:RegisterModule(module, version);
|