# HG changeset patch
# User Flick Current Version: 0.33 (alpha) Current Version: 0.34 (alpha) ReAction is a replacement for the default Blizzard action bars. It allows you
redefine your action button layout any way you like.ReAction
AddOn for World of Warcraft
-
- Released: 02 Mar 2007
- WoW Version Compatibility/TOC: 2.0.8 / TOC 20003
+ Released: 09 Mar 2007
+ WoW Version Compatibility: v2.0.10 / TOC 20003The Basics
To install ReAction, drag the ReAction folder to World of Warcraft/Interface/AddOns. - Exit World of Warcraft if it's running, then restart.
+To install ReAction, drag the ReAction folder to your World of Warcraft/Interface/AddOns + folder. Exit World of Warcraft if it's running, then restart.
When you first enter World of Warcraft with ReAction installed, it will look very similar to the default Blizzard UI, depending on what other AddOns are - installed. Any keybindings you have made to the action bars, however, are not - in effect. Also, only the main menu bar is shown by default. Other bars can + installed. However, only the main menu bar is shown by default. Other bars can be created as desired.
If you have FuBar installed, on the left side you should see the ReAction plugin icon and label. If not, there should be a button on your minimap. In either @@ -62,6 +61,8 @@
(Technically it's 4 modes, because Config and Keybinding modes can both be + active at the same time).
You can toggle Configuration mode by shift-clicking the ReAction Control Button and toggle Keybinding mode by alt-clicking the button. Global configuration options are also available by right-clicking the ReAction button. There are @@ -88,24 +89,34 @@ mode by alt-clicking the ReAction Control Button (or choosing 'keybinding mode' from the right-click menu). ReAction button keybindings are not available from the standard keybindings menu! You must use the ReAction keybinding - interface. They're even greyed out in the keybinding menu to remind you.
+ interface. They're even greyed out in the keybinding menu with a textual reminder, + so there's no excuse!Like configuration mode, normal button operation is disabled while in keybinding - mode (though again, any hotkeys you have assigned will work).
-With the keybinding interface out, you can mouseover any ReAction button to - see what its current keybinding is. You can set the keybinding on a button by - clicking the button, then pressing the key. To clear a keybinding from a ReAction - button, shift-click the button. Tooltips will remind you of this interface. - You can also right-click/shift-right-click to set/clear a right-click hotkey, - which will simulate right-clicking the button (useful for complex macros, typically - to save screen real estate). If you've set up bars with multiple pages, you - can also bind a key to the page up/down buttons on a per-bar basis. Just make - sure the buttons are showing (they can be hidden later) and assign the keybinding - like any other ReAction button.
-Keybindings are saved on a per-character basis. Keybinding mode is not available + mode (though again, any hotkeys you have assigned will work). With the keybinding + interface out, you can mouseover any ReAction button to see what its current + keybinding is. You can set the keybinding on a button by clicking the button, + then pressing the key. To clear a keybinding from a ReAction button, shift-click + the button. Tooltips will remind you of this interface. You can also right-click/shift-right-click + to set/clear a right-click hotkey, which will simulate right-clicking the button + (useful for complex macro actions, typically to save screen real estate). If + you've set up bars with multiple pages, you can also bind a key to the page + up/down buttons on a per-bar basis. Just make sure the buttons are showing (they + can be hidden later) and assign the keybinding like any other ReAction button.
+Keybindings are saved on a per-profile basis. Keybinding mode is not available if you are in combat, and is automatically cancelled if you enter combat. When - you first log in with ReAction, it steals the keybindings from the default main - action bar (buttons 1-12) and assigns them to the corresponding buttons on the - default ReAction bar.
+ you first log in with ReAction, it steals the current keybindings from the default + main action bar (buttons 1-12) and assigns them to the corresponding buttons + on the default ReAction bar. This means that if you install ReAction, run with + it, and then disable it, your main bar will no longer have any keybindings and + they'll need to be reset. If you later enable ReAction again, it will remember + its own keybindings. +If a profile assigns a keybinding to a ReAction button, switching away from + that profile will clear the binding but will not restore the binding + to its previous state. If the new profile assigns that binding then, of course, + it will be in use. What this means is that any 'standard' keybinding action + that's bound to a key will become unbound when switching to a profile that uses + the key, and will have to be manually rebound later if you switch away from + that profile.
Remember to click the 'Save' button to save your keybindings! If you do not, they will automatically revert to the previous bindings when exiting keybinding mode. This can be useful for "trying out" certain layouts @@ -121,12 +132,10 @@ a new skill that doesn't fit on your current layout.
Version 0.34:
+Version 0.33:
Thanks to the authors of certain action bar mods that came before ReAction: + Bartender3, Bongos, FlexBar 2, and Trinity Bars. While I didn't steal any code + directly from you guys, your addons provided inspiration for various features + and options. Hopefully ReAction differentiates itself sufficiently that it's + not seen as simply a clone.
Finally, thanks to my alpha testers and stalwart adventuring companions: Deor, - Nogrim, Sorabel, and Yngvi.
+ Nogrim, Sorabel, and Yngvi (or, more to the point, their alts :-P).ReAction is distributed under the MIT license.
ReAction Copyright © 2007 Ryan Findley.
diff -r 88df7235ad8b -r 2735edcf9ab7 ReAction.toc --- a/ReAction.toc Tue Mar 20 21:38:47 2007 +0000 +++ b/ReAction.toc Wed Mar 21 00:13:27 2007 +0000 @@ -4,7 +4,7 @@ ## DefaultState: enabled ## LoadOnDemand: 0 ## Author: Flick -## Version: 0.33 +## Version: 0.34 ## X-Description: An action bar and button layout tool ## X-Category: Action Bars ## SavedVariables: ReActionDB diff -r 88df7235ad8b -r 2735edcf9ab7 classes/ReBar.lua --- a/classes/ReBar.lua Tue Mar 20 21:38:47 2007 +0000 +++ b/classes/ReBar.lua Wed Mar 21 00:13:27 2007 +0000 @@ -81,7 +81,6 @@ end local AceOO = AceLibrary("AceOO-2.0") -local ReBound = AceLibrary("ReBound-1.0") -- ReBar is an Ace 2 class prototype object. ReBar = AceOO.Class("AceEvent-2.0", ReAnchor, ReAnchor.IAnchorable) @@ -183,10 +182,6 @@ self:RefreshPageControls() end - -- register page up/down buttons with ReBound for keybinding - ReBound:Register(self.upArrow) - ReBound:Register(self.downArrow) - -- add bar to anchorTargets list table.insert(ReBar.anchorTargets, self) end diff -r 88df7235ad8b -r 2735edcf9ab7 libs/ReBound-1.0/ReBound-1.0.lua --- a/libs/ReBound-1.0/ReBound-1.0.lua Tue Mar 20 21:38:47 2007 +0000 +++ b/libs/ReBound-1.0/ReBound-1.0.lua Wed Mar 21 00:13:27 2007 +0000 @@ -1,22 +1,23 @@ --[[ Name: ReBound-1.0 -Revision: $Rev: 1 $ +Revision: $Rev: 2 $ Author: Flick Website: Documentation: SVN: -Description: Library for point-and-click key binding interfaces +Description: A library to assist with click-binding License: MIT -Dependencies: AceLibrary, AceEvent-2.0, AceLocale-2.2 +Dependencies: AceLibrary, AceEvent-2.0, AceLocale-2.2, AceOO-2.0 ]] -local version_major, version_minor = "ReBound-1.0", "$Rev: 1 $" +local version_major, version_minor = "ReBound-1.0", "$Rev: 2 $" if not AceLibrary then error(version_major .. " requires AceLibrary.") end if not AceLibrary:IsNewVersion(version_major, version_minor) then return end if not AceLibrary:HasInstance("AceEvent-2.0") then error(version_major .. " requires AceEvent-2.0.") end if not AceLibrary:HasInstance("AceLocale-2.2") then error(version_major .. " requires AceLocale-2.2.") end +if not AceLibrary:HasInstance("AceOO-2.0") then error(version_major .. " requires AceOO-2.0.") end local L = AceLibrary("AceLocale-2.2"):new("ReBound") @@ -24,16 +25,21 @@ local colorOff = "|r" local mouseButtonConvert = { + LeftButton = "BUTTON1", + RightButton = "BUTTON2", MiddleButton = "BUTTON3", Button4 = "BUTTON4", Button5 = "BUTTON5" } +--local BINDING_PENDING = { } -- a constant + -- localization L:RegisterTranslations( "enUS", function() return { ["none"] = true, ["Right-click"] = true, + ["Right Click"] = true, ["Click to select for binding"] = true, ["Shift-click to clear binding"] = true, ["Press a key to assign binding"] = true, @@ -42,15 +48,66 @@ end ) +local ReBound = AceLibrary("AceOO-2.0").Class("AceEvent-2.0") +--[[ + ReBound publishes the following events: -local ReBound = { } + -- temporary bindings (prior to SaveBindings() being called) + REBOUND_BIND_TEMP (id, key, targetFrameName, mouseButton) + REBOUND_UNBIND_TEMP (id, key) + + -- permanent bindings (fired all at once when SaveBindings() is called) + REBOUND_BIND (id, key, targetFrameName, mouseButton) + REBOUND_UNBIND (id, key) + + These events are published in response to click actions ONLY. This means + that if a key is unbound from a click-binding frame, and bound to some + other action, then REBOUND_UNBIND(id,key) will be fired. +]] + + +--[[ + Calls to new() which share ids will return an existing object with that id, similar + to AceLocale-2.2. +]] +local super_new = ReBound.new +function ReBound:new( id ) + self.instances = self.instances or { } + if self.instances[id] then + return self.instances[id] + else + local i = super_new(self,id) + self.instances[id] = i + return i + end +end + + +--[[ + Class object constructor + + arguments: + id : the ID that will be provided in events. This can be absolutely + anything, but a string is recommended. +]] +local super_init = ReBound.prototype.init +function ReBound.prototype:init( id ) + super_init(self) + + self.id = id + self.frames = { } + self.pending = { } + self.bindings = { } +end + + --[[ Arguments: key: A string representation of a key, suitable for passing to SetBinding. - target: The frame with an OnClick handler to attach a click-binding to + target: The frame with an OnClick handler to which the click-binding should be attached [button]: The mouse button to emulate. Default is "LeftButton". Returns: @@ -59,9 +116,9 @@ Notes: This does not save the bindings. ]] -function ReBound:SetBinding( key, target, button ) - if not key then self:error("ReBound:SetBinding() requires a key argument.") end - if not target then self:error("ReBound:SetBinding() requires a binding target argument") end +function ReBound.prototype:SetBinding( key, target, button ) + if not key then error("ReBound:SetBinding() requires a key argument.") end + if not target then error("ReBound:SetBinding() requires a binding target argument") end button = button or "LeftButton" -- prevent setting a binding that's already set @@ -82,8 +139,11 @@ -- set the new binding SetBindingClick(key, target:GetName(), button) - -- notify listeners, e.g. for storing the setting - self.event:TriggerEvent("REBOUND_BIND", key, target:GetName(), button) + -- store the temporary binding as "pending" for later notification + table.insert(self.pending, key) + + -- notify listeners, e.g. for displaying the setting + self:TriggerEvent("REBOUND_BIND_TEMP", self.id, key, target:GetName(), button) end @@ -103,8 +163,8 @@ This does NOT save the bindings. Call SaveBindings() to commit the bindings to disk. ]] -function ReBound:ClearBinding( key, target, button, silent ) - if not target and not key then self:error("ReBound:ClearBinding() requires a key or click-binding target argument") end +function ReBound.prototype:ClearBinding( key, target, button, silent ) + if not target and not key then error("ReBound:ClearBinding() requires a key or click-binding target argument") end button = button or "LeftButton" local keys = key and { key } or { self:GetBinding(target,button) } @@ -120,7 +180,7 @@ if f then name = f if b ~= "LeftButton" then - if b == "RightButton" then b = "Right Click" end + if b == "RightButton" then b = L["Right Click"] end name = f .."-"..b end end @@ -130,13 +190,14 @@ end end SetBinding(k,nil) - self.event:TriggerEvent("REBOUND_UNBIND", k) + table.insert(self.pending,k) + self:TriggerEvent("REBOUND_UNBIND_TEMP", self.id, k) end end --[[ - Gets the keys bound to clicking a frame. + Gets the keys currently click-bound to a frame. Arguments: target: target frame to query @@ -145,14 +206,66 @@ Returns: key1, key2, key3, etc, as strings. ]] -function ReBound:GetBinding( target, button ) - if not target then self:error("ReBound:GetBinding() requires a target frame argument") end +function ReBound.prototype:GetBinding( target, button ) + if not target then error("ReBound:GetBinding() requires a target frame argument") end button = button or "LeftButton" return GetBindingKey("CLICK "..target:GetName()..":"..button) end --[[ + Publishes permanent binding notification events. +]] +local function PublishBindings(self) + for _, key in ipairs(self.pending) do + local action = GetBindingAction(key) + local frame, button + if action then + frame, button = action:match("CLICK (.+)\:(.+)") + end + if frame == nil then + self:TriggerEvent("REBOUND_UNBIND", self.id, key) + else + self:TriggerEvent("REBOUND_BIND", self.id, key, frame, button) + end + end + self.pending = { } +end + + +--[[ + Saves the bindings using the current scheme. Also publishes events indicating that the + bindings have been saved/cleared permanently. +]] +function ReBound.prototype:SaveBindings() + SaveBindings(GetCurrentBindingSet()) -- will trigger an UPDATE_BINDINGS event. + PublishBindings(self) +end + + +--[[ + Reverts the bindings to the ones previously saved. Also publishes events indicating that the + bindings have been reverted. +]] +function ReBound.prototype:RevertBindings() + LoadBindings(GetCurrentBindingSet()) -- should trigger an UPDATE_BINDINGS event. + PublishBindings(self) +end + + +--[[ + Clears all bindings associated with registered frames. This is useful, for example, when switching profiles + and the keybinding data is stored in the profile. +]] +function ReBound.prototype:ClearRegisteredBindings() + for f, _ in pairs(self.frames) do + self:ClearBinding(nil,f,"LeftButton",true) + self:ClearBinding(nil,f,"RightButton",true) + end +end + + +--[[ Registers a target frame by creating a click-binding frame and putting that frame in the list of registered frames, which can then be all shown/hidden as one unit. @@ -162,7 +275,7 @@ Returns: A clickbinder frame. ]] -function ReBound:Register( target ) +function ReBound.prototype:Register( target ) local f = self:CreateClickBindingFrame(target) self.frames[target] = f return f @@ -173,20 +286,29 @@ Unregisters a target frame by removing it from the internal list. Does nothing to the clickbinding frame. Arguments: - target = the frame whose OnClick handler should no longer be the target of keybinding + target = the frame whose OnClick handler should no longer be managed. do NOT pass the clickbinding frame. Returns: nothing. ]] -function ReBound:Unregister( target ) +function ReBound.prototype:Unregister( target ) self.frames[target] = nil end --[[ + Unregisters all registered frames. +]] +function ReBound.prototype:UnregisterAll() + self.frames = { } +end + + + +--[[ Shows all the registered click binding frames. ]] -function ReBound:ShowAll() +function ReBound.prototype:ShowFrames() if InCombatLockdown() then -- can't set bindings while in combat, so don't bother showing them UIErrorsFrame:AddMessage(ERR_NOT_IN_COMBAT) @@ -201,7 +323,9 @@ --[[ Hides all the registered click binding frames. ]] -function ReBound:HideAll() +function ReBound.prototype:HideFrames() + -- because these frames aren't protected, there's no restriction + -- on hiding them while in combat. for _, f in pairs(self.frames) do f:Hide() end @@ -303,7 +427,7 @@ Returns: A clickbinder frame. ]] -function ReBound:CreateClickBindingFrame( target ) +function ReBound.prototype:CreateClickBindingFrame( target ) local f = CreateFrame("Button", nil, target) f.ReBound = self f:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") @@ -324,21 +448,18 @@ -- library setup local function activate( self, oldLib, oldDeactivate ) - -- set initial values - self.mode = oldLib and oldLib.mode or "char" - self.frames = { } - - self.event = AceLibrary("AceOO-2.0").Class("AceEvent-2.0"):new() - self.event:RegisterEvent("PLAYER_REGEN_DISABLED", function() self:HideAll() end) - + -- copy the list of active instances + self.instances = { } + if oldLib and oldLib.instances then + for k,v in pairs(oldLib.instances) do + self.instances[k] = v + end + end + if oldDeactivate then oldDeactivate(oldLib) end end -local function deactivate( self ) - self.event:UnregisterEvent("PLAYER_REGEN_DISABLED") -end - -AceLibrary:Register(ReBound, version_major, version_minor, activate, deactivate) +AceLibrary:Register(ReBound, version_major, version_minor, activate) ReBound = nil diff -r 88df7235ad8b -r 2735edcf9ab7 main.lua --- a/main.lua Tue Mar 20 21:38:47 2007 +0000 +++ b/main.lua Wed Mar 21 00:13:27 2007 +0000 @@ -9,7 +9,7 @@ local L = AceLibrary("AceLocale-2.2"):new("ReAction") local dewdrop = AceLibrary("Dewdrop-2.0") local tablet = AceLibrary("Tablet-2.0") -local ReBound = AceLibrary("ReBound-1.0") +local ReBound = AceLibrary("ReBound-1.0"):new("REACTION") -- private functions local function tcopy(t) @@ -54,21 +54,31 @@ self:RegisterEvent("PLAYER_ENTERING_WORLD","HideDefaultBars") self:DisableDefaultKeybindings() + -- create update function for keybinding frame + ReActionKeybindDialog:SetScript("OnHide", function(frame) + main:SetKeybindMode(false) + if frame.save then + ReBound:SaveBindings() + else + ReBound:RevertBindings() + end + frame.save = false + end ) + -- initial non-persistent state self.locked = true self.bars = { } end --- OnEnable is called at startup (startup = true, oldConfig = nil), --- and when the profile is changed (startup = false, oldConfig ~= nil ) -function main:OnEnable( startup, oldConfig ) +-- OnEnable is called at startup and when the profile is changed (via OnProfileEnable) +function main:OnEnable( ) if self.db.profile.firstRunDone ~= true then self.db.profile.bars = tcopy(ReAction_DefaultBlizzardBars) end self:DestroyAllBars() self:SetupBars() self:UpgradeProfile() - self:SetupKeybindings( oldConfig and oldConfig.bindings ) + self:SetupKeybindings() if self.db.profile.firstRunDone ~= true then self:Unlock() end @@ -81,7 +91,10 @@ -- OnProfileEnable() is only called when switching profiles, NOT for the initial profile at load time. function main:OnProfileEnable( oldName, oldData ) - self:OnEnable(false, oldData) + self:UnregisterEvent("REBOUND_BIND") + self:UnregisterEvent("REBOUND_UNBIND") + ReBound:ClearRegisteredBindings() + self:OnEnable() end function main:UpgradeProfile() @@ -164,22 +177,18 @@ BINDING_NAME_PREVIOUSACTIONPAGE = label end -function main:SetupKeybindings( previous ) - if previous then - self:UnregisterEvent("REBOUND_BIND") - self:UnregisterEvent("REBOUND_UNBIND") - for key, binding in pairs(previous) do - ReBound:ClearBinding(key, getglobal(binding.target), binding.button, true) - end - end +function main:SetupKeybindings() if self.db.profile.firstRunDone ~= true then self:StealKeyBindings() else for key, binding in pairs(self.db.profile.bindings) do - ReBound:SetBinding(key, getglobal(binding.target), binding.button) + local target = getglobal(binding.target) + if target then + ReBound:SetBinding(key, target, binding.button) + end end end - SaveBindings(GetCurrentBindingSet()) + ReBound:SaveBindings() self:RegisterEvent("REBOUND_BIND") self:RegisterEvent("REBOUND_UNBIND") end @@ -193,17 +202,17 @@ ReBound:SetBinding(key, self.bars[1].buttons[i]:GetActionFrame(), "LeftButton") end end - SaveBindings(GetCurrentBindingSet()) + ReBound:SaveBindings() end -function main:REBOUND_BIND(key, target, button) - if key and target then +function main:REBOUND_BIND(id, key, target, button) + if id == "REACTION" and key and target then self.db.profile.bindings[key] = { target = target, button = button } end end -function main:REBOUND_UNBIND(key) - if key then +function main:REBOUND_UNBIND(id, key) + if id == "REACTION" and key then self.db.profile.bindings[key] = nil end end @@ -227,10 +236,10 @@ end end if enabled then - ReBound:ShowAll() + ReBound:ShowFrames() ReActionKeybindDialog:Show() else - ReBound:HideAll() + ReBound:HideFrames() if ReActionKeybindDialog:IsShown() then ReActionKeybindDialog:Hide() end @@ -468,6 +477,10 @@ end ) + -- register page up/down buttons with ReBound for keybinding + ReBound:Register(bar.upArrow) + ReBound:Register(bar.downArrow) + if not self.locked then bar:ShowControls() end