simonw代码@痕记

导航

WoW安全模板技术文档翻译 - SecureStateHeader

SecureStateHeader - Button management state engine

原文来自SecureStateHeader.lua 
译者: simonw From CWDG
原文版权归属于原作者, 转载请注明译者, 未经许可不得用于商业用途.
本文URL: http://www.cnblogs.com/simonw/archive/2007/07/06/808492.html

译注:
modifier指Ctrl,Alt,Shift这几个键.
注意按钮与按键的不同, 按钮指非物理的button, 按键则时是物理button如键盘,鼠标.
由于很多东西尚未实践, 在加上原文单词拼写错误频出, 所以翻译难免有疏漏不足, 请您指出.

重要提示
:2.1.0 更新了next state functions导致需要state header. 这将引起不再向后兼容. 开发者仅仅在直接调用new state计算方式才受影响. 

这个模版可以用在很多用户交互界面元素上, , 动作条, 弹出菜单, 姿态条, 等等. 虽然他显得有些复杂, 但对于使用是非常直接的. 

这个模版的目标是实现下面4种机制:

1.      子对象可见性
这个模版能够基于当前的state隐藏或显示他的子窗体. 对于弹出菜单很有用.

2.      按钮映射
这个模版能够传递一个点击类型(LeftButton)到另一个同样基于这个state的模版. 对于使用了secureactiontemplates的翻页动作条很有用.

3.      State转换
这个模版能够在任何点击后转换到一个新的state, 可选的他同样能够在没有其他转换发生时, 一段时间之后调度第二个转换. 对于自隐藏菜单很有用.

4.      键位重绑定
这个模版能够在state转换时对子按钮对应的按键做重新绑定. 

这个引擎无法跨越战斗状态的通用规则限制, 一旦进入战斗状态将无法重新配置, 并且无法自己做出任何决断. 只有用户点击才能控制动作执行. 动作只能独立的被用户点击控制并且会因姿态和unit存在而改变.

使用HEADER 

创建一个窗体继承字这个模版, 设置子对象的属性, 对于你所有想要控制的子对象都应该这么做, 如下:

--headerFrame:SetAttribute("addchild", childFrame); 

最好的学习方式是你自己做一个简单的例子并使他能工作起来.

States
 

理解state的最好方式就是你可以把他想象成一组位置, 键绑定, 可见性等的完整配置. 所有的按钮被header控制. Header管理着任意数量的这些state, 并以这种方式管理任意数量的不同按钮的布局和重新配置(以及有限合理的扩展). 

state几乎可以是你能想象的任何字符串, 但是我推荐你使用正整数值, 因为这会更加便利并能获得更好的支持. Header初始是无state, 值为0.

改变state是通过转换规则进行的, 也就是一份描述如何从当前state到下一个新state的详细的说明. 一个简单的规则例子如下:

--   "3" – 改变的state 3

--   "1,3,5" – 循环起始从1跳到3跳到5再回到1

--   "1-5" – 循环从15升序

--   "5-1" --循环从51降序

--   "1-3:0;4:5;5:4" – state1-3 变为0, state4 ,5轮回切换.

--   "4:;*:3" – state4单独存在, 所有其他state映射到state3.

--   "1-4:=" – state1-4 映射到他们自己(重定锚位置很有用). 

Header当前的state保存在他自己的"state"属性中, 无论何时state改变都会调用self:StateChanged(newState), 如果他是被定义过的.

可见性

每个子对象通过2个属性控制自己的显示与隐藏, "showstates" "hidestates". 如果"showstates"被定义那么"hidestates"将不会再被请求. 每个属性的解析方式是相同的, 不同的是实体在列表中是显示还是隐藏.这个格式非常简单, 用逗号隔开列表中的state或者state范围, !感叹号是代表反向的意思. 

使用"showstates"的例子

-- "7" -- state 7时显示, 其他时候隐藏

-- "1-5" -- states 1 – 5时显示, 其他时候隐藏

-- "1-3,11-13,20" -- states 1-3, 11-13, 20,时显示, 其他时候隐藏

-- "*" – 所有情况下都显示

-- "!7,1-10" -- state 7隐藏, 1-10 显示, 其他时候隐藏 

鼠标按键重映射 

鼠标按键重映射是一种使得你的一个鼠标按键动作(LeftButton)像是另一个动作(如说话, RightButton, 或者用户定义的一个虚拟键). secure action template使用这个按键来寻找属性, 他会让你在不同的state时改变他的行为. 按键映射非常灵活, 使用SecureButton modifier规则访问子对象的"statebutton"属性, 所以你可以为不同的modifier组合和按钮建立不同的重映射. 如果属性存在那么他将会被解析为一系列的state来匹配条件和新的按钮.

示例:

-- "statebutton" -> "RightButton" – 任意情况下都映射到RightButton

-- "statebutton" -> "2:RightButton" -- state 2时映射到RightButton

-- "*statebutton1" -> "2:RightButton;3:MiddleButton"

-- 将鼠标左键在state 2时映射到RightButtonstate3时映射到MiddleButton

-- "shift-statebutton*" -> "1-10:LeftButton"

-- states 1-10时将shift +任意键映射到LeftButton

-- "*statebutton2" -> "1-10:heal;11-20:nuke"

--映射RightButtonstate1-10时到heal虚拟按钮在state 11-20时到nuke虚拟按钮 

重映射发生在点击开始, 之后的处理过程就是使用一个存在的替换按钮或者原始按钮当替换按钮不存在时. 

除了已经被映射的,你可以设定为任何你想的子按钮动作. 

State转换 

在使用了重映射按键后, 子按钮的"newstate"属性将会被用来查找新state所对应的规则. 如果这个属性的值是存在的那么他的赋值方式将与按键重映射过程一样, 但也有些不同的选项在冒号后. 

示例:

-- "1-5" – 循环经 1,2,3,4,5, 再回到 1...

-- "2,4,1,7" --循环经2,4,1,7, 再回到2...

-- "1-5:1;6-10:10-6;*:0"

如果是states 1-5,就切换到state 1, 如果是states 6-10 那么循环经10,9,8,7,6, 如果是其他情况则切换到state 0 

如果一个转换匹配找到那么state将会在窗体的原始OnClick动作处理完成后立即改变. 

你同样可以使用"delaystate" "delaytime" 属性, "delaystate" 解析方式与"newstate" 很像, 使用原始state(不是从"newstate"中获得的新state), "delaytime" 的解析方式和"statebutton"很像, 他需要一个结果在短时间内. 如果他们同时出现并且有效, 那么在这期间内如果没有另一个state发生改变, 就会在"delaytime"秒后改变"delaystate". 

这里有最后一个延时方面的选项属性, "delayhover", 他的解析如同"statebutton", 如果他的结果为nil或者false那么timer只是会简单的过期(除非取消掉). 如果结果存在那么timer会在鼠标聚焦于header窗体上或者他的任何子对象上时被重置. 

隐藏header窗体总是会引起终止(the expiration to be zeroed and hover cleared). 改变state会取消已经在调度中的"delaystate", 配置一个新的延时变化同样会取消掉上一个"delaystate".

键盘重映射 

以组方式处理按键绑定, 称之为绑定集合(binding sets). Header能够在state改变时改变绑定集合, 并且每个子按钮都能提供另一组不同的绑定集合来置换现在的. 这种绑定覆盖将会维持下去, 只有当按键不再在这个集合中时绑定才会被还原. 

Header有一个"statebindings"的属性用于定义哪些state对应于哪些绑定集合, 这与button re-mapping属性类似, 例如: 

-- "1:main;2:submenu;3-10:catmenu;11-20:bearmenu" 

通过"bindings-<setname>" 属性, 可以在每个按钮上能够在一个给定的绑定集合中设置一个或更多绑定, 他的值的格式是"<Key>[:<Button>[,<Button>...]][;...]", 其中Button在未定义时默认为LeftButton. 例如:

-- "bindings-main" => "CTRL-F"

-- "bindings-alt" => "CTRL-F:LeftButton,RightButton;CTRL-G" 

你可以为一个按键加一个*号前缀来提升绑定的优先级, 高优先级的绑定在普通的绑定之前执行并且只能用于一个短暂的目的. 例如:

-- "bindings-popup" => "*1;*2"; 

高级内容

这部分是一些高级特性, 在下面的部分里'state attribute'属性会被用来描述一个属性, 他直接抓取字符串值中的名字并以state的处理方式来解析. 一个空字符串将以nil返回. 例如: 

-- "1:heal;2-5:harm" – "heal" 解析到state 1, "harm" 解析到states 2 - 5, 其他为nil.

-- "4:;*:trueval" -- state 4时解析为nil, 其他stats时为"trueval".

state栈函数 

你能使用高级转换规则在转换过程中存储和回复state. 除了上面描述的规则下面的函数能够提供一个state(这个栈是受限制的, 最多保存4state): 

-- push([newState]) push the current state onto a 'state stack', and

--                  switch state to the newState (if provided)

-- pop([defaultState]) pop the top state off the stack and use it as the

--                     new state. If the stack is empty and defaultState

--                     is provided, then that's used as the new state.

-- set([newState]) replace the state stack with just the current state, and

--                  switch state to the newState (if provided)

-- swap([defaultState]) swap the current state and the top state off the

--                     stack. If the stack is empty and defaultState

--                     is provided, then that's used as the new state

--                     and the current state goes into the stack. 

你可以结合这些函数以及基本的规则来将他们分离到不同的地方. 例如: 

-- "1-5:push() 5-1;6-10:push(20)" -- If in states 1-5 then push the

--                                   current state and move to the previous

--                                   state in the range 1-5. If in states

--                                   6-10 then push the current state and

--                                   switch to state 20.

按钮定位 

可见的子对象可以设定与父对象的相对位置, 下面的子对象的state属性可以控制子对象的位置: 

-- "ofsx"        - The X offset, defaults to 0

-- "ofsy"        - The Y offset, defaults to 0 

在当定位发生的时候这2个属性的值至少有一个必须存. 

-- "ofspoint"    - The point on the button for the anchor, default "CENTER"

--"ofsrelpoint" - The point on the header to anchor to, defaults to the ofspoint.

state被更新子对象的位置就会被重应用, 这些子对象就不会有偏移值了. 

按钮尺寸 

可见的子对象可以设定他们的尺寸集, 下面的子对象的state属性可以设置他们的尺寸: 

-- "width"        - The desired width.

-- "height"       - The desired height.

-- "scale"        - The desired scale. 

只有这些属性才能改变尺寸. 

Header的父对象 

Header使用"headparent"state属性能够改变他的父对象, 匹配"headparent-<parentName>" 这样的属性格式. 

设置"headparent-<parentName>"的值用来描述父窗体, 使用"headparent"state属性来映射state规则至父窗体名称. 

"parentName" 不需要匹配窗体实际的名字或一个全局标识. 这仅仅是给header一个指向来找到父窗体. 例如: 

-- "headparent" -> "1:frame" -- Check "headparent-frame" for the frame object

-- when in state 1

-- "headparent" -> "1:parent1;2:parent2" -- Check "headparent-parent1" when

-- in state 1 and check "headparent-parent2" when in state 2 

任何给出窗体的"headparent-<parentName>"属性必须是一个受保护的窗体.

Header尺寸 

Header也可以设置尺寸. 按照下面的属性: 

-- "headwidth" - The desired width.

-- "headheight" - The desired height.

-- "headscale" - The desired scale. 

只有这些属性才能改变尺寸.

Header定位 

Header也可以被定位, 按照下面的属性: 

-- "headofsx"    - The header X offset, defaults to 0

-- "headofsy"    - The header X offset, defaults to 0 

在当定位发生的时候这2个属性的值至少有一个必须存.

-- "headofspoint" - The point on the header to anchor, default "CENTER"

--"headofsrelpoint" The point to anchor against, defaults to the             headofspoint. 

"headofsrelpoint"属性有2个特定的值用于与父窗体相对的点位置: 

-- "headofsrelpoint" -> "screen" -- Anchor relative to screen origin

-- "headofsrelpoint" -> "cursor" -- Anchor relative to cursor position

Header Unit改变 

你可以使用"headstateunit"state属性来改变unit或者unit前缀的属性. 每个state能够有一个下面的值:

-- "<unit>"    -- A normal unit, e.g. "player" (unitsuffix is cleared)

-- "+<suffix>" -- A unit suffix e.g. "target" (unit is cleared)

-- "none"      -- Clear unit and unitsuffix 

外部State改变 

State Header能够被外部代码触发从而让state改变, unit存在改变, 姿态改变等. 使用header属性"state-<type>", "state-unitexists"是一个普遍的机制. 

当这其中的一个属性被设置为<newvalue>, header 会检查"statemap-<type>-<newvalue>", 如果没有被设置则是"statemap-<newvalue>"属性. 如果其中一个找到了那么就会处理特定的state转换, 然后headerstate就会被按照结果改变.

例如: 

切换到state 0当收到一个flase"state-unitexists", 反之为true时切换到state 1. 

-- "statemap-unitexists-false" => "0"

-- "statemap-unitexists-true" => "1" 

-- If a non-empty statemap is provided for an external change, then the following attributes can also be used to provide a delayed subsequent change

-- "delaystatemap-<type>-<value>" then "delaystatemap-<type>"

-- "delaytimemap-<type>-<value>" then "delaytimemap-<type>"

-- "delayhovermap-<type>-<value>" then "delayhovermap-<type>" 

只有当值被实际改变后外部state源才会设置这个值, 除非显示出ACTION已经发生了. 

SLAVE HEADERS 

设置一个state header为另一个state header的子对象也是可以的. headerstate变化了, 他会为每个子对象设置"state-parent"属性至他们的新state. 

你同样可以设置一个state header来将state改变的信息传递至父对象. 如果你为这个header设置了"exportstate"属性, 那么任何在这个header上的state改变都会设置他父对象的"state-<exportstate>"的值至header的新state. 

控制TEMPLATES的锚点 

有些时候你可能需要共享同一个header在多个窗体中, 比如一个unit窗体对应一个下拉列表. 另一些时候你可能需要header通过一个鼠标滑过或者更复杂的动作来控制窗体的显示. 要达到这些目的你可以使用anchor templates.

 当被激活, 下面的这些属性可以控制一个或多个动作. 

1.      "anchorchild"属性用来查找哪些窗体被控制. 这可以是一个窗体或者是在这个anchor的当前父对象被使用情况下的"$parent"字符串. 如果没有anchor child被指定或者没有窗体, 那么后面不会发生任何事情.

2.      "childofsx" "childofsy"属性被请求或者存在时, "childpoint" "childrelpoint"属性就会被访问. 这用来为anchorchild重定位锚点相对anchor窗体.

3.      "childreparent"属性被请求时, 如果他是一个true值那么anchor child的父对象就会被设置到anchor窗体上. 这是个特别有用的地方用于联合"useparent-unit"创建感应的unit上下文弹出窗口.

4.      "childraise" 属性被请求时, 如果他是一个true值那么这个anchor childRaise()方法会被调用到.

5.      "childstate"属性被请求时, 如果他是一个true值那么他会改变anchor child'state-anchor'属性. 如果你给一个新state加一个”^”前缀那么属性将总是被设置, 否则他只在与以前不同的时候才被设置. 你也可以在anchor上提供一个值为true"childverify"属性, 这样只会在anchorchild的父对象时anchor窗体的时候改变state.

6.      anchor child 列表

提供下面的templates:

SecureAnchorButtonTemplate: 使用按钮OnClick提供一个OnClick的支持. 你可以通过"onenterbutton"属性或者modifiers键的组合来指定替换按钮.

SecureAnchorEnterTemplate: 使用虚拟按钮的"OnEnter" "OnLeave"提供鼠标滑过的支持. 你可以通过" onenterbutton"属性或者"onleavebutton"属性以及modifiers键的组合来指定替换按钮. 

SecureUpDownTemplate: 使用按钮的压下与释放来提供OnMouseUp/OnMouseDown的支持. 你可以通过" onmouseupbutton"属性或者" onmousedownbutton"属性以及modifiers键的组合来指定替换按钮. 

例如:

-- local anchor = CreateFrame("Frame", nil, nil, "SecureAnchorEnterTemplate")

-- Send state changes to the menuheader on enter and leave

-- anchor:SetAttribute("anchorchild", menuheader);

-- anchor:SetAttribute("*childraise-OnEnter", true);

-- anchor:SetAttribute("*childstate-OnEnter", "enter");

-- anchor:SetAttribute("*childstate-OnLeave", "leave");

--

-- -- Switch from state 0 to state 1 on enter, and then schedule a

-- -- switch from state 1 to state 0 1 second after a leave (with hover

-- -- detection in case the mouse stays on the menu).

-- menuheader:SetAttribute("statemap-anchor-enter", "0:1");

-- menuheader:SetAttribute("statemap-anchor-leave",      ";"); -- Non-empty

-- menuheader:SetAttribute("delaystatemap-anchor-leave", "1:0");

-- menuheader:SetAttribute("delaytimemap-anchor-leave", "1:1");

-- menuheader:SetAttribute("delayhovermap-anchor-leave", "1:true");

 

posted on 2007-07-06 14:31  simonw  阅读(1454)  评论(0编辑  收藏  举报