view AceGUIWidget-DoTimerEditBoxDropDown.lua @ 156:1e2ee3f52bc8 beta-l10n-2

Stub files to test server-side localization generator. These are NOT LOADED YET.
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Sat, 21 Feb 2015 17:04:00 -0500
parents 6d5fcbdc0590
children
line wrap: on
line source
local Type, Version = "EditBoxDropDown", 5
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 programmer 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' as a key, e.g., 
    values = function()
        local ret = build_real_dropdown_table()
        ret[get_callback] = true  -- assuming "function get_callback (widget, event, text) .... end"
        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

Version 4 was never released.

Version 5 adds the OnDropdownShown callback.


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

OnDropdownShown
   when the down arrow is clicked to display the list


farmbuyer@gmail.com
]]

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")
		button.obj:Fire("OnDropdownShown")
	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 base_SetWidth = AceGUI.WidgetBase.SetWidth
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,
--[=[
	["SetWidth"] = function (self, width)
		print("got",width)
		base_SetWidth(self, width)
		self.frame.width = width + 45
	end,

	["GetWidth"] = function (self)
		return self.frame:GetWidth() + 45
	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()+15)
	--frame.width = frame:GetWidth() + 15
	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)