Making draggable frames

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 ; to stop, call. The frame must be flagged as movable (either through the movable attribute in its XML declaration or using (isMovable).

There are generally 2 methods that are used for moving a frame, by Dragging the frame (OnDragStart & OnDragStop Handlers) or Clicking the frame (OnMouseDown & OnMouseUp 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 (isEnabled)); buttons are mouse-enabled by default.

Using OnDragStart / OnDragStop
The code below creates a draggable Frame widget and uses the OnDrag widget handlers to initiate dragging. Note the use of movable and enableMouse attributes:   self:RegisterForDrag("LeftButton"); self:StartMoving; self:StopMovingOrSizing;         <Size x="64" y="64" /> <Anchors><Anchor point="CENTER" relativeTo="UIParent"/></Anchors> </Frame>

Note the use of the OnLoad Script, In order to drag a frame it must register for drag via.

Using OnMouseUp/OnMouseDown
While 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  block above:  <OnMouseDown> if button == "LeftButton" and not self.isMoving 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 ( self.isMoving ) then self:StopMovingOrSizing; self.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>  <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 OnDragStart OnDragStop
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:RegisterForDrag("LeftButton") frame:SetScript("OnDragStart", frame.StartMoving) frame:SetScript("OnDragStop", 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);

Using OnMouseDown / OnMouseUp
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", function(self, button)  if button == "LeftButton" and not self.isMoving then    self:StartMoving;    self.isMoving = true;   end end) frame:SetScript("OnMouseUp", function(self, button)  if button == "LeftButton" and self.isMoving then    self:StopMovingOrSizing;    self.isMoving = false;   end end) frame:SetScript("OnHide", function(self)  if ( self.isMoving ) then    self:StopMovingOrSizing;    self.isMoving = false;   end end) -- 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);

General Notes
The use of the OnHide Handler is not entirely necessary. However it is a good idea if your frames are being Hidden or Shown by running code and not the User.

The use of full functions is also not required in Lua: frame:SetScript("OnDragStart" frame.StartMoving) frame:SetScript("OnDragStop" frame.StopMovingOrSizing)

frame:SetScript("OnMouseDown" frame.StartMoving) frame:SetScript("OnMouseUp" frame.StopMovingOrSizing)

In XML all that is required is just the function: self:StartMoving;</OnDragStart> self:StopMovingOrSizing;</OnDragStop>

<OnMouseDown>self:StartMoving;</OnMouseDown> <OnMouseUp>self:StopMovingOrSizing;</OnMouseUp>

If the frame you intend to move is not the frame you are setting the script on (e.g. ), it is best to use OnMouseDown/Up because OnDragStop will not fire when the mouse button is released which will leave the frame stuck on the cursor. This may be due to the fact that when you start moving a different frame (i.e. the frame's parent frame), the dragging of the source frame stops because either the mouse's focus is shifted to the other frame or the client can only keep track of one moving frame at a time. Either way, this happens before the client even knows it was dragging a frame.

You cannot use OnMouseDown to move a frame and OnDragStart/Stop at the same time on the same frame. This is because when you click the frame :StartMoving is immediately called and you can't drag a frame that's already moving.