WindowLib is a small library that takes care of standard "windowy" behavior used in the main frames of many addons, and attempts to do so in a smarter way than the average addon author would find time to do.

  • Save and restore positions: WindowLib will pick the attach point based on which quadrant of the screen the frame is in: top-left? bottom-right? center?
  • Handle window dragging
  • Mouse wheel zooming
  • Only mouse-enabling the window while Alt is held

Why bother?

Because users change their UI scale. Because users like to copy their addon settings from one computer to another (think desktop vs laptop?).

How scaling and positioning works with different approaches

If you absolutely do not want to use LibWindow, then please use its strategy in your code:

  • Save the position in the parent's scale, not the frame's own EffectiveScale. This prevents overlaps and gaps when changing scale.
  • Compute the closest screen corner and save relative to your frame's closest corner, i.e. bottomright-to-bottomright, topleft-to-topleft. This prevents things disappearing off screen.

Example code

lwin = LibStub("LibWindow-1.1")
myframe = CreateFrame("Frame") 

lwin.RegisterConfig(myframe, self.db.profile)
lwin.RestorePosition(myframe)  -- restores scale also


API Documentation


.RegisterConfig(frame, storage[, names])

 local lwin = LibStub("LibWindow-1.1")
 lwin.RegisterConfig(myframe, self.db.profile)

This call initializes a frame for use with LibWindow, and tells it where configuration data lives. Optionally, you can specify the exact names of the variables that will be saved.

Note that if your addon supports profiles (i.e. changing config data live), you'll need to do a new .RegisterConfig() followed by .RestorePosition() when the profile is changed over.


the frame to enable positioning support for
a table to store configuration in
''(optional)'' a table specifying which names to use in the storage table - see below

Manual variable naming

The optional last argument to the function is a table containing name mappings and/or a prefix for all variable names.

Example use - manually specify all variable names:

 names = {
   x = "posx",
   y = "posy",
   scale = "size",
   point = "whereToAttach",

Example use - just prefix most names, but hard-code one

 names = {
   prefix = "myframe",  -- names become e.g. "myframex", "myframey"
   point = "gluemyframe",



Computes which quadrant the frame lives in, and saves its position relative to the right corner.

 myframe:SetScript("OnDragStop", lwin.SavePosition)

You do not need to call this yourself if you used :MakeDraggable() on the frame.



Restores position and scale from configuration data.



.SetScale(frame, scale)

Sets the scale of the frame (without causing it to move, of course) and saves it.

 lwin.SetScale(myframe, myscale)



Adds drag handlers to the frame and makes it movable. Positioning information is automatically stored according to :RegisterConfig().

  • You can of course also handle dragging yourself, in which case you instead call .SavePosition(frame) when dragging is done.
  • If you like, you can manually call .OnDragStart(frame) and .OnDragStop(frame) which do the necessary API calls for you + call .SavePosition(frame).



Only mouse-enables the window while [Alt] is held. This lets users click through the window normally.




Adds mousewheel handlers to the frame, automatically increasing/decreasing scale by 10% per wheel tick and calling .SetScale(myframe) for you.


You can also manually call .OnMouseWheel(frame,dir). This would be of interest if e.g. your addon already uses the mouse wheel for something. I recommend using ctrl as the modifier key for scaling - it is already in use by other addons.




On positioning logic

WindowLib will pick the attach point based on which quadrant of the screen the frame is in: top-left? bottom-right? center?

  • This means that frames do NOT necessarily stay exactly in place when the UI is resized
  • This is a GOOD THING! Much better than static positioning (i.e. frame:GetLeft() * frame:GetEffectiveScale() - which does not do a very good job!)
  • Two frames sitting next to each other when the UI is resized, will ''keep'' sitting next to each other with this relative positioning. (For example: Think of how your Bags sit next to each other, regardless of UI scale!)


Adding LibWindow to an existing addon

You will likely have window positioning data saved in your database since before. This data will likely need to be converted before LibWindow will handle it well.

LibWindow remembers position in the parent's scale. BUT: most addons translate to global scale and save that. This is not compatible unless your UIParent is scale 1.0.

Example old code:

 function MyAddon:SavePosition(frame)
   local p = self.db.profile
   p.x = frame:GetLeft() / frame:GetEffectiveScale()
   p.y = frame:GetTop() / frame:GetEffectiveScale()

Example code to patch the scale for LibWindow use:

 function MyAddon:OnProfileEnable()
   local p = self.db.profile
   if not p.libwindowed then
     p.x = p.x / UIParent:GetScale()
     p.y = p.y / UIParent:GetScale()
     p.libwindowed = true
   -- now it's safe to call .RestorePosition()