farmbuyer@54: local Type, Version = "EditBoxDropDown", 5 farmbuyer@1: local AceGUI = LibStub and LibStub("AceGUI-3.0", true) farmbuyer@1: if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end farmbuyer@1: farmbuyer@1: --[[ farmbuyer@1: This is a from-scratch AceGUI-style implementation of the combination drop- farmbuyer@1: down editboxes seen in DoTimer's option menus. Typing in the editbox adds farmbuyer@1: entries to a list; clicking the scrolldown button displays the list in a farmbuyer@1: dropdown; clicking on an entry in the dropdown removes it from the list. farmbuyer@1: farmbuyer@1: (This type of widget may actually have a more formal name already. I first farmbuyer@1: encountered it in DoTimer, so that's what I named it after.) farmbuyer@1: farmbuyer@1: The implementation does not borrow any of DoTimer's actual code. I've tried farmbuyer@1: writing this to imitate the look-and-feel of an interface that many players farmbuyer@1: will find familiar and easy to use. farmbuyer@1: farmbuyer@1: Version 2 is a rehash to follow the same form of the rest of the AceGUI widgets farmbuyer@1: after their rewrite, and to work with the new EditBox behavior. farmbuyer@1: farmbuyer@1: Version 3 adds the EditBoxDropDownOptionControl variant, specifically for use farmbuyer@1: as a 'dialogControl' in AceConfig option tables. Details follow; the more farmbuyer@54: disappointing restrictions are because AceConfig never gives the programmer any farmbuyer@1: direct link to the widgets in use. farmbuyer@1: - 'desc' option field ignored farmbuyer@1: - 'name' field may contain an embedded tab ('\t') followed by more text to be farmbuyer@1: used as the tooltip text when hovering over the editbox field farmbuyer@1: - 'get' field must be a function, returning a function to be used as the farmbuyer@1: OnTextEnterPressed callback; this is typically how new entries should be farmbuyer@1: added to data farmbuyer@1: - 'values' field must be a function, returning the usual list of entries, PLUS farmbuyer@54: the callback used for 'get' as a key, e.g., farmbuyer@1: values = function() farmbuyer@54: local ret = build_real_dropdown_table() farmbuyer@54: ret[get_callback] = true -- assuming "function get_callback (widget, event, text) .... end" farmbuyer@1: return ret farmbuyer@1: end farmbuyer@1: The callback will be immediately removed from the table, but is required to farmbuyer@1: be present to pass tests in the AceConfig source. farmbuyer@1: - 'set' receives the key of the dropdown table, but that entry will already be farmbuyer@1: removed by the time the 'set' function is called farmbuyer@1: farmbuyer@54: Version 4 was never released. farmbuyer@54: farmbuyer@54: Version 5 adds the OnDropdownShown callback. farmbuyer@54: farmbuyer@1: farmbuyer@1: EditBoxDropDown API farmbuyer@1: farmbuyer@1: :SetLabel(txt) farmbuyer@1: forwards to the editbox's SetLabel farmbuyer@1: farmbuyer@1: :SetText(txt) farmbuyer@1: forwards to the editbox's SetText farmbuyer@1: farmbuyer@1: :SetEditBoxTooltip(txt) farmbuyer@1: sets text for the tooltip shown when hovering over the editbox farmbuyer@1: no default farmbuyer@1: farmbuyer@1: :SetButtonTooltip(txt) farmbuyer@1: sets text for the tooltip shown when hovering over the dropdown button farmbuyer@1: default "Click on entries to remove them." farmbuyer@1: farmbuyer@1: :SetList(t) farmbuyer@1: T is a table to be shown in the dropdown list; the values will be displayed farmbuyer@1: in the dropdown farmbuyer@1: When entries are clicked, they will be removed from T. T is not copied, farmbuyer@1: the table is edited "live" before firing the callback below. farmbuyer@1: farmbuyer@1: farmbuyer@1: EditBoxDropDown Callbacks farmbuyer@1: farmbuyer@1: OnTextEnterPressed farmbuyer@1: same as the editbox's OnEnterPressed farmbuyer@1: farmbuyer@1: OnListItemClicked farmbuyer@1: similar to a Dropdown widget's OnValueChanged, the key and value from the farmbuyer@1: table given to :SetList are passed farmbuyer@1: farmbuyer@54: OnDropdownShown farmbuyer@54: when the down arrow is clicked to display the list farmbuyer@1: farmbuyer@54: farmbuyer@54: farmbuyer@gmail.com farmbuyer@1: ]] farmbuyer@1: farmbuyer@1: local button_hover_text_default = "Click on entries to remove them." farmbuyer@1: local maps -- from actual editbox frame back to this widget farmbuyer@1: farmbuyer@1: farmbuyer@1: local function Menu_OnClick (button, userlist_key, widget) farmbuyer@1: local v = widget.list[userlist_key] farmbuyer@1: widget.list[userlist_key] = nil farmbuyer@1: widget.dropdown.is_on = nil farmbuyer@1: -- firing these off changes widget contents, must be done last :-( farmbuyer@1: widget:Fire("OnListItemClicked", userlist_key, v) farmbuyer@1: widget:Fire("OnValueChanged", userlist_key) farmbuyer@1: end farmbuyer@1: farmbuyer@1: local function BuildList (widget) farmbuyer@1: local ret = {} farmbuyer@1: if widget.list then for k,v in pairs(widget.list) do farmbuyer@1: table.insert (ret, { farmbuyer@1: text = tostring(v) or tostring(k), farmbuyer@1: func = Menu_OnClick, farmbuyer@1: arg1 = k, farmbuyer@1: arg2 = widget, farmbuyer@1: notCheckable = true, farmbuyer@1: }) farmbuyer@1: end end farmbuyer@1: return ret farmbuyer@1: end farmbuyer@1: farmbuyer@1: farmbuyer@1: local function ddEditBox_OnMouseEnter (editbox) farmbuyer@1: if editbox.tooltip_text then farmbuyer@1: GameTooltip:SetOwner(editbox.frame, "ANCHOR_RIGHT") farmbuyer@1: GameTooltip:SetText(editbox.tooltip_text, nil, nil, nil, nil, 1) farmbuyer@1: end farmbuyer@1: end farmbuyer@1: farmbuyer@1: local function ddEditBox_OnMouseLeave (editbox_or_button) farmbuyer@1: GameTooltip:Hide() farmbuyer@1: end farmbuyer@1: farmbuyer@1: local function ddEditBox_Clear (editboxframe) farmbuyer@1: editboxframe:SetText("") farmbuyer@1: end farmbuyer@1: farmbuyer@1: local function ddEditBox_Reset (editboxframe) -- :ClearFocus triggers this farmbuyer@1: editboxframe:SetText(maps[editboxframe].editbox_basetext or "") farmbuyer@1: end farmbuyer@1: farmbuyer@1: local function ddEditBox_OnEnterPressed (editbox, _, text) farmbuyer@1: editbox.obj:Fire("OnTextEnterPressed", text) farmbuyer@1: ddEditBox_Reset(editbox.editbox) farmbuyer@1: editbox.editbox:ClearFocus() -- cursor still blinking in there, one more time farmbuyer@1: end farmbuyer@1: farmbuyer@1: local function Button_OnClick (button) farmbuyer@1: local dd = button.obj.dropdown farmbuyer@1: -- Annoyingly, it's very tedious to find the correct nested frame to use farmbuyer@1: -- for :IsShown() here. We'll avoid it all by using our own flag. farmbuyer@1: if dd.is_on then farmbuyer@1: dd.is_on = nil farmbuyer@1: HideDropDownMenu(--[[level=]]1) -- EasyMenu always uses top/1 level farmbuyer@1: else farmbuyer@1: dd.is_on = true farmbuyer@1: local t = BuildList(button.obj) farmbuyer@1: EasyMenu (t, button.obj.dropdown, button.obj.frame, 0, 0, "MENU") farmbuyer@1: PlaySound("igMainMenuOptionCheckBoxOn") farmbuyer@54: button.obj:Fire("OnDropdownShown") farmbuyer@1: end farmbuyer@1: end farmbuyer@1: farmbuyer@1: local function Button_OnEnter (button) farmbuyer@1: if button.tooltip_text then farmbuyer@1: GameTooltip:SetOwner(button, "ANCHOR_RIGHT") farmbuyer@1: GameTooltip:SetText(button.tooltip_text, nil, nil, nil, nil, 1) farmbuyer@1: end farmbuyer@1: end farmbuyer@1: farmbuyer@1: farmbuyer@54: --local base_SetWidth = AceGUI.WidgetBase.SetWidth farmbuyer@1: local methods = { farmbuyer@1: ["OnAcquire"] = function (self) farmbuyer@1: self:SetHeight(20) farmbuyer@1: self:SetWidth(100) farmbuyer@1: self.button.tooltip_text = button_hover_text_default farmbuyer@1: self:SetList(nil) farmbuyer@1: self.editbox:DisableButton(true) farmbuyer@1: farmbuyer@1: maps = maps or {} farmbuyer@1: maps[self.editbox.editbox] = self farmbuyer@1: end, farmbuyer@54: --[=[ farmbuyer@54: ["SetWidth"] = function (self, width) farmbuyer@54: print("got",width) farmbuyer@54: base_SetWidth(self, width) farmbuyer@54: self.frame.width = width + 45 farmbuyer@54: end, farmbuyer@1: farmbuyer@54: ["GetWidth"] = function (self) farmbuyer@54: return self.frame:GetWidth() + 45 farmbuyer@54: end, farmbuyer@54: ]=] farmbuyer@1: ["OnRelease"] = function (self) farmbuyer@1: self.frame:ClearAllPoints() farmbuyer@1: self.frame:Hide() farmbuyer@1: self.editbox.tooltip_text = nil farmbuyer@1: self.button.tooltip_text = nil farmbuyer@1: self:SetList(nil) farmbuyer@1: maps[self.editbox.editbox] = nil farmbuyer@1: end, farmbuyer@1: farmbuyer@1: ["SetParent"] = function (self, parent) farmbuyer@1: self.frame:SetParent(nil) farmbuyer@1: self.frame:SetParent(parent.content) farmbuyer@1: self.parent = parent farmbuyer@1: self.editbox:SetParent(parent) farmbuyer@1: end, farmbuyer@1: farmbuyer@1: ["SetText"] = function (self, text) farmbuyer@1: self.editbox_basetext = text farmbuyer@1: return self.editbox:SetText(text) farmbuyer@1: end, farmbuyer@1: farmbuyer@1: ["SetLabel"] = function (self, text) farmbuyer@1: return self.editbox:SetLabel(text) farmbuyer@1: end, farmbuyer@1: farmbuyer@1: ["SetDisabled"] = function (self, disabled) farmbuyer@1: self.editbox:SetDisabled(disabled) farmbuyer@1: if disabled then farmbuyer@1: self.button:Disable() farmbuyer@1: else farmbuyer@1: self.button:Enable() farmbuyer@1: end farmbuyer@1: end, farmbuyer@1: farmbuyer@1: ["SetList"] = function (self, list) farmbuyer@1: self.list = list farmbuyer@1: end, farmbuyer@1: farmbuyer@1: ["SetEditBoxTooltip"] = function (self, text) farmbuyer@1: self.editbox.tooltip_text = text farmbuyer@1: end, farmbuyer@1: farmbuyer@1: ["SetButtonTooltip"] = function (self, text) farmbuyer@1: self.button.tooltip_text = text farmbuyer@1: end, farmbuyer@1: } farmbuyer@1: farmbuyer@1: -- called with the 'name' entry farmbuyer@1: local function optcontrol_SetLabel (self, text) farmbuyer@1: local name, desc = ('\t'):split(text) farmbuyer@1: if desc then farmbuyer@1: self:SetEditBoxTooltip(desc) farmbuyer@1: end farmbuyer@1: self.editbox:SetLabel(name) farmbuyer@1: end farmbuyer@1: farmbuyer@1: local function optcontrol_SetValue (self, epcallback) farmbuyer@1: -- set the callback farmbuyer@1: self:SetCallback("OnTextEnterPressed", epcallback) farmbuyer@1: -- remove the fake entry from the values table farmbuyer@1: self.list[epcallback] = nil farmbuyer@1: end farmbuyer@1: farmbuyer@1: farmbuyer@1: local function Constructor(is_option_control) farmbuyer@1: local num = AceGUI:GetNextWidgetNum(Type) farmbuyer@1: farmbuyer@1: -- Its frame becomes our widget frame, else its frame is never shown. Gluing farmbuyer@1: -- them together seems a little evil, but it beats making this widget into a farmbuyer@1: -- formal containter. Inspired by new-style InteractiveLabel. farmbuyer@1: local editbox = AceGUI:Create("EditBox") farmbuyer@1: local frame = editbox.frame farmbuyer@1: editbox:SetHeight(20) farmbuyer@1: editbox:SetWidth(100) farmbuyer@54: --frame:SetWidth(frame:GetWidth()+15) farmbuyer@54: --frame.width = frame:GetWidth() + 15 farmbuyer@1: editbox:SetCallback("OnEnter", ddEditBox_OnMouseEnter) farmbuyer@1: editbox:SetCallback("OnLeave", ddEditBox_OnMouseLeave) farmbuyer@1: editbox:SetCallback("OnEnterPressed", ddEditBox_OnEnterPressed) farmbuyer@1: editbox.editbox:SetScript("OnEditFocusGained", ddEditBox_Clear) farmbuyer@1: editbox.editbox:SetScript("OnEditFocusLost", ddEditBox_Reset) farmbuyer@1: --editbox.editbox:SetScript("OnEscapePressed", ddEditBox_Reset) farmbuyer@1: farmbuyer@1: local button = CreateFrame("Button", nil, frame) farmbuyer@1: button:SetHeight(20) farmbuyer@1: button:SetWidth(24) farmbuyer@1: button:SetPoint("LEFT", editbox.frame, "RIGHT", 0, 0) farmbuyer@1: button:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up") farmbuyer@1: button:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down") farmbuyer@1: button:SetDisabledTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Disabled") farmbuyer@1: button:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight","ADD") farmbuyer@1: button:SetScript("OnClick", Button_OnClick) farmbuyer@1: button:SetScript("OnEnter", Button_OnEnter) farmbuyer@1: button:SetScript("OnLeave", ddEditBox_OnMouseLeave) farmbuyer@1: button.parent = frame farmbuyer@1: farmbuyer@1: local dropdown = CreateFrame("Frame", "AceGUI-3.0EditBoxDropDownMenu"..num, nil, "UIDropDownMenuTemplate") farmbuyer@1: dropdown:Hide() farmbuyer@1: farmbuyer@1: local widget = { farmbuyer@1: editbox = editbox, farmbuyer@1: button = button, farmbuyer@1: dropdown = dropdown, farmbuyer@1: frame = frame, farmbuyer@1: type = Type farmbuyer@1: } farmbuyer@1: for method, func in pairs(methods) do farmbuyer@1: widget[method] = func farmbuyer@1: end farmbuyer@1: editbox.obj, button.obj, dropdown.obj = widget, widget, widget farmbuyer@1: if is_option_control then farmbuyer@1: widget.is_option_control = true farmbuyer@1: widget.SetLabel = optcontrol_SetLabel farmbuyer@1: widget.SetValue = optcontrol_SetValue farmbuyer@1: end farmbuyer@1: farmbuyer@1: return AceGUI:RegisterAsWidget(widget) farmbuyer@1: end farmbuyer@1: farmbuyer@1: AceGUI:RegisterWidgetType (Type, Constructor, Version) farmbuyer@1: farmbuyer@1: AceGUI:RegisterWidgetType (Type.."OptionControl", function() return Constructor(true) end, Version) farmbuyer@1: