WoW:Making Draggable Frames: Difference between revisions

From AddOn Studio
Jump to navigation Jump to search
No edit summary
(nowiki in XML comments.)
Line 1: Line 1:
== XML Declarations ==
{{UIHowTo}}
Draggable frames can be moved by having the user hold down a mouse button over the frame, then move the mouse to reposition the frame. This HOWTO describes how to make a frame draggable.


First, the XML tags movable="true" and enableMouse="true" must be in the frames declaration.
== Summary ==
Note: Some frame templates like 'button' already include enableMouse="true".
To initiate dragging of a frame, call {{api|Frame StartMoving|Frame:StartMoving()}}; to stop, call {{api|Frame StopMovingOrSizing|Frame:StopMovingOrSizing()}}. The frame must be flagged as movable (either through the movable attribute in its XML declaration or using {{api|Frame SetMovable|Frame:SetMovable}}(isMovable).


Example:
While those functions can theoretically be called from anywhere, {{api|Frame RegisterForDrag|Frame:RegisterForDrag}}("button1", ...) allows the use of OnDragStart/OnDragStop widget handlers. Note that to receive mouse events (including dragging), the frame must be mouse enabled (either through the enableMouse attribute in its XML declaration or using {{api|Frame EnableMouse|Frame:EnableMouse}}(isEnabled)); buttons are mouse-enabled by default.
<Frame name="TellTrackFrame" enableMouse="true" movable="true" resizable="true" parent="UIParent" hidden="true">


== Simple Dragging ==
== Using XML ==
The code below creates a draggable Frame widget and uses the Drag widget handlers to initiate dragging. Note the use of movable and enableMouse attributes:
<Frame name="DragFrame1" enableMouse="true" movable="true">
  <Scripts>
  <OnLoad>self:RegisterForDrag("LeftButton");</OnLoad>
  <OnDragStart>self:StartMoving();</OnDragStart>
  <OnDragStop>self:StopMovingOrSizing();</OnDragStop>
  </Scripts>
  <nowiki><!-- Tags below add a visual element to the frame. --></nowiki>
  <Layers>
  <Layer level="ARTWORK">
    <Texture setAllPoints="true">
    <Color r="1.0" g="0.5" b="0.0" a="0.5" />
    </Texture>
  </Layer>
  </Layers>
  <Size x="64" y="64" />
  <Anchors><Anchor point="CENTER" relativeTo="UIParent"/></Anchors>
</Frame>


One simple way to detect drag is to add OnDragStart and OnDragStop script elements to the frame:
=== Using OnMouseUp/OnMouseDown ===
The OnDrag* handlers typically require the mouse button to be held down for a small amount of time prior to enabling the mouse behavior, making them well suited for dragging widgets that normally respond to clicks. However, if the frame you wish to make draggable is not normally a button, you can use OnMouseUp/OnMouseDown to provide a more responsive experience.


The following snippet illustrates this concept, and should replace the <Scripts> block above:
  <Scripts>
  <Scripts>
<OnLoad>
  <OnMouseDown>
  this:RegisterForDrag("LeftButton");
  if button == "LeftButton" and not self.isLocked then
</OnLoad>
    self:StartMoving();
<OnDragStart>
    self.isMoving = true;
  this:StartMoving();
  end
  this.isMoving = true;
  </OnMouseDown>
</OnDragStart>
  <OnMouseUp>
<OnDragStop>
  if button == "LeftButton" and self.isMoving then
  this:StopMovingOrSizing();
    self:StopMovingOrSizing();
  this.isMoving = false;
    self.isMoving = false;
</OnDragStop>
  end
  </OnMouseUp>
  <OnHide>
  if ( this.isMoving ) then
    this:StopMovingOrSizing();
    this.isMoving = false;
  end
  </OnHide>
  </Scripts>
  </Scripts>


== Advanced Dragging ==
Note: this also adds a frame.isLocked boolean field to control whether the frame can be dragged or not. There's generally no limit to the amount of conditional logic you can insert prior to :StartMoving() -- for instance, requiring the user to hold down some modifier button is a possible alternative.


Another way, which is more responsive but requires an onhide element so that the frame wont get stuck to the mouse:
=== Using TitleRegion ===
Frames can have a TitleRegion object that handles dragging automatically -- as long as the mouse is held down within the TitleRegion, it'll allow the frame to be dragged.


<Scripts>
The frame created below will be draggable by clicking on its top 20 pixels:
<OnMouseUp>
  <Frame enableMouse="true">
  if ( this.isMoving ) then
   <Size x="100" y="100"/>
  this:StopMovingOrSizing();
   <TitleRegion>
  this.isMoving = false;
  <Size x="100" y="20"/>
  end
  <Anchors><Anchor point="TOP"></Anchors>
</OnMouseUp>
<OnMouseDown>
  if ( ( ( not this.isLocked ) or ( this.isLocked == 0 ) ) and ( arg1 == "LeftButton" ) ) then
  this:StartMoving();
  this.isMoving = true;
  end
</OnMouseDown>
<OnHide>
  if ( this.isMoving ) then
  this:StopMovingOrSizing();
  this.isMoving = false;
  end
</OnHide>
</Scripts>
 
Note: this method also demonstrates an optional isLocked parameter to determine whether you can drag the frame or not.
 
== Parent Dragging ==
 
Some advanced dragging addons use overlays that make default Blizzard frames draggable.  This is possible by using GetParent when starting and stopping drag. To do this, one must make the parent frame movable through the use of the SetMovable widget function, i.e. <tt>''frame'':SetMovable(true)</tt>.  One drawback with overlay frames that are mouse enabled is that they will prevent the parent frame's click script tags from being called so you often have to simulate their click events.
 
== Quick Dragging Code ==
 
While somewhat untested there is an easier and more automatic way to activate dragging. If you have your <Frame> delcaration attributes "enableMouse" and "movable" set to true, dragging may be accomplished by adding a <TitleRegion> tag inside of your <Frame>
 
  <Frame name="myname" frameStrata="HIGH" toplevel="true" enableMouse="true" movable="true" parent="UIParent">
   <TitleRegion setAllPoints="true"/>
   ...
</Frame>
 
I haven't discovered any adverse side effects to doing this yet, I am not even sure if this is the intended use for it.
 
Using this method can result in the frame not responding to other mouse events, also both mouse buttons will drag the frame.
 
 
You can also specify <Size> and <Anchors> within <TitleRegion>, e.g.
 
<Frame name="myname" frameStrata="HIGH" toplevel="true" enableMouse="true" movable="true" parent="UIParent">
   <TitleRegion>
   <TitleRegion>
   <Size>
  <nowiki><!-- Tags below add a visual element to the frame. --></nowiki>
     <AbsDimension x="200" y="20"/>
  <Layers>
   </Size>
   <Layer level="ARTWORK">
  <Anchors>
     <Texture setAllPoints="true">
    <Anchor point="TOP"/>
    <Color r="1.0" g="0.5" b="0.0" a="0.5" />
  </Anchors>
    </Texture>
  </TitleRegion>
   </Layer>
  ...
  </Layers>
  <Anchors><Anchor point="CENTER" relativeTo="UIParent"/></Anchors>
  </Frame>
  </Frame>


This way, your <Frame> can still receive mouse events, and you can only drag it by clicking within its <TitleRegion>.
== Using Lua ==
 
The code sample below illustrates the use of the [[Widget API]] to achieve the same results as the XML above:
== Lua Only Approach ==
local frame = CreateFrame("Frame", "DragFrame2", UIParent)
 
  frame:SetMovable(true)
If your frame is called MyFrame -
  frame:EnableMouse(true)
 
  frame:SetScript("OnMouseDown", frame.StartMoving)
  MyFrame:SetMovable(true)
frame:SetScript("OnMouseUp", frame.StopMovingOrSizing)
  MyFrame:EnableMouse(true)
  -- The code below makes the frame visible, and is not necessary to enable dragging.
  MyFrame:SetScript("OnMouseDown",function()
  frame:SetPoint("CENTER"); frame:SetWidth(64); frame:SetHeight(64);
  MyFrame:StartMoving()
local tex = frame:CreateTexture("ARTWORK");
  end)
  tex:SetAllPoints();
  MyFrame:SetScript("OnMouseUp",function()
tex:SetTexture(1.0, 0.5, 0); tex:SetAlpha(0.5);
  MyFrame:StopMovingOrSizing()
  end)
 
 
[[Category: HOWTOs|Make a Frame Draggable]]

Revision as of 18:11, 28 December 2009

HOWTOs

Draggable frames can be moved by having the user hold down a mouse button over the frame, then move the mouse to reposition the frame. This HOWTO describes how to make a frame draggable.

Summary

To initiate dragging of a frame, call Frame:StartMoving(); to stop, call Frame:StopMovingOrSizing(). The frame must be flagged as movable (either through the movable attribute in its XML declaration or using Frame:SetMovable(isMovable).

While those functions can theoretically be called from anywhere, Frame:RegisterForDrag("button1", ...) allows the use of OnDragStart/OnDragStop widget handlers. Note that to receive mouse events (including dragging), the frame must be mouse enabled (either through the enableMouse attribute in its XML declaration or using Frame:EnableMouse(isEnabled)); buttons are mouse-enabled by default.

Using XML

The code below creates a draggable Frame widget and uses the Drag widget handlers to initiate dragging. Note the use of movable and enableMouse attributes:

<Frame name="DragFrame1" enableMouse="true" movable="true">
 <Scripts>
  <OnLoad>self:RegisterForDrag("LeftButton");</OnLoad>
  <OnDragStart>self:StartMoving();</OnDragStart>
  <OnDragStop>self:StopMovingOrSizing();</OnDragStop>
 </Scripts>
 <!-- Tags below add a visual element to the frame. -->
 <Layers>
  <Layer level="ARTWORK">
   <Texture setAllPoints="true">
    <Color r="1.0" g="0.5" b="0.0" a="0.5" />
   </Texture>
  </Layer>
 </Layers>
 <Size x="64" y="64" />
 <Anchors><Anchor point="CENTER" relativeTo="UIParent"/></Anchors>
</Frame>

Using OnMouseUp/OnMouseDown

The OnDrag* handlers typically require the mouse button to be held down for a small amount of time prior to enabling the mouse behavior, making them well suited for dragging widgets that normally respond to clicks. However, if the frame you wish to make draggable is not normally a button, you can use OnMouseUp/OnMouseDown to provide a more responsive experience.

The following snippet illustrates this concept, and should replace the <Scripts> block above:

<Scripts>
 <OnMouseDown>
  if button == "LeftButton" and not self.isLocked then
   self:StartMoving();
   self.isMoving = true;
  end
 </OnMouseDown>
 <OnMouseUp>
  if button == "LeftButton" and self.isMoving then
   self:StopMovingOrSizing();
   self.isMoving = false;
  end
 </OnMouseUp>
 <OnHide>
  if ( this.isMoving ) then
   this:StopMovingOrSizing();
   this.isMoving = false;
  end
 </OnHide>
</Scripts>

Note: this also adds a frame.isLocked boolean field to control whether the frame can be dragged or not. There's generally no limit to the amount of conditional logic you can insert prior to :StartMoving() -- for instance, requiring the user to hold down some modifier button is a possible alternative.

Using TitleRegion

Frames can have a TitleRegion object that handles dragging automatically -- as long as the mouse is held down within the TitleRegion, it'll allow the frame to be dragged.

The frame created below will be draggable by clicking on its top 20 pixels:

<Frame enableMouse="true">
 <Size x="100" y="100"/>
 <TitleRegion>
  <Size x="100" y="20"/>
  <Anchors><Anchor point="TOP"></Anchors>
 <TitleRegion>
 <!-- Tags below add a visual element to the frame. -->
 <Layers>
  <Layer level="ARTWORK">
   <Texture setAllPoints="true">
    <Color r="1.0" g="0.5" b="0.0" a="0.5" />
   </Texture>
  </Layer>
 </Layers>
 <Anchors><Anchor point="CENTER" relativeTo="UIParent"/></Anchors>
</Frame>

Using Lua

The code sample below illustrates the use of the Widget API to achieve the same results as the XML above:

local frame = CreateFrame("Frame", "DragFrame2", UIParent)
frame:SetMovable(true)
frame:EnableMouse(true)
frame:SetScript("OnMouseDown", frame.StartMoving)
frame:SetScript("OnMouseUp", frame.StopMovingOrSizing)
-- The code below makes the frame visible, and is not necessary to enable dragging.
frame:SetPoint("CENTER"); frame:SetWidth(64); frame:SetHeight(64);
local tex = frame:CreateTexture("ARTWORK");
tex:SetAllPoints();
tex:SetTexture(1.0, 0.5, 0); tex:SetAlpha(0.5);