WoW:XML/Anchor

From AddOn Studio
< XML
Jump to navigation Jump to search

XML UI ← XML types < Anchor

An Anchor is also a fundamental UI building block for defining positional layout for UI elements of LayoutFrames. An Anchor is an element of an Anchors array defined in a LayoutFrame. Anchor XML elements, as a part of Anchors array, can be defined on any LayoutFrame or LayoutFrame type, which also includes Frames, Textures, and FontStrings.

Inheritance[edit]

Inherited by: none, Inherits: none, Defined in: LayoutFrame > Anchors

Elements[edit]

Attributes[edit]

  • point (FRAMEPOINT) - this place on this layout frame that should get anchored
  • relativeTo (string) - global name of LayoutFrame to make relative to. By is relative to its parent.
  • relativeKey (string) - a Lua reference to make relative to. Alternative to using 'relativeTo'. See 'parentKey' in LayoutFrame.
  • relativePoint (FRAMEPOINT) - place on the relative frame that 'point' should anchored to. Default value is same as value of 'point'
  • x (int) - horizontal offset from the effective 'relativePoint'. Default is 0.
  • y (int) - vertical offset from the effective 'relativePoint'. Default is 0.

Summary[edit]

An Anchor is also a fundamental UI building block for defining positional layout for UI elements of LayoutFrames. An Anchor is an element of an Anchors array defined in a LayoutFrame. Anchor XML elements, as a part of Anchors array, can be defined on any LayoutFrame or LayoutFrame type, which also includes Frames, Textures, and FontStrings.

Example[edit]

<Frame name="MyFrame">
  <Frames>
    <Frame name="$parentChild">
      <Anchors>
        <Anchor point="TOP">
          <Offset>
            <AbsDimension x="0" y="-22" />
          </Offset>
        </Anchor>
        <Anchor point="BOTTOM">
          <Offset>
            <AbsDimension x="0" y="22" />
          </Offset>
        </Anchor>
      </Anchors>
    </frame>
  </Frames>
</Frame>

This example will align the top of the 'child' frame to the top of 'MyFrame', and the bottom of 'child' to the bottom of 'MyFrame', and then offset that alignment by 22 vertically on both of those sides. This example would effectivly cause the child to stretch vertically to match its parent frame in wow, no matter how the parent frame changed size or position. Using anchors in this way lets wow automatically partially or wholely manage the actual position and size of the child.

Details[edit]

Every frame needs a final position and size in order to be drawn or to be available for user interaction in the WoW UI. Anchors and Size are used to define that layout postion and size. Anchors work by defining how to place the edge or corner of a layout frame. There is no limit to the number of anchors that can be defined on a layout frame. This makes for a very flexible layout system.

Points and Relative Points[edit]

1) A particular edge or corner of a layout frame is an anchor 'point'.

This defines where in this layout frame that it should be anchored. For example, a value of 'TOPLEFT' refers to the top left corner of the of this layout frame. For an edge, 'LEFT' for example, the center of that edge is considered to be the actual anchor point, if no other anchors additionally overriding the layout frames vertical axis are defined.

2) The 'relativePoint' defines where, on another layout frame, to place a particular 'point' of the layout frame itself.

This is the anchor's relative position on the 'relative frame'. By default, the anchor will attach to the same point on the relative frame as defined by 'point'. So, if 'point' is set to 'LEFT', then by default the anchor will also attach to the left side of the relative frame. If 'relativePoint' is set however, it overrides the default behavior. So if 'relativePoint' set to 'RIGHT', then the left side of this layout frame, in this example, will instead anchor to the right side of the relative frame.

3) The 'relativeTo' and 'relativeKey' atributes define which other 'layout frame' this anchor is relative to.

  • relativeTo (string)
  • relativeKey (string)

By default, all layout frames are relative to their parent. If either of these are set, then it overrides the relative layout frame with respect to this anchor point. Different anchors can also have different relative layout frames, such that a texture could automatically stretch across two different other layout frames.

The 'relativeTo' is the global name of a frame to use for the 'relativePoint'. Alternatively, the 'relativeKey' is the name of a runtime Lua variable on this layout frame's parent, that contains a reference to the layout frame to be used for the 'relativePoint'. Using 'parentKey' elsewhere is a way to define these values for 'realtiveKey'. If 'relativeTo' is defined, then 'relativeKey' will be ignored. See the 'parentKey' attribute in XML/LayoutFrame as one of the many ways to set this value.

Layouts[edit]

Layout in printing or computer graphics is concerned with the flow of elements with respect to each other. A layout system is a system for defining that layout. For WoW, flow in layout is defined by frames and anchors, and flow order is established by 'laying out' frames from their creation order from top to bottom, and from parent to child. Frames with the same parent and render z-order, will be rendered from first to last in definition order as well.

<Frame name="Frame">
	<Frames>
		<Frame name="Frame2">
			<Size x="100" y="100">
			<Anchors><Anchor point="TOP" relativePoint="BOTTOM" x="0" y="-10" /></Anchors>
		</frame>
		<Frame name="Frame3">
			<Size x="100" y="100">
			<Anchors><Anchor point="TOPLEFT" /></Anchors>
		</frame>
	</Frames>
</Frame>

This will place the 'Frame2' child at an offset of 10 from the bottom of its parent frame; 'relativeTo' for 'Frame2' is its parent by default. Frame3 will be rendered on top of Frame2 is they overlap.

    <Frame name="Frame3">
      <Size x="50" y="50">
      <Anchors><Anchor point="TOPLEFT" relativeTo="Frame2" /></Anchors>
    </frame>

Would place the top left of Frame3 on the top left of frame 2.

Relative References[edit]

Frames can have names. This allows an 'anchor' to reference a frame by its name for relative layout using 'relativeTo'. The 'relativeTo' attribute can contain a replacable tag '$parent' for refereing to another frame without actually having to know its direct parents name, like '$parent3' would refrence 'Frame3' if the parent's name was 'Frame'. This is useful for allowing cleaner code, as parent frame's names could be changed without disturbing the layout. This is even more important with templates as a template may have no way of knowing its parents names and could still access known relative elements by name.

<Frame name="FrameTemplate" virtual='true'>
  <Frames>
    <Frame name="$parent2">
      <Size x="100" y="100">
      <Anchors><Anchor point="TOP" relativePoint="BOTTOM" x="0" y="-10" /></Anchors>
    </frame>
    <Frame name="$parent3">
      <Size x="50" y="50">
      <Anchors><Anchor point="TOPLEFT" relativeTo="$parent2" /></Anchors>
    </frame>
  </Frames>
</Frame>
<Frame name="Frame" inherits="FrameTemplate" />

This works the same as the last example, but using a tmplate with all manner of relative anchors and names.

<Frame name="FrameTemplate" virtual='true'>
  <Frames>
    <Frame parentKey="frame2">
      <Size x="100" y="100">
      <Anchors><Anchor point="TOP" relativePoint="BOTTOM" x="0" y="-10" /></Anchors>
    </frame>
    <Frame parentKey="frame3">
      <Size x="50" y="50">
      <Anchors><Anchor point="TOPLEFT" relativeKey="$parent.frame2" /></Anchors>
    </frame>
  </Frames>
</Frame>
<Frame name="Frame" inherits="FrameTemplate" />

This works the same as the last example, but using lua runtime based 'keys' instead of names.

Default References[edit]

This is one of those things you wish you could explain at the beginning, but needs to be at the end. :)

Each of these are, in the general case, equivalient:

<[[XML/Anchor|Anchor]] point="TOPLEFT" />
<[[XML/Anchor|Anchor]] point="TOPLEFT" relativeTo="$parent" />
<[[XML/Anchor|Anchor]] point="TOPLEFT" relativeKey="$parent" />

In each case the relative frame is parent. For the default case, the relative frame its the parent. For 'relativeTo', a named parent frame's full name will replace '$parent' causing 'relativeTo' to refer to the parent frame by name. For 'relativeKey', the named or unnamed parent frame's Lua 'reference' will be used as the relative frame, similar to 'self.GetParent()'.

However as of 5.2, this will bug and *not* be equivalent of either '$parent' or 'self':

<Anchor point="TOPLEFT" relativeKey="" />

In this case will ignore all size and anchors, and act as though layout was 'setallpoints' with this frame same size and position as parent,

Also, as of 5.2, May 13 2013, following may/will crash WoW:

<Anchor point="TOPLEFT" relativeKey="$parent.$parent.AccidentallyOnUIParent" />

Where Frame is top frame with no parent and contains a LayerFrame with above anchor, which would effectivly be asking for key on parent of UIParent, since the frame is using the default parent. This is exceedingly easy to accidentally set, or move a frame where some child asks UIParent instead of intended parents parent. So: <Frame><Layers><Layer><Texture><Anchors><Anchor ...CENTER relativeKey=$parent.$parent.Key"/>... will crash, but not <Frame><Frames><Frame><Layers><Layer><Texture><Anchors><Anchor ...CENTER relativeKey=$parent.$parent.$parent.Key"/>... or <Frame parent="UIParent"><Layers><Layer><Texture><Anchors><Anchor ...CENTER relativeKey=$parent.$parent.$parent.Key"/>... and it doesnt matter in any of the cases if the key exists or not.

And finally, for 'relativeTo' below, in the case where the immediate parent doesn't actually have a 'name':

<Anchor point="TOPLEFT" relativeTo="$parent" />

... will *not* be equivalient to:

<Anchor point="TOPLEFT" />

... and will look for a parent with a name up the hierarchy until finds, or hits UIParent which is always named.

Full Key Based Example[edit]

This is a more complex, albeit awkward, example using 'keys' rather than 'names' for referencing frames. The template in this case uses 'keys' instead of names to make for a cleaner global name space, as this template is used a lot.

The imputus for this template was to use explicit button layer frames, as the 'Button' frame type only knows how to stretch a single simple texture. Using three cuts down on horizontal scaling distortion at the ends for different button sizes. Notice that each original texture is sliced using texture coords so that the ends dont stretch, but is the same texture. Notice that the scripts have to manage the button's texture expressly, as the normal button texture management isn't used. Also important is that the parent keys are available to the script just like any other member of a frame's Lua side runtime object.

Note: Portions edited for brevity. See WoW UI Toolkit for complete original template. As always, if copy/paste give template another name.

<Button name="UIPanelButtonXxxTemplate" virtual="true">
  <Size x="40" y="22"/>
  <Layers>
    <Layer level="BACKGROUND">
      <Texture parentKey="Left" file="Interface\Buttons\UI-Panel-Button-Up">
        <Size x="12"/>
        <Anchors>
          <Anchor point="TOPLEFT"/>
          <Anchor point="BOTTOMLEFT"/>
        </Anchors>
        <TexCoords left="0" right="0.09375" top="0" bottom="0.6875"/>
      </Texture>
      <Texture parentKey="Right" file="Interface\Buttons\UI-Panel-Button-Up">
        <Size x="12"/>
        <Anchors>
          <Anchor point="TOPRIGHT"/>
          <Anchor point="BOTTOMRIGHT"/>
        </Anchors>
        <TexCoords left="0.53125" right="0.625" top="0" bottom="0.6875"/>
      </Texture>
      <Texture parentKey="Middle" file="Interface\Buttons\UI-Panel-Button-Up">
        <Anchors>
          <Anchor point="TOPLEFT" relativeKey="$parent.Left" relativePoint="TOPRIGHT"/>
          <Anchor point="BOTTOMRIGHT" relativeKey="$parent.Right" relativePoint="BOTTOMLEFT"/>
        </Anchors>
        <TexCoords left="0.09375" right="0.53125" top="0" bottom="0.6875"/>
      </Texture>
    </Layer>
  </Layers>
  <Scripts>
    <OnMouseDown>
      if not self:IsEnabled() then return end
      self.Left:SetTexture("Interface\\Buttons\\UI-Panel-Button-Down");
      self.Middle:SetTexture("Interface\\Buttons\\UI-Panel-Button-Down");
      self.Right:SetTexture("Interface\\Buttons\\UI-Panel-Button-Down");
    </OnMouseDown>
    <OnMouseUp>
      if not self:IsEnabled() then return end
      self.Left:SetTexture("Interface\\Buttons\\UI-Panel-Button-Up");
      self.Middle:SetTexture("Interface\\Buttons\\UI-Panel-Button-Up");
      self.Right:SetTexture("Interface\\Buttons\\UI-Panel-Button-Up");
    </OnMouseUp>
    <OnDisable>
      self.Left:SetTexture("Interface\\Buttons\\UI-Panel-Button-Disabled");
      self.Middle:SetTexture("Interface\\Buttons\\UI-Panel-Button-Disabled");
      self.Right:SetTexture("Interface\\Buttons\\UI-Panel-Button-Disabled");
    </OnDisable>
  </Scripts>
  <ButtonText name="$parentText"/>
  <NormalFont style="GameFontNormal"/>
  <HighlightFont style="GameFontHighlight"/>
  <DisabledFont style="GameFontDisable"/>
  <HighlightTexture inherits="UIPanelButtonHighlightTexture"/>
</Button>

See also XML Button.