/branches
local AceGUI = LibStub("AceGUI-3.0") |
do |
local Type = "Spell_EditBox" |
local Version = 3 |
-- I know theres a better way of doing this than this, but not sure for the time being, works fine though! |
local function Constructor() |
return AceGUI:Create("Predictor_Base") |
end |
AceGUI:RegisterWidgetType(Type, Constructor, Version) |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/"> |
<Script file="SpellLoader.lua"/> |
<Script file="EditBox-Core.lua"/> |
<Script file="Spells-EditBox.lua"/> |
<Script file="Casts-EditBox.lua"/> |
<Script file="Auras-EditBox.lua"/> |
<Script file="Player-EditBox.lua"/> |
</Ui> |
-- Based off of AceGUI-3.0 EditBox |
local AceGUI = LibStub("AceGUI-3.0") |
do |
local Type = "Spell_EditBox" |
local Version = 2 |
local PREDICTION_ROWS = 10 |
local totalSpellsLoaded, spellLoader = 0 |
local spells, indexedSpells, visiblePredicters = {}, {}, {} |
-- Defined blew |
local searchSpells |
-- Spells have to gradually be loaded in to prevent the client from lagging, this starts as soon as one widget is shown |
-- as of 3.1 the spellID goes up to ~66,000 which means it'll take around 5 - 10 seconds for it to load them all |
-- Given users have to actually move the mouse, type what they want etc |
-- it should result in them not noticing it does not have all the spell data yet |
local function startLoading() |
if( spellLoader ) then return end |
spellLoader = CreateFrame("Frame") |
spellLoader.timeElapsed = 0 |
spellLoader.totalInvalid = 0 |
spellLoader.index = 0 |
spellLoader:SetScript("OnUpdate", function(self, elapsed) |
self.timeElapsed = self.timeElapsed + elapsed |
if( self.timeElapsed < 0.10 ) then return end |
self.timeElapsed = self.timeElapsed - 0.10 |
-- Too many invalid spells found will assume we found all there is that we can |
if( self.totalInvalid >= 5000 ) then |
self:Hide() |
return |
end |
-- Load as many spells in |
local spellsLoaded = totalSpellsLoaded |
for i=spellLoader.index + 1, spellLoader.index + 500 do |
local name, _, icon = GetSpellInfo(i) |
-- The majority of spells that use the engineer gear icon are actually invalid spells that we can easily ignore |
-- since there are ~12000 not counting duplicate names that use this icon it's worthwhile to filter out these spells |
self.totalInvalid = self.totalInvalid + 1 |
if( name and icon ~= "Interface\\Icons\\Trade_Engineering" ) then |
name = string.lower(name) |
if( not spells[name] ) then |
spells[string.lower(name)] = i |
table.insert(indexedSpells, name) |
totalSpellsLoaded = totalSpellsLoaded + 1 |
self.totalInvalid = 0 |
end |
end |
end |
-- Every ~1 second it will update any visible predicters to make up for the fact that the data is delay loaded |
if( spellLoader.index % 5000 == 0 ) then |
for predicter in pairs(visiblePredicters) do |
searchSpells(predicter, predicter.lastQuery) |
end |
end |
-- Increment and do it all over! |
spellLoader.index = spellLoader.index + 500 |
end) |
end |
-- Search for spells quickly |
searchSpells = function(self, query) |
for _, button in pairs(self.buttons) do button:Hide() end |
local usedButtons = 0 |
for i=1, totalSpellsLoaded do |
local name = indexedSpells[i] |
if( string.match(name, query) ) then |
usedButtons = usedButtons + 1 |
local spellName, _, spellIcon = GetSpellInfo(spells[name]) |
local button = self.buttons[usedButtons] |
button.spellID = spells[name] |
button:SetFormattedText("|T%s:20:20:2:11|t %s", spellIcon, spellName) |
button:Show() |
if( usedButtons ~= self.selectedButton ) then |
button:UnlockHighlight() |
if( GameTooltip:IsOwned(button) ) then |
GameTooltip:Hide() |
end |
end |
-- Ran out of text to suggest :< |
if( usedButtons >= PREDICTION_ROWS ) then break end |
end |
end |
if( usedButtons > 0 ) then |
self:SetHeight(15 + usedButtons * 17) |
self:Show() |
else |
self:Hide() |
end |
self.lastQuery = query |
self.usedButtons = usedButtons |
end |
local function OnAcquire(self) |
self:SetHeight(26) |
self:SetWidth(200) |
self:SetDisabled(false) |
self:SetLabel() |
self.showbutton = true |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.predictFrame:Hide() |
self:SetDisabled(false) |
end |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function EditBox_OnEscapePressed(this) |
this:ClearFocus() |
end |
local function ShowButton(self) |
if( self.lasttext ~= "" ) then |
self.editbox.predictFrame.selectedButton = nil |
searchSpells(self.editbox.predictFrame, "^" .. string.lower(self.lasttext)) |
else |
self.editbox.predictFrame:Hide() |
end |
if( self.showbutton ) then |
self.button:Show() |
self.editbox:SetTextInsets(0,20,3,3) |
end |
end |
local function HideButton(self) |
self.button:Hide() |
self.editbox:SetTextInsets(0,0,3,3) |
self.editbox.predictFrame:Hide() |
end |
local function EditBox_OnEnterPressed(this) |
if( this.predictFrame.selectedButton ) then |
this.predictFrame.buttons[this.predictFrame.selectedButton]:Click() |
this.predictFrame.selectedButton = nil |
return |
end |
local self = this.obj |
local value = this:GetText() |
local cancel = self:Fire("OnEnterPressed", value) |
if( not cancel ) then |
HideButton(self) |
end |
-- Reactivate the cursor, odds are if you're adding auras you're adding multiple auras |
self.editbox:SetFocus() |
end |
local function Button_OnClick(this) |
local editbox = this.obj.editbox |
editbox:ClearFocus() |
EditBox_OnEnterPressed(editbox) |
end |
local function Predicter_OnHide(self) |
-- Allow users to use arrows to go back and forth again without the fix |
self.editbox:SetAltArrowKeyMode(false) |
visiblePredicters[self] = nil |
ClearOverrideBindings(self) |
end |
local function Predicter_OnShow(self) |
-- I'm pretty sure this is completely against what you are supposed to actually do :> |
visiblePredicters[self] = true |
-- User doesn't need arrow keys, and by doing this the override binding for up/down arrows will work properly |
self.editbox:SetAltArrowKeyMode(true) |
SetOverrideBindingClick(self, true, "DOWN", self:GetName(), 1) |
SetOverrideBindingClick(self, true, "UP", self:GetName(), -1) |
SetOverrideBindingClick(self, true, "LEFT", self:GetName(), "LEFT") |
SetOverrideBindingClick(self, true, "RIGHT", self:GetName(), "RIGHT") |
end |
-- When using SetAltArrowKeyMode the ability to move the cursor with left and right is disabled, this reenables that |
-- since the cursor position automatically can't go below 0, this is a quick and easy fix |
local function EditBox_FixCursorPosition(self, direction) |
self:SetCursorPosition(self:GetCursorPosition() + (direction == "RIGHT" and 1 or -1)) |
end |
local function EditBox_OnReceiveDrag(this) |
local self = this.obj |
local type, id, info = GetCursorInfo() |
if( type == "spell" ) then |
local name = GetSpellName(id, info) |
self:SetText(name) |
self:Fire("OnEnterPressed" ,name) |
ClearCursor() |
end |
HideButton(self) |
AceGUI:ClearFocus() |
end |
local function EditBox_OnTextChanged(this) |
local self = this.obj |
local value = this:GetText() |
if( value ~= self.lasttext ) then |
self:Fire("OnTextChanged", value) |
self.lasttext = value |
ShowButton(self) |
end |
end |
local function EditBox_OnEditFocusLost(self) |
Predicter_OnHide(self.predictFrame) |
end |
local function EditBox_OnEditFocusGained(self) |
if( self.predictFrame:IsVisible() ) then |
Predicter_OnShow(self.predictFrame) |
end |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if( disabled ) then |
self.editbox:EnableMouse(false) |
self.editbox:ClearFocus() |
self.editbox:SetTextColor(0.5, 0.5, 0.5) |
self.label:SetTextColor(0.5, 0.5, 0.5) |
else |
self.editbox:EnableMouse(true) |
self.editbox:SetTextColor(1, 1, 1) |
self.label:SetTextColor(1, 0.82, 0) |
end |
end |
local function SetText(self, text, cursor) |
self.lasttext = text or "" |
self.editbox:SetText(self.lasttext) |
self.editbox:SetCursorPosition(cursor or 0) |
HideButton(self) |
end |
local function SetLabel(self, text) |
if( text and text ~= "" ) then |
self.label:SetText(text) |
self.label:Show() |
self.editbox:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 7, -18) |
self:SetHeight(44) |
self.alignoffset = 30 |
else |
self.label:SetText("") |
self.label:Hide() |
self.editbox:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 7, 0) |
self:SetHeight(26) |
self.alignoffset = 12 |
end |
end |
local function Predicter_OnMouseDown(self, direction) |
if( direction == "LEFT" or direction == "RIGHT" ) then |
EditBox_FixCursorPosition(self.editbox, direction) |
return |
end |
self.selectedButton = (self.selectedButton or 0) + direction |
if( self.selectedButton > self.usedButtons ) then |
self.selectedButton = 1 |
elseif( self.selectedButton <= 0 ) then |
self.selectedButton = self.usedButtons |
end |
for i=1, self.usedButtons do |
local button = self.buttons[i] |
if( i == self.selectedButton ) then |
button:LockHighlight() |
GameTooltip:SetOwner(button, "ANCHOR_BOTTOMRIGHT") |
GameTooltip:SetHyperlink("spell:" .. button.spellID) |
else |
button:UnlockHighlight() |
if( GameTooltip:IsOwned(button) ) then |
GameTooltip:Hide() |
end |
end |
end |
end |
local function Spell_OnClick(self) |
local name = GetSpellInfo(self.spellID) |
SetText(self.parent.obj, name, string.len(name)) |
self.parent.obj:Fire("OnEnterPressed", name) |
end |
local function Spell_OnEnter(self) |
self.parent.selectedButton = nil |
self:LockHighlight() |
GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT") |
GameTooltip:SetHyperlink("spell:" .. self.spellID) |
end |
local function Spell_OnLeave(self) |
self:UnlockHighlight() |
GameTooltip:Hide() |
end |
local predicterBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", |
edgeSize = 26, |
insets = {left = 9, right = 9, top = 9, bottom = 9}, |
} |
local function Constructor() |
local num = AceGUI:GetNextWidgetNum(Type) |
local frame = CreateFrame("Frame", nil, UIParent) |
local editbox = CreateFrame("EditBox", "AceGUI30SpellEditBox" .. num, frame, "InputBoxTemplate") |
-- Don't feel like looking up the specific callbacks for when a widget resizes, so going to be creative with SetPoint instead! |
local predictFrame = CreateFrame("Frame", "AceGUI30SpellEditBox" .. num .. "Predicter", UIParent) |
predictFrame:SetBackdrop(predicterBackdrop) |
predictFrame:SetBackdropColor(0, 0, 0, 0.85) |
predictFrame:SetWidth(1) |
predictFrame:SetHeight(150) |
predictFrame:SetPoint("TOPLEFT", editbox, "BOTTOMLEFT", -6, 0) |
predictFrame:SetPoint("TOPRIGHT", editbox, "BOTTOMRIGHT", 0, 0) |
predictFrame:SetFrameStrata("TOOLTIP") |
predictFrame:Hide() |
predictFrame.buttons = {} |
for i=1, PREDICTION_ROWS do |
local button = CreateFrame("Button", nil, predictFrame) |
button:SetHeight(17) |
button:SetWidth(1) |
button:SetPushedTextOffset(-2, 0) |
button:SetScript("OnClick", Spell_OnClick) |
button:SetScript("OnEnter", Spell_OnEnter) |
button:SetScript("OnLeave", Spell_OnLeave) |
button.parent = predictFrame |
button.editbox = editbox |
button:Hide() |
if( i > 1 ) then |
button:SetPoint("TOPLEFT", predictFrame.buttons[i - 1], "BOTTOMLEFT", 0, 0) |
button:SetPoint("TOPRIGHT", predictFrame.buttons[i - 1], "BOTTOMRIGHT", 0, 0) |
else |
button:SetPoint("TOPLEFT", predictFrame, 8, -8) |
button:SetPoint("TOPRIGHT", predictFrame, -7, 0) |
end |
-- Create the actual text |
local text = button:CreateFontString(nil, "ARTWORK", "GameFontNormal") |
text:SetHeight(1) |
text:SetWidth(1) |
text:SetJustifyH("LEFT") |
text:SetAllPoints(button) |
button:SetFontString(text) |
-- Setup the highlighting |
local texture = button:CreateTexture(nil, "ARTWORK") |
texture:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
texture:ClearAllPoints() |
texture:SetPoint("TOPLEFT", button, 0, -2) |
texture:SetPoint("BOTTOMRIGHT", button, 5, 2) |
texture:SetAlpha(0.70) |
button:SetHighlightTexture(texture) |
button:SetHighlightFontObject(GameFontHighlight) |
button:SetNormalFontObject(GameFontNormal) |
table.insert(predictFrame.buttons, button) |
end |
local self = {} |
self.type = Type |
self.num = num |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetDisabled = SetDisabled |
self.SetText = SetText |
self.SetLabel = SetLabel |
frame.obj = self |
self.frame = frame |
editbox.obj = self |
editbox.predictFrame = predictFrame |
self.editbox = editbox |
self.predictFrame = predictFrame |
predictFrame.editbox = editbox |
predictFrame.obj = self |
self.alignoffset = 30 |
frame:SetHeight(44) |
frame:SetWidth(200) |
-- Despite the fact that wowprogramming/wowwiki say EditBoxes have OnKeyUp/OnKeyDown thats not actually true |
-- so doing some trickery with bindings and such to make navigation work |
predictFrame:SetScript("OnMouseDown", Predicter_OnMouseDown) |
predictFrame:SetScript("OnHide", Predicter_OnHide) |
predictFrame:SetScript("OnShow", Predicter_OnShow) |
editbox:SetScript("OnShow", startLoading) |
editbox:SetScript("OnEnter", Control_OnEnter) |
editbox:SetScript("OnLeave", Control_OnLeave) |
editbox:SetAutoFocus(false) |
editbox:SetFontObject(ChatFontNormal) |
editbox:SetScript("OnEscapePressed", EditBox_OnEscapePressed) |
editbox:SetScript("OnEnterPressed", EditBox_OnEnterPressed) |
editbox:SetScript("OnTextChanged", EditBox_OnTextChanged) |
editbox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag) |
editbox:SetScript("OnMouseDown", EditBox_OnReceiveDrag) |
editbox:SetScript("OnEditFocusGained", EditBox_OnEditFocusGained) |
editbox:SetScript("OnEditFocusLost", EditBox_OnEditFocusLost) |
editbox:SetTextInsets(0, 0, 3, 3) |
editbox:SetMaxLetters(256) |
editbox:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 6, 0) |
editbox:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0) |
editbox:SetHeight(19) |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
label:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, -2) |
label:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, -2) |
label:SetJustifyH("LEFT") |
label:SetHeight(18) |
self.label = label |
local button = CreateFrame("Button",nil,editbox,"UIPanelButtonTemplate") |
button:SetWidth(40) |
button:SetHeight(20) |
button:SetPoint("RIGHT", editbox, "RIGHT", -2, 0) |
button:SetText(OKAY) |
button:SetScript("OnClick", Button_OnClick) |
button:Hide() |
self.button = button |
button.obj = self |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type, Constructor, Version) |
end |
-- This is basically the main portion of the predictor, the other files handle |
local AceGUI = LibStub("AceGUI-3.0") |
do |
local Type = "Predictor_Base" |
local Version = 1 |
local PREDICTOR_ROWS = 10 |
local SpellData = LibStub("AceGUI-3.0-SpellLoader") |
local tooltip |
local alreadyAdded = {} |
local predictorBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", |
edgeSize = 26, |
insets = {left = 9, right = 9, top = 9, bottom = 9}, |
} |
local function OnAcquire(self) |
self:SetHeight(26) |
self:SetWidth(200) |
self:SetDisabled(false) |
self:SetLabel() |
self.showButton = true |
SpellData:RegisterPredictor(self.predictFrame) |
SpellData:StartLoading() |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.predictFrame:Hide() |
self.spellFilter = nil |
self:SetDisabled(false) |
SpellData:UnregisterPredictor(self.predictFrame) |
end |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function Predictor_Query(self) |
for _, button in pairs(self.buttons) do button:Hide() end |
for k in pairs(alreadyAdded) do alreadyAdded[k] = nil end |
local query = "^" .. string.lower(self.obj.editBox:GetText()) |
local activeButtons = 0 |
for spellID, name in pairs(SpellData.spellList) do |
if( not alreadyAdded[name] and string.match(name, query) and ( not self.obj.spellFilter or self.obj.spellFilter(self.obj, spellID) ) ) then |
activeButtons = activeButtons + 1 |
local button = self.buttons[activeButtons] |
local spellName, spellRank, spellIcon = GetSpellInfo(spellID) |
if( self.obj.useRanks and spellRank and spellRank ~= "" ) then |
button:SetFormattedText("|T%s:20:20:2:11|t %s (%s)", spellIcon, spellName, spellRank) |
else |
button:SetFormattedText("|T%s:20:20:2:11|t %s", spellIcon, spellName) |
end |
if( not self.obj.useRanks ) then |
alreadyAdded[name] = true |
end |
button.spellID = spellID |
button:Show() |
-- Highlight if needed |
if( activeButtons ~= self.selectedButton ) then |
button:UnlockHighlight() |
if( GameTooltip:IsOwned(button) ) then |
GameTooltip:Hide() |
end |
end |
-- Ran out of text to suggest :< |
if( activeButtons >= PREDICTOR_ROWS ) then break end |
end |
end |
if( activeButtons > 0 ) then |
self:SetHeight(15 + activeButtons * 17) |
self:Show() |
else |
self:Hide() |
end |
self.activeButtons = activeButtons |
end |
local function ShowButton(self) |
if( self.lastText ~= "" ) then |
self.predictFrame.selectedButton = nil |
Predictor_Query(self.predictFrame) |
else |
self.predictFrame:Hide() |
end |
if( self.showButton ) then |
self.button:Show() |
self.editBox:SetTextInsets(0, 20, 3, 3) |
end |
end |
local function HideButton(self) |
self.button:Hide() |
self.editBox:SetTextInsets(0, 0, 3, 3) |
self.predictFrame.selectedButton = nil |
self.predictFrame:Hide() |
end |
local function Predictor_OnHide(self) |
-- Allow users to use arrows to go back and forth again without the fix |
self.obj.editBox:SetAltArrowKeyMode(false) |
-- Make sure the tooltip isn't kept open if one of the buttons was using it |
for _, button in pairs(self.buttons) do |
if( GameTooltip:IsOwned(button) ) then |
GameTooltip:Hide() |
end |
end |
-- Reset all bindings set on this predictor |
ClearOverrideBindings(self) |
end |
local function Predictor_OnShow(self) |
-- If the user is using an edit box in a configuration, they will live without arrow keys while the predictor |
-- is opened, this also is the only way of getting up/down arrow for browsing the predictor to work. |
self.obj.editBox:SetAltArrowKeyMode(true) |
local name = self:GetName() |
SetOverrideBindingClick(self, true, "DOWN", name, 1) |
SetOverrideBindingClick(self, true, "UP", name, -1) |
SetOverrideBindingClick(self, true, "LEFT", name, "LEFT") |
SetOverrideBindingClick(self, true, "RIGHT", name, "RIGHT") |
end |
local function EditBox_OnEnterPressed(this) |
local self = this.obj |
-- Something is selected in the predictor, use that value instead of whatever is in the input box |
if( self.predictFrame.selectedButton ) then |
self.predictFrame.buttons[self.predictFrame.selectedButton]:Click() |
return |
end |
local cancel = self:Fire("OnEnterPressed", this:GetText()) |
if( not cancel ) then |
HideButton(self) |
end |
-- Reactive the cursor, odds are if someone is adding spells they are adding more than one |
-- and if they aren't, it can't hurt anyway. |
self.editBox:SetFocus() |
end |
local function EditBox_OnEscapePressed(this) |
this:ClearFocus() |
end |
-- When using SetAltArrowKeyMode the ability to move the cursor with left and right arrows is disabled |
-- this reenables that so the user doesn't notice anything wrong |
local function EditBox_FixCursorPosition(self, direction) |
self:SetCursorPosition(self:GetCursorPosition() + (direction == "RIGHT" and 1 or -1)) |
end |
local function EditBox_OnReceiveDrag(this) |
local self = this.obj |
local type, id, info = GetCursorInfo() |
if( type == "spell" ) then |
local name, rank = GetSpellName(id, info) |
if( self.useRanks and rank and rank ~= "" ) then |
name = string.format("%s (%s)", name, rank) |
end |
self:SetText(name) |
self:Fire("OnEnterPressed", name) |
ClearCursor() |
end |
HideButton(self) |
AceGUI:ClearFocus() |
end |
local function EditBox_OnTextChanged(this) |
local self = this.obj |
local value = this:GetText() |
if( value ~= self.lastText ) then |
self:Fire("OnTextChanged", value) |
self.lastText = value |
ShowButton(self) |
end |
end |
local function EditBox_OnEditFocusLost(self) |
Predictor_OnHide(self.obj.predictFrame) |
end |
local function EditBox_OnEditFocusGained(self) |
if( self.obj.predictFrame:IsVisible() ) then |
Predictor_OnShow(self.obj.predictFrame) |
end |
end |
local function Button_OnClick(this) |
EditBox_OnEnterPressed(this.obj.editBox) |
end |
-- API calls |
local function SetUseRanks(self, enabled) |
self.useRanks = enabled |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if( disabled ) then |
self.editBox:EnableMouse(false) |
self.editBox:ClearFocus() |
self.editBox:SetTextColor(0.5, 0.5, 0.5) |
self.label:SetTextColor(0.5, 0.5, 0.5) |
else |
self.editBox:EnableMouse(true) |
self.editBox:SetTextColor(1, 1, 1) |
self.label:SetTextColor(1, 0.82, 0) |
end |
end |
local function SetText(self, text, cursor) |
self.lastText = text or "" |
self.editBox:SetText(self.lastText) |
self.editBox:SetCursorPosition(cursor or 0) |
HideButton(self) |
end |
local function SetLabel(self, text) |
if( text and text ~= "" ) then |
self.label:SetText(text) |
self.label:Show() |
self.editBox:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 7, -18) |
self:SetHeight(44) |
self.alignoffset = 30 |
else |
self.label:SetText("") |
self.label:Hide() |
self.editBox:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 7, 0) |
self:SetHeight(26) |
self.alignoffset = 12 |
end |
end |
local function Predictor_OnMouseDown(self, direction) |
-- Fix the cursor positioning if left or right arrow key was used |
if( direction == "LEFT" or direction == "RIGHT" ) then |
EditBox_FixCursorPosition(self.editBox, direction) |
return |
end |
self.selectedButton = (self.selectedButton or 0) + direction |
if( self.selectedButton > self.activeButtons ) then |
self.selectedButton = 1 |
elseif( self.selectedButton <= 0 ) then |
self.selectedButton = self.activeButtons |
end |
-- Figure out what to highlight and show the spell tooltip while we're at it |
for i=1, self.activeButtons do |
local button = self.buttons[i] |
if( i == self.selectedButton ) then |
button:LockHighlight() |
GameTooltip:SetOwner(button, "ANCHOR_BOTTOMRIGHT", 3) |
GameTooltip:SetHyperlink("spell:" .. button.spellID) |
else |
button:UnlockHighlight() |
if( GameTooltip:IsOwned(button) ) then |
GameTooltip:Hide() |
end |
end |
end |
end |
local function Spell_OnClick(self) |
local name, rank = GetSpellInfo(self.spellID) |
if( self.useRanks and rank and rank ~= "" ) then |
name = string.format("%s (%s)", name, rank) |
end |
SetText(self.parent.obj, name, string.len(name)) |
self.parent.selectedButton = nil |
self.parent.obj:Fire("OnEnterPressed", name) |
end |
local function Spell_OnEnter(self) |
self.parent.selectedButton = nil |
self:LockHighlight() |
GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT", 3) |
GameTooltip:SetHyperlink("spell:" .. self.spellID) |
end |
local function Spell_OnLeave(self) |
self:UnlockHighlight() |
GameTooltip:Hide() |
end |
local function Constructor() |
local num = AceGUI:GetNextWidgetNum(Type) |
local frame = CreateFrame("Frame", nil, UIParent) |
local editBox = CreateFrame("EditBox", "AceGUI30SpellEditBox" .. num, frame, "InputBoxTemplate") |
-- Don't feel like looking up the specific callbacks for when a widget resizes, so going to be creative with SetPoint instead! |
local predictFrame = CreateFrame("Frame", "AceGUI30SpellEditBox" .. num .. "Predictor", UIParent) |
predictFrame:SetBackdrop(predictorBackdrop) |
predictFrame:SetBackdropColor(0, 0, 0, 0.85) |
predictFrame:SetWidth(1) |
predictFrame:SetHeight(150) |
predictFrame:SetPoint("TOPLEFT", editBox, "BOTTOMLEFT", -6, 0) |
predictFrame:SetPoint("TOPRIGHT", editBox, "BOTTOMRIGHT", 0, 0) |
predictFrame:SetFrameStrata("TOOLTIP") |
predictFrame.buttons = {} |
predictFrame.Query = Predictor_Query |
predictFrame:Hide() |
-- Create the mass of predictor rows |
for i=1, PREDICTOR_ROWS do |
local button = CreateFrame("Button", nil, predictFrame) |
button:SetHeight(17) |
button:SetWidth(1) |
button:SetPushedTextOffset(-2, 0) |
button:SetScript("OnClick", Spell_OnClick) |
button:SetScript("OnEnter", Spell_OnEnter) |
button:SetScript("OnLeave", Spell_OnLeave) |
button.parent = predictFrame |
button.editBox = editBox |
button:Hide() |
if( i > 1 ) then |
button:SetPoint("TOPLEFT", predictFrame.buttons[i - 1], "BOTTOMLEFT", 0, 0) |
button:SetPoint("TOPRIGHT", predictFrame.buttons[i - 1], "BOTTOMRIGHT", 0, 0) |
else |
button:SetPoint("TOPLEFT", predictFrame, 8, -8) |
button:SetPoint("TOPRIGHT", predictFrame, -7, 0) |
end |
-- Create the actual text |
local text = button:CreateFontString(nil, "ARTWORK", "GameFontNormal") |
text:SetHeight(1) |
text:SetWidth(1) |
text:SetJustifyH("LEFT") |
text:SetAllPoints(button) |
button:SetFontString(text) |
-- Setup the highlighting |
local texture = button:CreateTexture(nil, "ARTWORK") |
texture:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
texture:ClearAllPoints() |
texture:SetPoint("TOPLEFT", button, 0, -2) |
texture:SetPoint("BOTTOMRIGHT", button, 5, 2) |
texture:SetAlpha(0.70) |
button:SetHighlightTexture(texture) |
button:SetHighlightFontObject(GameFontHighlight) |
button:SetNormalFontObject(GameFontNormal) |
table.insert(predictFrame.buttons, button) |
end |
-- Set the main info things for this thingy |
local self = {} |
self.type = Type |
self.num = num |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetDisabled = SetDisabled |
self.SetText = SetText |
self.SetLabel = SetLabel |
self.frame = frame |
self.predictFrame = predictFrame |
self.editBox = editBox |
self.alignoffset = 30 |
frame:SetHeight(44) |
frame:SetWidth(200) |
frame.obj = self |
editBox.obj = self |
predictFrame.obj = self |
-- Purely meant for a single tooltip for doing scanning |
if( not tooltip ) then |
tooltip = CreateFrame("GameTooltip") |
tooltip:SetOwner(UIParent, "ANCHOR_NONE") |
for i=1, 6 do |
tooltip["TextLeft" .. i] = tooltip:CreateFontString() |
tooltip["TextRight" .. i] = tooltip:CreateFontString() |
tooltip:AddFontStrings(tooltip["TextLeft" .. i], tooltip["TextRight" .. i]) |
end |
end |
self.tooltip = tooltip |
-- EditBoxes override the OnKeyUp/OnKeyDown events so that they can function, meaning in order to make up and down |
-- arrow navigation of the menu work, I have to do some trickery with temporary bindings. |
predictFrame:SetScript("OnMouseDown", Predictor_OnMouseDown) |
predictFrame:SetScript("OnHide", Predictor_OnHide) |
predictFrame:SetScript("OnShow", Predictor_OnShow) |
editBox:SetScript("OnEnter", Control_OnEnter) |
editBox:SetScript("OnLeave", Control_OnLeave) |
editBox:SetAutoFocus(false) |
editBox:SetFontObject(ChatFontNormal) |
editBox:SetScript("OnEscapePressed", EditBox_OnEscapePressed) |
editBox:SetScript("OnEnterPressed", EditBox_OnEnterPressed) |
editBox:SetScript("OnTextChanged", EditBox_OnTextChanged) |
editBox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag) |
editBox:SetScript("OnMouseDown", EditBox_OnReceiveDrag) |
editBox:SetScript("OnEditFocusGained", EditBox_OnEditFocusGained) |
editBox:SetScript("OnEditFocusLost", EditBox_OnEditFocusLost) |
editBox:SetTextInsets(0, 0, 3, 3) |
editBox:SetMaxLetters(256) |
editBox:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 6, 0) |
editBox:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0) |
editBox:SetHeight(19) |
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") |
label:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, -2) |
label:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, -2) |
label:SetJustifyH("LEFT") |
label:SetHeight(18) |
self.label = label |
local button = CreateFrame("Button", nil, editBox, "UIPanelButtonTemplate") |
button:SetPoint("RIGHT", editBox, "RIGHT", -2, 0) |
button:SetScript("OnClick", Button_OnClick) |
button:SetWidth(40) |
button:SetHeight(20) |
button:SetText(OKAY) |
button:Hide() |
self.button = button |
button.obj = self |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type, Constructor, Version) |
end |
local major = "AceGUI-3.0-SpellLoader" |
local minor = 1 |
local SpellLoader = LibStub:NewLibrary(major, minor) |
if( not SpellLoader ) then return end |
SpellLoader.predictors = SpellLoader.predictors or {} |
SpellLoader.spellList = SpellLoader.spellList or {} |
SpellLoader.spellsLoaded = SpellLoader.spellsLoaded or 0 |
local SPELLS_PER_RUN = 500 |
local TIMER_THROTTLE = 0.10 |
local spells, predictors = SpellLoader.spellList, SpellLoader.predictors |
function SpellLoader:RegisterPredictor(frame) |
self.predictors[frame] = true |
end |
function SpellLoader:UnregisterPredictor(frame) |
self.predictors[frame] = nil |
end |
function SpellLoader:StartLoading() |
if( self.loader ) then return end |
local blacklist = { |
["Interface\\Icons\\Trade_Alchemy"] = true, |
["Interface\\Icons\\Trade_BlackSmithing"] = true, |
["Interface\\Icons\\Trade_BrewPoison"] = true, |
["Interface\\Icons\\Trade_Engineering"] = true, |
["Interface\\Icons\\Trade_Engraving"] = true, |
["Interface\\Icons\\Trade_Fishing"] = true, |
["Interface\\Icons\\Trade_Herbalism"] = true, |
["Interface\\Icons\\Trade_LeatherWorking"] = true, |
["Interface\\Icons\\Trade_Mining"] = true, |
["Interface\\Icons\\Trade_Tailoring"] = true, |
["Interface\\Icons\\Temp"] = true, |
} |
local timeElapsed, totalInvalid, currentIndex = 0, 0, 0 |
self.loader = CreateFrame("Frame") |
self.loader:SetScript("OnUpdate", function(self, elapsed) |
timeElapsed = timeElapsed + elapsed |
if( timeElapsed < TIMER_THROTTLE ) then return end |
timeElapsed = timeElapsed - TIMER_THROTTLE |
-- 5,000 invalid spells in a row means it's a safe assumption that there are no more spells to query |
if( totalInvalid >= 5000 ) then |
self:Hide() |
return |
end |
-- Load as many spells in |
for spellID=currentIndex + 1, currentIndex + SPELLS_PER_RUN do |
local name, rank, icon = GetSpellInfo(spellID) |
-- Pretty much every profession spell uses Trade_* and 99% of the random spells use the Trade_Engineering icon |
-- we can safely blacklist any of these spells as they are not needed. Can get away with this because things like |
-- Alchemy use two icons, the Trade_* for the actual crafted spell and a different icon for the actual buff |
-- Passive spells have no use as well, since they are well passive and can't actually be used |
if( name and not blacklist[icon] and rank ~= SPELL_PASSIVE ) then |
name = string.lower(name) |
SpellLoader.spellsLoaded = SpellLoader.spellsLoaded + 1 |
spells[spellID] = string.lower(name) |
totalInvalid = 0 |
else |
totalInvalid = totalInvalid + 1 |
end |
end |
-- Every ~1 second it will update any visible predictors to make up for the fact that the data is delay loaded |
if( currentIndex % 5000 == 0 ) then |
for predictor in pairs(predictors) do |
if( predictor:IsVisible() ) then |
predictor:Query() |
end |
end |
end |
-- Increment and do it all over! |
currentIndex = currentIndex + SPELLS_PER_RUN |
end) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
do |
local Type = "Aura_EditBox" |
local Version = 1 |
local filterCache = {} |
-- Tooltip scanning every single spell while it's loading would be not be that fun. Scanning spells as we search them (and get valid results) |
-- is a better solution since the results can be cached too. |
local function spellFilter(self, spellID) |
if( filterCache[spellID] ~= nil ) then return filterCache[spellID] end |
-- Very very few auras are over 100 yard range, and those are generally boss spells should be able to get away with this |
if( select(9, GetSpellInfo(spellID)) > 100 ) then |
filterCache[spellID] = true |
return false |
end |
-- We look for a description tag, 99% of auras have a description tag indicating what they are |
-- so we don't find one, then it's likely a safe assumption that it is not an aura |
self.tooltip:SetHyperlink("spell:" .. spellID) |
for i=1, self.tooltip:NumLines() do |
local text = self.tooltip["TextLeft" .. i] |
if( text ) then |
local r, g, b = text:GetTextColor() |
r = math.floor(r + 0.10) |
g = math.floor(g + 0.10) |
b = math.floor(b + 0.10) |
-- Gold first text, it's a profession link |
if( i == 1 and ( r ~= 1 or g ~= 1 or g ~= 1 ) ) then |
filterCache[spellID] = false |
return false |
-- Gold for anything else and it should be a valid aura |
elseif( r ~= 1 or g ~= 1 or b ~= 1 ) then |
filterCache[spellID] = true |
return true |
end |
end |
end |
filterCache[spellID] = false |
return false |
end |
-- I know theres a better way of doing this than this, but not sure for the time being, works fine though! |
local function Constructor() |
local self = AceGUI:Create("Predictor_Base") |
self.spellFilter = spellFilter |
return self |
end |
AceGUI:RegisterWidgetType(Type, Constructor, Version) |
end |
## Interface: 30200 |
## Title: Lib: AceGUI-3.0-Spell-EditBox |
## Notes: Adds predicted lists for spells while the user types |
## Author: Shadowed |
## X-Category: Library |
## OptionalDeps: Ace3 |
## X-Curse-Packaged-Version: r20090807071246 |
## X-Curse-Project-Name: AceGUI-3.0-Spell-EditBox |
## X-Curse-Project-ID: ace-gui-3-0-spell-editbox |
## X-Curse-Repository-ID: wow/ace-gui-3-0-spell-editbox/mainline |
AceGUI-3.0-Spell-EditBox.xml |
## test.lua |
local AceGUI = LibStub("AceGUI-3.0") |
do |
local Type = "Player_EditBox" |
local Version = 1 |
local playerSpells = {} |
local frame |
local function spellFilter(self, spellID) |
return playerSpells[spellID] |
end |
local function loadPlayerSpells(self) |
table.wipe(playerSpells) |
for tab=2, GetNumSpellTabs() do |
local offset = select(3, GetSpellTabInfo(tab)) |
for i=1, offset do |
self.tooltip:SetSpell(i + offset, tab) |
local spellName, _, spellID = self.tooltip:GetSpell() |
if( spellName ) then |
playerSpells[spellID] = true |
end |
end |
end |
end |
-- I know theres a better way of doing this than this, but not sure for the time being, works fine though! |
local function Constructor() |
local self = AceGUI:Create("Predictor_Base") |
self.spellFilter = spellFilter |
if( not frame ) then |
frame = CreateFrame("Frame") |
frame:RegisterEvent("SPELLS_CHANGED") |
frame:SetScript("OnEvent", loadPlayerSpells) |
frame.tooltip = self.tooltip |
loadPlayerSpells(frame) |
end |
return self |
end |
AceGUI:RegisterWidgetType(Type, Constructor, Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
do |
local Type = "Cast_EditBox" |
local Version = 1 |
local filterCache = {} |
local function spellFilter(self, spellID) |
if( filterCache[spellID] ~= nil ) then return filterCache[spellID] end |
-- Scan first line to figure out if it's a profession |
self.tooltip:SetHyperlink("spell:" .. spellID) |
local r, g, b = self.tooltip.TextLeft1:GetTextColor() |
r = math.floor(r + 0.10) |
g = math.floor(g + 0.10) |
b = math.floor(b + 0.10) |
if( r ~= 1 or g ~= 1 or b ~= 1 ) then |
filterCache[spellID] = false |
return false |
end |
-- Hide all spells without a cast time |
filterCache[spellID] = select(7, GetSpellInfo(spellID)) > 0 |
return filterCache[spellID] |
end |
-- I know theres a better way of doing this than this, but not sure for the time being, works fine though! |
local function Constructor() |
local self = AceGUI:Create("Predictor_Base") |
self.spellFilter = spellFilter |
return self |
end |
AceGUI:RegisterWidgetType(Type, Constructor, Version) |
end |