changeset 99:7d94df3804a7

- Console drag buttons for resizing - While dragging a console frame, other frames are ghosted out - Dropdown menu includes Toggle, Pin, and MinMax
author Nenue
date Thu, 27 Oct 2016 06:11:04 -0400
parents 33bc8baba858
children 790dca545f1d
files Console.lua Console.xml Devian.lua Devian.xml Dock.lua Dock.xml UI.lua
diffstat 7 files changed, 381 insertions(+), 612 deletions(-) [+]
line wrap: on
line diff
--- a/Console.lua	Wed Oct 26 10:17:43 2016 -0400
+++ b/Console.lua	Thu Oct 27 06:11:04 2016 -0400
@@ -2,17 +2,53 @@
 local _, D = ...
 DevianConsoleMixin = {}
 
+local print = function(...) print('Dvn', ...) end
+function D.IterateChannels(callback, sender)
+  for index, channel in ipairs(D.console) do
+    if channel ~= sender then
+      callback(channel)
+    end
+  end
+end
+
 function DevianConsoleMixin:OnLoad()
   self:SetMaxResize(GetScreenWidth(), GetScreenHeight())
   self:SetMinResize(100, 24)
-
   self:EnableMouse(true)
   self:RegisterForDrag('LeftButton')
   self:SetMovable(true)
   self:SetResizable(true)
+  self:SetClampedToScreen(true)
   self.out:SetFont("Interface\\Addons\\Devian\\font\\SourceCodePro-Regular.ttf", 13, 'NORMAL')
   self.out:SetJustifyH('LEFT')
   self.out:SetFading(false)
+  self.out:SetMaxLines(2048)
+
+  self:RegisterEvent('PLAYER_STARTED_MOVING')
+  self:RegisterEvent('PLAYER_STOPPED_MOVING')
+
+  UIDropDownMenu_Initialize(self.DropdownFrame, function()
+    local info = UIDropDownMenu_CreateInfo()
+    info.notCheckable = 1
+    info.text = 'Pin'
+    info.func = function() self:Pin() end
+    UIDropDownMenu_AddButton(info)
+    if self.minimized then
+      info.text = 'Maximize'
+      info.func = function() self:Maximize() end
+    else
+      info.text = 'Minimize'
+      info.func = function() self:Minimize() end
+    end
+    UIDropDownMenu_AddButton(info)
+    info.text = 'Close'
+    info.func = function() self:Toggle() end
+    UIDropDownMenu_AddButton(info)
+
+  end, 'MENU')
+  self.DropdownButton:SetScript('OnClick', function(button)
+    ToggleDropDownMenu(1, nil, self.DropdownFrame, button, 0, 0)
+  end)
 
   self.width = self:GetWidth()
   self.height = self:GetWidth()
@@ -27,10 +63,47 @@
   self:Update()
 end
 
-function DevianConsoleMixin:Update()
+local blockedType = {['table'] = true, ['function'] = true }
+local blockedKey = {[0] = true }
+
+-- Synchronize vars
+function DevianConsoleMixin:Finalize()
+  local id = self:GetID()
+  for k,v in pairs(D.channels[id]) do
+    if not self[k] then
+      D.channels[id][k] = nil
+    end
+  end
+  for k,v in pairs(self) do
+    if not (blockedType[type(v)] or blockedKey[k]) then
+      if D.channels[id][k] ~= v then
+        D.channels[id][k] = v
+      end
+    end
+  end
+end
+
+function DevianConsoleMixin:Pin(pinned)
+  self.pinned = pinned or (not self.pinned)
+  self:Update(true)
+end
+
+function DevianConsoleMixin:Update(setFinal)
   self.title:SetText(self.index..' '.. (self.signature or '?'))
-  self:SetSize(self.width, self.height)
   self:SetPoint('TOPLEFT', UIParent, 'TOPLEFT', self.x, self.y)
+
+  if self.minimized then
+    self:SetHeight(20)
+    self:SetMaxResize(GetScreenWidth(),20)
+    self.GripBottom:Hide()
+    self.GripSide:Hide()
+  else
+    self:SetSize(self.width, self.height)
+    self.GripBottom:Show()
+    self.GripSide:Show()
+  end
+
+
   --  oldprint(self:GetName(), self.x, self.y)
 
   local isFront = D.currentProfile.current_channel == self.index
@@ -49,10 +122,18 @@
   --oldprint(self:GetID(), self.enabled, self.minimized, self.x, self.y)
   self.isFront = isFront
   self:SetShown(self.enabled)
-  self.out:SetShown(self.enabled)
+  self.backdrop:SetShown(self.enabled)
+  self.out:SetShown((not self.minimized))
+
+
+  if setFinal then
+    self:Finalize()
+  end
+
 end
 
 
+
 function DevianConsoleMixin:OnShow()
   self:Update()
 end
@@ -85,22 +166,23 @@
 end
 
 function DevianConsoleMixin:Minimize()
-  self:SetHeight(20)
-  self:SetMaxResize(GetScreenWidth(),20)
   self.minimized = true
-  self.out:Hide()
-  D.channels[self.index].minimized = true
+  self:Update(true)
 end
 
 function DevianConsoleMixin:Maximize()
-  local db = D.channels[self.index]
-  self:SetHeight(db.height)
-  self:SetMaxResize(GetScreenWidth(),GetScreenHeight())
-  self.minimized = nil
-  self.out:Show()
-  D.channels[self.index].minimized = nil
+  self.minimized = nilnil
+  self:Update(true)
 end
 
+function DevianConsoleMixin:OnMouseDown(button)
+
+  if button == 'LeftButton' then
+    self:ToFront()
+  end
+end
+
+
 function DevianConsoleMixin:OnMouseUp(button)
   if button == 'LeftButton' then
     self:ToFront()
@@ -116,19 +198,33 @@
 end
 
 function DevianConsoleMixin:OnDragStart()
-
-    self:StartMoving()
+  self:ToFront()
+  self:StartMoving()
+  D.IterateChannels(function(frame)
+    frame.out:SetShown(false)
+    frame.backdrop:Hide()
+  end, self)
 end
 
 function DevianConsoleMixin:OnDragStop()
+
+  local channelEntry = D.channels[self:GetID()]
   self.x = self:GetLeft()
   self.y = self:GetTop() - GetScreenHeight()
-  D.currentProfile.channels[self:GetID()].x = self:GetLeft()
-  D.currentProfile.channels[self:GetID()].y = self:GetTop() - GetScreenHeight()
+  if not self.minimized then
+    self.width = self:GetWidth()
+    self.height = self:GetHeight()
+  end
+
+  --print(channelEntry.x, channelEntry.y, channelEntry.width, channelEntry.height)
   self:StopMovingOrSizing()
-end
+  self:SetClampRectInsets(0,0,0,0)
 
-function DevianConsoleMixin:Reset()
+  self:Update(true)
+
+  D.IterateChannels(function(frame)
+    frame:Update()
+  end, self)
 end
 
 function DevianConsoleMixin:ToFront()
@@ -143,4 +239,52 @@
   self.enabled = (not self.enabled)
   --oldprint(self:GetID(), self.enabled)
   self:Update()
-end
\ No newline at end of file
+end
+
+function DevianConsoleMixin:OnEvent(event, arg)
+  oldprint(event, arg)
+  local db = D.db
+  if self.enabled then
+    if event == 'PLAYER_STARTED_MOVING' then
+      self.moveFade:GetProgress()
+      self.moveFade:Stop()
+      local F1 = self.moveFade.alphaOut
+      F1:SetFromAlpha(db.movement_fade_from)
+      F1:SetToAlpha(db.movement_fade_to)
+      F1:SetDuration(db.movement_fade_time)
+      self.moveFade:Play()
+      self:EnableMouse(false)
+    else
+      self.moveFade:Stop()
+      local F1 = self.moveFade.alphaOut
+      F1:SetToAlpha(db.movement_fade_from)
+      F1:SetFromAlpha(db.movement_fade_to)
+      F1:SetDuration(db.movement_fade_time)
+      self.moveFade:Play()
+      self:EnableMouse(true)
+    end
+  end
+end
+
+DevianConsoleSizeButtonMixin = {}
+function DevianConsoleSizeButtonMixin:OnLoad()
+  self:RegisterForDrag('LeftButton')
+end
+function DevianConsoleSizeButtonMixin:OnDragStart()
+  local anchor = self:GetPoint(2)
+  local frame = self:GetParent()
+  if anchor == 'BOTTOMLEFT' then
+    frame:SetClampRectInsets(frame:GetLeft(), GetScreenWidth()- frame:GetRight(), GetScreenHeight() - frame:GetTop(), 0)
+    frame:SetMinResize(frame.width, 24)
+    frame:SetMaxResize(frame.width, GetScreenHeight())
+  elseif anchor == 'BOTTOM' then
+    frame:SetClampRectInsets(frame:GetLeft(), 0, GetScreenHeight() - frame:GetTop(), GetScreenHeight() - frame:GetBottom())
+    frame:SetMinResize(200, frame.height)
+    frame:SetMaxResize(GetScreenWidth(), frame.height)
+  end
+  frame:StartSizing()
+end
+function DevianConsoleSizeButtonMixin:OnDragStop()
+  local frame = self:GetParent()
+  frame:OnDragStop()
+end
--- a/Console.xml	Wed Oct 26 10:17:43 2016 -0400
+++ b/Console.xml	Thu Oct 27 06:11:04 2016 -0400
@@ -2,6 +2,19 @@
 ..\FrameXML\UI.xsd">
   <Script file="Console.lua" />
 
+  <Button name="DevianSizeButtonTemplate" virtual="true" mixin="DevianConsoleSizeButtonMixin">
+    <PushedTexture setAllPoints="true">
+      <Color r="1" g="1" b="1" a="0.5" />
+    </PushedTexture>
+    <HighlightTexture setAllPoints="true">
+      <Color r="1" g="0.5" b="0" a="0.2" />
+    </HighlightTexture>
+    <Scripts>
+      <OnLoad method="OnLoad" />
+      <OnDragStart method="OnDragStart" />
+      <OnDragStop method="OnDragStop" />
+    </Scripts>
+  </Button>
 
   <Frame name="DevianConsoleTemplate" toplevel="true" parent="UIParent" mixin="DevianConsoleMixin" virtual="true" hidden="true">
     <Scripts>
@@ -10,6 +23,7 @@
       <OnMouseWheel method="OnMouseWheel" />
       <OnDragStart method="OnDragStart" />
       <OnDragStop method="OnDragStop" />
+      <OnMouseDown method="OnMouseDown" />
       <OnMouseUp method="OnMouseUp" />
       <OnEnter method="OnEnter" />
       <OnLeave method="OnLeave" />
@@ -82,29 +96,32 @@
         </Texture>
       </Layer>
 
-      <Layer level="ARTWORK">
+      <Layer level="BACKGROUND">
         <Texture name="$parentBackdrop" parentKey="backdrop" setAllPoints="true" alphaMode="BLEND">
           <Color r="1" g="1" b="1" a="1"/>
         </Texture>
       </Layer>
     </Layers>
     <Frames>
-      <Button name="$parentMenuButton"  parentKey="dropmenu" inherits="DevianDDButton" enableMouse="true">
+      <Button name="$parentMenuButton"  parentKey="DropdownButton" enableMouse="true">
+        <Size x="24" y="24" />
         <Anchors>
           <Anchor point="TOPRIGHT" relativePoint="TOPRIGHT" x="-4" y="0" />
         </Anchors>
+        <Layers>
+          <Layer level="BACKGROUND">
+            <Texture setAllPoints="true" >
+              <Color a="1" r="1" g="1" b="1" />
+            </Texture>
+          </Layer>
+        </Layers>
       </Button>
 
-      <Frame name="$parentDDMenu" parentKey="menuFrame" id="1">
-        <Scripts>
-          <OnLoad>
-          </OnLoad>
-        </Scripts>
-      </Frame>
+      <Frame name="$parentDDMenu" parentKey="DropdownFrame" inherits="UIDropDownMenuTemplate" id="1" />
       <ScrollingMessageFrame inherits="DevianBuffer" />
 
 
-      <ScrollingMessageFrame parentKey="out" name="$parentScrollFrame" maxLines="500" fade="false" displayDuration="3600" fadeDuration="3600" insertMode="BOTTOM">
+      <ScrollingMessageFrame parentKey="out" name="$parentScrollFrame" frameLevel="12">
         <Anchors>
           <Anchor point="TOPLEFT" x="3" y="-20" />
           <Anchor point="BOTTOMRIGHT" x="-3" y="0" />
@@ -114,7 +131,7 @@
           </Layer>
         </Layers>
       </ScrollingMessageFrame>
-      <Button name="$parentResizeButton" parentKey="ResizeButton">
+      <Button name="$parentResizeButton" parentKey="ResizeButton" frameLevel="23">
         <Scripts>
           <OnLoad>
             self:RegisterForDrag('LeftButton')
@@ -123,26 +140,53 @@
             self:GetParent():StartSizing()
           </OnDragStart>
           <OnDragStop>
-            self:GetParent():StopMovingOrSizing()
+            self:GetParent():OnDragStop()
           </OnDragStop>
+          <OnEnter>
+            self.Icon:SetVertexColor(1,1,1,1)
+          </OnEnter>
+          <OnLeave>
+            self.Icon:SetVertexColor(0.4,0.4,0.4,1)
+          </OnLeave>
         </Scripts>
         <Anchors>
           <Anchor point="BOTTOMRIGHT" x="-1" y="1" />
         </Anchors>
-        <Size x="18" y="18" />
+        <Size x="24" y="24" />
         <Layers>
+          <Layer level="BACKGROUND">
+            <Texture setAllPoints="true" parentKey="Background" />
+          </Layer>
           <Layer level="ARTWORK">
-            <Texture name="DevianGripperRight" file="Interface\Addons\Devian\corner.blp" parentKey="grip" alphaMode="ADD">
+
+            <Texture file="Interface\Addons\Devian\corner.blp" parentKey="Icon" alphaMode="ADD">
+              <Size x="24" y="24" />
               <Color r="0.4" g="0.4" b="0.4" a="1"/>
-              <Gradient orientation="VERTICAL">
-                <MaxColor r="0.9" g="0.9" b="0.9" a="1"/>
-                <MinColor r="0.4" g="0.4" b="0.4" a="1"/>
-              </Gradient>
+              <Anchors>
+                <Anchor point="BOTTOMRIGHT" />
+              </Anchors>
             </Texture>
           </Layer>
         </Layers>
       </Button>
 
+
+      <Button name="$parentHorizontalSizer" parentKey="GripSide" inherits="DevianSizeButtonTemplate" frameLevel="25">
+        <Size x="16" />
+        <Anchors>
+          <Anchor point="TOPRIGHT"/>
+          <Anchor point="BOTTOM" y="24" />
+        </Anchors>
+      </Button>
+
+      <Button name="$parentVerticalSizer" parentKey="GripBottom" inherits="DevianSizeButtonTemplate" frameLevel="24">
+        <Size y="16" />
+        <Anchors>
+          <Anchor point="BOTTOMLEFT" />
+          <Anchor point="RIGHT" x="-24" />
+        </Anchors>
+      </Button>
+
     </Frames>
     <Animations>
       <AnimationGroup name="moveFade" parentKey="moveFade" setToFinalAlpha="true">
--- a/Devian.lua	Wed Oct 26 10:17:43 2016 -0400
+++ b/Devian.lua	Thu Oct 27 06:11:04 2016 -0400
@@ -223,14 +223,18 @@
       return (b.index > a.index)
     end)
     for i, info in ipairs(sortedChannels) do
+
+      info.tags = info.tags or {}
       for tag, tagSet in pairs(currentProfile.tags) do
         for _, index in pairs(tagSet) do
           if index == info.index then
             sortedTags[tag] = sortedTags[tag] or {}
             sortedTags[tag][i] = i
+            tinsert(info.tags, tag)
           end
         end
       end
+      print('Set tags:', table.concat(info.tags, ', '))
 
       info.index = i
     end
@@ -287,7 +291,6 @@
     DEVIAN_WORKSPACE = true
     DEVIAN_PNAME = currentProfile.name
     DEVIAN_PID = id
-    print('setting phandler')
     setprinthandler(D.Message)
   else
     DEVIAN_WORKSPACE = false
@@ -522,59 +525,62 @@
 end
 
 D.UpdateTags = function()
-
   wipe(registeredTags)
-  for tag, tagSet in pairs(currentProfile.tags) do
-    registeredTags[tag] = registeredTags[tag] or {}
-    for _, id in pairs(tagSet) do
-      if D.console[id] then
-        tinsert(registeredTags[tag], D.console[id])
-      end
+  for index, channel in ipairs(D.channels) do
+    for _, tag in ipairs(channel.tags) do
+      registeredTags[tag] = registeredTags[tag] or {}
+      tinsert(registeredTags[tag], D.console[index])
     end
   end
 end
 
-D.Tag = function(self, tag, dest)
+D.Tag = function(self, tag, id)
   local sig
-  if tag ~= nil and dest ~= nil then
+  if tag and id then
     --@debug@
     --print(tag, dest)
     --@end-debug@
 
     -- convert to ID
-    if tonumber(dest) == nil then
-      if D.sigID[dest] then
-        dest = currentProfile.channels[D.sigID[dest]].index
-      else
-        sig = dest
+    local channel, sig
+    if tonumber(id) == nil then
+      sig = id
+      if D.sigID[id] then
+        id = D.sigID[id]
+        channel = D.channels[id]
       end
     else
-      dest = tonumber(dest)
+      id = tonumber(id)
+      channel = D.channels[id]
     end
-    --@debug@
-    --print('2 tag,dest,sig', tag, dest, sig)--@end-debug@
 
-    -- make a new channel?
-    local channel
-    if not currentProfile.channels[dest] then
-      dest = #D.channels + 1
-      D:Print(L('New channel created', sig and (dest..':'..sig) or dest ))
-      channel = D:GetOrCreateChannel(dest, tag)
+    -- if channel is still nil, create one
+    if not channel then
+      id = #D.channels + 1
+      D:Print(L('New channel created', (sig and (id..':'..sig)) or id))
+      channel = D:GetOrCreateChannel(id, sig)
     else
-      channel = currentProfile.channels[dest]
+      sig = channel.signature
     end
     --@debug@
     --print('3 tag,dest,channel.sig=',tag, dest, channel.signature)--@end-debug@
 
     if not currentProfile.tags[tag] then -- no tag table?
-    currentProfile.tags[tag] = {}
+      currentProfile.tags[tag] = {}
     end
 
-    if currentProfile.tags[tag][dest] then -- is tag set?
-      currentProfile.tags[tag][dest] = nil
-      D:Print(L('Tag removed from channel', tag, currentProfile.channels[dest].index, currentProfile.channels[dest].signature))
+    local existingTag = tContains(channel.tags, tag)
+    if existingTag then -- is tag set?
+
+      for i, tag in ipairs(channel.tags) do
+        if tag == tag then
+          tremove(channel.tags, i)
+          D:Print(L('Tag removed from channel', tag, channel.index, channel.signature))
+          break
+        end
+      end
     else
-      currentProfile.tags[tag][dest] = dest
+      tinsert(channel.tags, tag)
       D:Print(L('Tag added to channel', tag, channel.index, channel.signature))
     end
     D.UpdateTags()
@@ -860,25 +866,43 @@
 end
 
 function D:GetOrCreateChannel(id, name)
+  id = id or (#D.channels + 1)
+
   local info = D.channels[id]
+  if not info then
+    --print('new channel')
+    name = name or ('Channel ' .. id)
+    info = {
+      index = id,
+      signature = name,
+      tags = {}
+    }
+    D.DeepCopy(defaults.default_channel, info)
+    D.channels[id] = info
+  elseif not info.tags then
+    -- fix old data?
+    info.tags = {info.signature}
+    oldprint(D.db)
+    for tag, tagSet in pairs(D.tags) do
+      for _, index in pairs(tagSet) do
+        if index == id then
+          tinsert(info.tags, tag)
+        end
+      end
+    end
+  end
+
   local frame = D.console[id]
   if not frame then
 
-    --print('new frame')
+    if DEVIAN_WORKSPACE then
+      D:Print('Adding '.. (info.index) .. ':' .. (info.signature) .. ' (|cFF00FFFF'.. concat(info.tags, '|r; |cFF00FFFF')..'|r)')
+    end
     frame = CreateFrame('Frame', 'DevianConsole'..id, Devian, 'DevianConsoleTemplate')
     frame:SetID(id)
     D.console[id] = frame
+    D.sigID[info.signature] = id
   end
-  if not info then
-    --print('new channel')
-    info = {
-    }
-    D.DeepCopy(defaults.default_channel, info)
-    info.index = id
-    info.signature = name
-    D.channels[id] = info
-  end
-
 
   frame:Setup(info)
   return frame
--- a/Devian.xml	Wed Oct 26 10:17:43 2016 -0400
+++ b/Devian.xml	Thu Oct 27 06:11:04 2016 -0400
@@ -81,58 +81,4 @@
     </Layers>
   </Button>
 
-  <Button name="DevianBeacon" hidden="true" virtual="true" enableMouse="true" alpha="1" frameStrata="MEDIUM" mixin="DevianDockButtonMixin">
-    <Size x="64" y="24" />
-
-    <Scripts>
-      <OnShow method="OnShow" />
-      <OnEnter method="OnEnter" />
-      <OnMouseDown method="OnMouseDown" />
-      <OnClick method="Select" />
-    </Scripts>
-    <Layers>
-      <Layer level="BACKGROUND">
-        <Texture parentKey="Background" setAllPoints="true">
-          <Color a="0.5" r="0" g="0" b="0" />
-        </Texture>
-      </Layer>
-      <Layer level="OVERLAY">
-        <Texture parentKey="Stripe">
-          <Anchors>
-            <Anchor point="TOPLEFT" />
-            <Anchor point="BOTTOMRIGHT" relativePoint="TOPRIGHT" x="0" y="-3" />
-          </Anchors>
-        </Texture>
-      </Layer>
-    </Layers>
-    <Frames>
-      <Frame name="$parentText" parentKey="caption" alpha="1" blendMode="BLEND" enableMouse="false">
-        <Size x="64" y="24" />
-        <Anchors>
-          <Anchor point="BOTTOM" relativePoint="BOTTOM" x="0" y="0" />
-        </Anchors>
-        <Layers>
-          <Layer level="OVERLAY">
-            <FontString text="Test String" inherits="DevianDockHeader" parentKey="name" wordwrap="false" setAllPoints="true">
-              <Anchors>
-                <Anchor point="BOTTOM" />
-              </Anchors>
-              <Color r="1" g="1" b="1" a="1" />
-              <Shadow>
-                <Color r="0" g="0" b="0" a="1" />
-                <Offset x="0" y="-2" />
-              </Shadow>
-            </FontString>
-          </Layer>
-        </Layers>
-        <Animations>
-          <AnimationGroup parentKey="pulse" looping="BOUNCE" setToFinalAlpha="false">
-            <Alpha fromAlpha=".5" toAlpha="1" duration="0.4" endDelay="0.6" order="1" childKey="name" />
-
-          </AnimationGroup>
-        </Animations>
-      </Frame>
-    </Frames>
-  </Button>
-
 </Ui>
\ No newline at end of file
--- a/Dock.lua	Wed Oct 26 10:17:43 2016 -0400
+++ b/Dock.lua	Thu Oct 27 06:11:04 2016 -0400
@@ -6,6 +6,9 @@
 -- Docking and arrangement calls
 local _, D = ...
 local ceil, floor, sqrt, pairs, GetScreenWidth, GetScreenHeight =  math.ceil, math.floor, math.sqrt, pairs, GetScreenWidth, GetScreenHeight
+
+local CreateFrame, tinsert, random = CreateFrame, tinsert, math.random
+local ipairs, pairs = ipairs, pairs
 local db
 local print = DEVIAN_WORKSPACE and function(...) print('DvnDock', ...) end or nop
 local DOCK_BUTTON_PADDING = 6
@@ -14,54 +17,9 @@
   usedButtons = {},
   buttons = {},
 }
-DevianDockButtonMixin = {}
+DevianDockTabMixin = {}
 
---- Updates region visibility as needed
-local getFadeInArgs = function(sign, region)
-  --print('Dvn', region)
-  local db = D.db
-  local alph = region:GetAlpha()
-  local a = (db[sign..'_alpha_on'] - alph)
-  local b = (db[sign..'_alpha_on']-db[sign..'_alpha_off'])
-  local dur = (a / b) * db[sign..'_fade_in']
-  return dur, alph, db[sign..'_alpha_on']
-end
-
-local getFadeOutArgs = function(sign, region)
-  local db = D.db
-  local alph = region:GetAlpha()
-  local a = (alph  - db[sign..'_alpha_off'])
-  local b = (db[sign..'_alpha_on']-db[sign..'_alpha_off'])
-  local dur = (a / b) * db[sign..'_fade_out']
-  return dur, alph, db[sign..'_alpha_off']
-end
-
-local function queueFade (self, duration, from, to)
-  self:SetAlpha(to)
-end
-
-local numBeacons = 0
-function DevianDockHandler:GetDockButton(console)
-  self.usedButtons = self.usedButtons or {}
-  local index = console:GetID()
-  local button = self.usedButtons[index]
-  if not button then
-    numBeacons = numBeacons + 1
-    button = CreateFrame('Button', 'DevianDockBeacon'.. numBeacons, UIParent, 'DevianBeacon')
-    button.color = {r = math.random(), g = math.random(), b = math.random()}
-    button.Stripe:SetColorTexture(button.color.r, button.color.g, button.color.b,1)
-    button.console = console
-    self.usedButtons[index] = button
-    tinsert(self.buttons, button)
-    --oldprint('create dock', index, console.signature)
-  end
-  button.index = console.index
-  button.caption.name:SetText(console.signature)
-  button:SetShown(true)
-  return button
-end
-
-function DevianDockButtonMixin:OnMouseDown(button)
+function DevianDockTabMixin:OnMouseDown(button)
   --print("click", self:GetName(), button, self.console.index)
   if button == "LeftButton" then
     if IsShiftKeyDown() then
@@ -84,12 +42,12 @@
     self.console:MinMax()
   end
 end
-function DevianDockButtonMixin:OnShow()
+function DevianDockTabMixin:OnShow()
   self:Update()
 end
-function DevianDockButtonMixin:OnEnter()
+function DevianDockTabMixin:OnEnter()
 end
-function DevianDockButtonMixin:Update()
+function DevianDockTabMixin:Update()
   local db = D.db
   local isActive = (self.raised or self.selected or self.newMessage)
 
@@ -115,7 +73,7 @@
   self:SetWidth(self.caption.name:GetStringWidth() + DOCK_BUTTON_PADDING)
 end
 
-function DevianDockButtonMixin:Select()
+function DevianDockTabMixin:Select()
   self.caption.pulse:Stop()
   self:Update()
 end
@@ -153,6 +111,30 @@
   end
 end
 
+
+do
+  local numBeacons = 0
+  function DevianDockHandler:GetDockButton(console)
+    self.usedButtons = self.usedButtons or {}
+    local index = console:GetID()
+    local button = self.usedButtons[index]
+    if not button then
+      numBeacons = numBeacons + 1
+      button = CreateFrame('Button', 'DevianDockBeacon'.. numBeacons, UIParent, 'DevianDockTabTemplate')
+      button.color = {r = random(), g = random(), b = random()}
+      button.Stripe:SetColorTexture(button.color.r, button.color.g, button.color.b,1)
+      button.console = console
+      self.usedButtons[index] = button
+      tinsert(self.buttons, button)
+      --oldprint('create dock', index, console.signature)
+    end
+    button.index = console.index
+    button.caption.name:SetText(console.signature)
+    button:SetShown(true)
+    return button
+  end
+end
+
 function DevianDockHandler:OnMouseWheel(delta)
   if delta >= 1 then
     self.dockScale = (self.dockScale or 1) - 0.1
@@ -189,10 +171,3 @@
   D.db.dockPoint = D.db.dockPoint or 'TOPLEFT'
   self:SetPoint(D.db.dockPoint , UIParent, D.db.dockPoint , 0, 0)
 end
-
-
-local function FrameFade(frame)
-  if not D.fader then
-    D.fader = CreateFrame('Frame', 'DevianFaderFrame', UIParent):CreateAnimationGroup('fader'):CreateAnimation('Alpha', 'FadeIn')
-  end
-end
\ No newline at end of file
--- a/Dock.xml	Wed Oct 26 10:17:43 2016 -0400
+++ b/Dock.xml	Thu Oct 27 06:11:04 2016 -0400
@@ -13,7 +13,7 @@
       alpha="0.1"
       hidden="true"
       mixin="DevianDockHandler"
-      frameStrata="LOW">
+      frameStrata="DIALOG">
     <Scripts>
       <OnMouseWheel method="OnMouseWheel" />
     </Scripts>
@@ -34,4 +34,60 @@
       </Layer>
     </Layers>
   </Frame>
+
+
+  <Button name="DevianDockTabTemplate" hidden="true" virtual="true" enableMouse="true" alpha="1" frameStrata="MEDIUM" mixin="DevianDockTabMixin">
+    <Size x="64" y="24" />
+
+    <Scripts>
+      <OnShow method="OnShow" />
+      <OnEnter method="OnEnter" />
+      <OnMouseDown method="OnMouseDown" />
+      <OnClick method="Select" />
+    </Scripts>
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture parentKey="Background" setAllPoints="true">
+          <Color a="0.5" r="0" g="0" b="0" />
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <Texture parentKey="Stripe">
+          <Anchors>
+            <Anchor point="TOPLEFT" />
+            <Anchor point="BOTTOMRIGHT" relativePoint="TOPRIGHT" x="0" y="-3" />
+          </Anchors>
+        </Texture>
+      </Layer>
+    </Layers>
+    <Frames>
+      <Frame name="$parentText" parentKey="caption" alpha="1" blendMode="BLEND" enableMouse="false">
+        <Size x="64" y="24" />
+        <Anchors>
+          <Anchor point="BOTTOM" relativePoint="BOTTOM" x="0" y="0" />
+        </Anchors>
+        <Layers>
+          <Layer level="OVERLAY">
+            <FontString text="Test String" inherits="DevianDockHeader" parentKey="name" wordwrap="false" setAllPoints="true">
+              <Anchors>
+                <Anchor point="BOTTOM" x="0" y="2" />
+              </Anchors>
+              <Color r="1" g="1" b="1" a="1" />
+              <Shadow>
+                <Color r="0" g="0" b="0" a="1" />
+                <Offset x="0" y="-2" />
+              </Shadow>
+            </FontString>
+          </Layer>
+        </Layers>
+        <Animations>
+          <AnimationGroup parentKey="pulse" looping="BOUNCE" setToFinalAlpha="false">
+            <Alpha fromAlpha=".5" toAlpha="1" duration="0.4" endDelay="0.6" order="1" childKey="name" />
+
+          </AnimationGroup>
+        </Animations>
+      </Frame>
+    </Frames>
+  </Button>
+
 </Ui>
\ No newline at end of file
--- a/UI.lua	Wed Oct 26 10:17:43 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,438 +0,0 @@
---- Devian - UI.lua
--- @file-author@
--- @project-revision@ @project-hash@
--- @file-revision@ @file-hash@
--- Created: 12/27/2015 3:01 AM
-if not LibStub then
-  print('Something has happened...')
-end
-local _, D = ...
-local DEVIAN_FRAME = 'DevianConsole'
-local insert, tonumber, pairs, concat = tinsert, tonumber, pairs, table.concat
-local L = D.L
-local print = D.print
-
-
-
-
-local Console_OnMovementChanged = function(self, event)
-  local db = D.db
-  if self.enabled then
-    if event == 'PLAYER_STARTED_MOVING' then
-      self.moveFade:Stop()
-      local F1 = self.moveFade.alphaOut
-      F1:SetFromAlpha(db.movement_fade_from)
-      F1:SetToAlpha(db.movement_fade_to)
-      F1:SetDuration(db.movement_fade_time)
-      self.moveFade:Play()
-      self:EnableMouse(false)
-    else
-      self.moveFade:Stop()
-      local F1 = self.moveFade.alphaOut
-      F1:SetToAlpha(db.movement_fade_from)
-      F1:SetFromAlpha(db.movement_fade_to)
-      F1:SetDuration(db.movement_fade_time)
-      self.moveFade:Play()
-      self:EnableMouse(true)
-    end
-  end
-end
-
-
-
-local function Console_MinMax(self)
-end
-
-local function Console_Minimize(self)
-end
-
-local function Console_Maximize(self)
-  self:Save()
-end
-
-
-local function Console_Save(self)
-  local db = D.currentProfile.channels[self.index]
-    db.x = self:GetLeft()
-    db.y = (self:GetTop() - GetScreenHeight())
-    db.width = self:GetWidth()
-
-  if not self.minimized then
-    db.height = self:GetHeight()
-    self:SetHeight(db.height)
-  end
-
-  db.dockedTo = self.dockedTo
-  db.docked = self.docked
-
-  db.minimized = self.minimized
-  db.enabled = self.enabled
-  db.active = self.active
-  self:SetPoint('TOPLEFT', UIParent, 'TOPLEFT', db.x, db.y)
-  self:SetWidth(db.width)
-end
-
---- Brings the console to the front.
--- Frame method used to bring a console frame to the front of the display stack.
-local function Console_ToFront(c)
-  local db = D.db
-  local profile = D.currentProfile
-  c:Raise()
-  c:SetAlpha(db.frontalpha)
-  c.out.backdrop:SetColorTexture(unpack(db.frontdrop))
-  c.out.backdrop:SetGradientAlpha(unpack(db.frontgrad))
-  c.out.backdrop:SetBlendMode(db.frontblend)
-  c.dropmenu.icon:SetVertexColor(unpack(db.frontheader))
-  c.title:SetTextColor(unpack(db.frontborder))
-
-  --oldprint('changing current toplevel from', db.current_channel, 'to', c.index)
-  profile.current_channel = c.index
-
-  --oldprint('toplevel is now', db.current_channel)
-  c:Save()
-
-
-  -- update dock buttons
-  local beacon = c.dockButton
-  beacon.selected = true
-  beacon.newMessage = nil
-  beacon:Update()
-
-  for _, part in pairs(c.border) do
-    part:SetColorTexture(unpack(db.frontborder))
-  end
-
-  for id, bc in pairs(D.console) do
-    if id ~= c.index then
-      bc:SetAlpha(db.backalpha)
-      bc.out.backdrop:SetColorTexture(unpack(db.backdrop))
-      bc.out.backdrop:SetGradientAlpha(unpack(db.backgrad))
-      bc.out.backdrop:SetBlendMode(db.backblend)
-      bc.dropmenu.icon:SetVertexColor(unpack(db.backheader))
-      bc.title:SetTextColor(unpack(db.backborder))
-
-      local beacon = D.dock.usedButtons[bc.index]
-      beacon.raised = nil
-      beacon.showName = nil
-      beacon.selected = nil
-      beacon:Update()
-
-      for _, part in pairs(bc.border) do
-        part:SetColorTexture(unpack(db.backborder))
-      end
-    end
-  end
-end
-
-local function Console_MouseDown(self, button, up)
-  if button == 'LeftButton' then
-    if up then
-      self:StopMovingOrSizing()
-    elseif self.out.grip:IsMouseOver() then
-      self:StartSizing()
-    else
-      self:StartMoving()
-    end
-  else
-    if up then
-      self:MinMax()
-    end
-  end
-end
-local function Console_MouseUp(self, button)
-  return Console_MouseDown(self, button, true)
-end
-
-
---- Constructs the frame object for a console channel
--- Initializes the console channel at a specified index.
--- Configuration data can be overridden by passing a desired settings table.
--- @param i Numeric index of the channel as it manifests in db.channels
--- @param vars Optional settings table to be used.
-local function CreateConsole(i, vars)
-  local db = D.db
-  if tonumber(i) == nil or math.floor(i) ~= i then
-    error('Non-integer index value.')
-  end
-  if not vars then
-    vars = D.channels[i] and D.channels[i] or D.channels[db.primary_channel]
-  end
-  local f
-  f = CreateFrame('Frame', 'DevianChannelFrame' .. i, UIParent, DEVIAN_FRAME)
-
-  --@debug@
-  --print(f:GetName())
-
-  --print('create(2)')
-  for k,v in pairs(vars) do
-    f[k] = v
-    --@debug@
-    --print(' f['..type(k)..' '..tostring(k)..'] = '..type(v)..' '..tostring(v))
-  end
-
-  f:SetPoint('TOPLEFT', UIParent, 'TOPLEFT', vars.x, vars.y)
-  f:SetSize(vars.width, vars.height)
-  f:Lower()
-  f.out:SetFont(db.font, db.fontsize, db.fontoutline)
-  if (db.current_channel == i) then
-    f.out.backdrop:SetColorTexture(unpack(db.frontdrop))
-    f.dropmenu.icon:SetVertexColor(unpack(db.headerfontcolor))
-    f.title:SetTextColor(unpack(db.headerfontcolor))
-    f.header:SetAlpha(db.headeralpha)
-  else
-    f.out.backdrop:SetColorTexture(unpack(db.backdrop))
-  end
-
-  f.Save = Console_Save
-  f.Minimize = Console_Minimize
-  f.Maximize = Console_Maximize
-  f.MinMax = Console_MinMax
-  f.ToFront = Console_ToFront
-  f.Toggle = D.Console_Toggle
-  f:SetScript('OnMouseDown', Console_MouseDown)
-  f:SetScript('OnMouseUp', Console_MouseUp)
-  f.profileID = db.current_profile
-
-
-  UIDropDownMenu_Initialize(f.menuFrame, function()
-    local info = { {
-        text= "Close",
-        value = "OptClose",
-        func = function ()
-          f.enabled = nil
-          f:Hide()
-          f:Save()
-        end },--[[
-      {
-        text = "Dock",
-        value = "OptDock",
-        func = function() print('Dvn', 'docking shenanary') end }]]
-    }
-    for _, v in ipairs(info) do
-      UIDropDownMenu_AddButton(v)
-    end
-  end, 'MENU')
-
-  if db.movement_fade then
-    f:RegisterEvent('PLAYER_STARTED_MOVING')
-    f:RegisterEvent('PLAYER_STOPPED_MOVING')
-    f:SetScript('OnEvent', Console_OnMovementChanged)
-  end
-
-  f.dockButton = D:GetDockButton(f)
-
-
-  if vars.minimized then
-    f:Minimize()
-  else
-    f:Maximize()
-  end
-  if db.enabled then
-    f.enabled = true
-    f:Show()
-  end
-
-  return f
-end
-
-
---- Updates console information and returns the handle of the channel object that was worked on.
--- When key is nil or not a valid handle, a new channel is created using whatever signature can be found in cinfo.
--- The signature can be passed as a string, or as a table entry under the key 'signature'
--- If the signature of a new channel is also a tag, the channel will be added to that tag
--- @param cinfo string signature of a new channel, or a table of config variables to be imposed on the channel
--- @param key string signature or index number of channel to operate on
--- @usage channel = D:SetChannel('new', nil) -- creates a new channel
--- @usage channel = D:SetChannel({x = 200, y = 100}, 4) -- updates channel #4
-function D:SetChannel(cinfo, key)
-  local profile = D.currentProfile
-  local t_info = {}
-  local channel, isNew, id, sig, t_id
-  --@debug@
-  --print('setchan(0) cinfo, key', cinfo, key)--@end-debug@
-  -- obtain source data
-  if tonumber(key) ~= nil and  D.channels[key] then
-    id = tonumber(key)
-  elseif D.sigID[tostring(key)] then
-    id = D.sigID[tostring(key)]
-  else
-    id = profile.default_channel
-    isNew = true
-  end
-  local dbvars = D.channels[id]
-  t_id = id           -- overridden later if new
-  t_info.index = t_id --
-  --@debug@
-  --print('setchan(1) cinfo, key, id=', cinfo, key, id)--@end-debug@
-
-
-  -- obtain config info
-  if type(cinfo) == 'string' then
-    sig = cinfo
-    cinfo = {signature = sig}
-  elseif type(cinfo) ~= 'table' then -- stop here if a table wans't passed
-    error('Expecting table of string as arg1')
-  elseif cinfo.signature then -- new sig
-    sig = cinfo.signature
-  elseif isNew then -- new channel sig
-    sig = 'Ch'
-  else -- old sig
-    sig = D.channels[id].signature
-  end
-  t_info.signature = sig
-  --@debug@
-  --print('setchan(2) sig,id,isNew=', sig, id, isNew)--@end-debug@
-
-  for k,v in pairs(cinfo) do -- allow all cinfo to pass
-  t_info[k] = v
-  end
-
-  local blocked = {          -- ignore these vars:
-    ['docked'] = true,       -- table
-    ['dockedTo'] = true,     -- table-related
-    ['signature'] = true}    -- already determined
-  for k,v in pairs(dbvars) do
-    if not t_info[k] and not blocked[k] then -- already set or blocked?
-    --print('assign', k, '=', v)
-    t_info[k] = v
-    end
-  end
-  -- new channel overrides
-  if isNew then
-    if D.sigID[sig] then -- find a non-clashing signature
-    local result, i = sig, 1
-    while D.sigID[result] do
-      result = sig .. i
-      i = i + 1
-    end
-    t_info.signature = result
-    end
-    t_id = self.max_channel + 1
-    t_info.index = t_id
-
-    self.max_channel = t_id
-    --@debug@
-    --print('setchan(3a) t_id, isNew, sig, t_info.signature=', t_id, isNew, sig, t_info.signature)--@end-debug@
-  else
-    --@debug@
-    --print('setchan(3b) t_id, isNew, sig, t_info.signature=', t_id, isNew, sig, t_info.signature)--@end-debug@
-  end
-
-  local channel
-  if not self.console[t_id] then -- create a frame
-    if isNew then -- position the channel frame
-      profile.max_channel = t_id
-      t_info.x = t_info.x + 20
-      t_info.y = t_info.y - 20
-      profile.channels[t_id] = t_info
-      --@debug@
-      --print('setchan(4a)', 't_id, x, y=', t_id, t_info.x, t_info.y)--@end-debug@
-    end
-    channel = CreateConsole(t_id, t_info)
-    self.console[t_id] = channel
-    self.sig[t_info.signature] = channel
-    self.sigID[t_info.signature] = t_id
-    self.IDsig[t_id] = t_info.signature
-  end
-  channel = self.console[t_id]
-  if channel.minimized then
-    channel:Minimize()
-  else
-    channel:Maximize()
-  end
-
-  if channel.enabled then -- hide or show last since Min/Max mess with visibility
-    --print('setchan(5a) enable')
-    channel:Show()
-    --channel:ToFront()
-  else
-    --print('setchan(5a) disable')
-    channel:Hide()
-  end
-  --@debug@
-  --print('setchan(end); c:IsVisible(), c.enabled, db.enabled=', channel:IsVisible(), channel.enabled, profile.enabled)--@end-debug@
-  return channel
-end
-
---- Console frame toggler
--- @paramsig [...]
--- @param ... one or more space-seperated channel keys
-function D:Console_Toggle(cmd, force)
-
-
-  local search = {}
-  if cmd then
-    cmd = tostring(cmd)
-    local i, j = 0, 0
-    repeat
-      i, j = cmd:find("%S+", j+1)
-      if i and j then
-        local key = cmd:sub(i, j)
-        if self.sig[key] then
-          --print(key, self.sigID[key])
-          insert(search, self.sigID[key])
-        elseif self.console[tonumber(key)] then
-          --print(key, tonumber(key))
-          insert(search, tonumber(key))
-        end
-      end
-    until not(i or j)
-  end
-
-  local profile = D.currentProfile
-  local setAll
-
-  -- if nothing was found, then do an all toggle
-  if #search < 1 then
-    search = self.sigID -- using this since it lists frame ID's
-    setAll = true
-  end
-
-  -- sort global enable state
-  if setAll then
-    --oldprint('setall', setAll)
-    profile.enabled = (not profile.enabled) and true or nil
-    if force == 0 then
-      profile.enabled = nil
-    end
-  end
-
-  -- sort local enable states: if global enable, apply that change to channel
-  -- else, channel enable takes precedence
-  for i, id in pairs(search) do
-    local c = self.console[id]
-    if setAll then
-      c.enabled = profile.enabled
-    else
-      c.enabled = (not c.enabled) and true or nil
-      if force == 0 then
-        c.enabled = nil
-      end
-    end
-
-    if c.enabled then
-      profile.enabled = true
-      c:Show()
-    else
-      c:Hide()
-    end
-    c:Save()
-  end
-
-  if setAll then
-    if profile.enabled then
-      self:Print('toggled all consoles ON')
-      if D.console[profile.current_channel] then
-        D.console[profile.current_channel]:ToFront()
-      end
-    else
-      self:Print('toggled all consoles OFF')
-    end
-  else
-    local result = {}
-    for i, id in pairs(search) do
-      result[i] = tostring(id) .. ' = ' .. (self.console[id].enabled and 'ON' or 'OFF')
-    end
-    self:Print('toggled: '..concat(result, ', '))
-  end
-end