diff AceGUIWidget-DoTimerEditBoxDropDown.lua @ 1:822b6ca3ef89

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