Files
Mush-Soundpack/cosmic rage/lua/InfoBox.lua
2025-07-01 23:28:00 +03:00

1679 lines
74 KiB
Lua

--[=[--
InfoBox.lua version 1.2
Encapsulates and enhances Infobar functionality into miniwindows; gauges and status text.
14-DEc-08
by: WillFa (Spellbound on 3k.org:3000)
portions borrowed or adapted from samples by Nick Gammon.
License: Free for public use in your MushClient plugins and scripts as long as credit for this module is
attributed to WillFa, and Nick Gammon.
Assumes that the Sylfaen font is installed on the system. Change line 67 if it is not.
Requires: MushClient v. 4.37+ for complete functionality. (CloseWindow() uses DeleteWindow MC function.)
MushClient v. 4.35+ for basic functionality. (WindowGradient calls)
Usage:
require "InfoBox"
MW = InfoBox:New()
MW:AddBar("text", percent, "green", "red")
MW:Update()
Tutorial Demo can be found in the included InfoBox_Demo.xml plugin.
Reference help for functions and values you will actually use can be obtained from the Doc() function.
i.e.:
InfoBox:Doc()
InfoBox:Doc("AddBar")
General Usage is more easily deduced by tprint(InfoBox), tprint(MW), tprint(MW.Bar) rather than reading this source.
The metatables are setup in such a way that tprint will not recurse up the inheritance chain and spam you.
--]=]--
local world, colour_names = world, colour_names, _M
local os = {time = os.time}
local math, string, table, bit = math, string, table, bit
local tonumber, tostring, rawget, rawset, type, print = tonumber, tostring, rawget, rawset, type, print
local unpack, assert, require, getmetatable, setmetatable = unpack, assert, require, getmetatable, setmetatable
local pairs, ipairs, check = pairs, ipairs, check
local Global = _G
local capitalize, CalcShades, SplitRGB, idx, newidx
local CalcWindowHeight, strip_colours, sidewindows, Draw, DoFade, PrintText, ResizeOutput
local fontProps = {"fontID", "fontName", "fontSize", "fontBold", "fontItalic", "fontUnderline", "fontStrikeout", "fontCharset", "fontPitchAndFamily"}
local commas, CheckStyle, DoFade, PrintText
local matteHilight, matteShadow, Bar, ansiCodes, ansiColors, _Doc
module(...)
setmetatable ( _M, {__index = world, class = "module"}) -- find all the MushClient Functions
windowWidth = 240
windowHeight = 7 -- this will change as more bars are added. It gives 5 pixels padding top and bottom.
columns = 0
rows = 0
axis = "rows"
backgroundColour = 0x808080
matteHilight = 0xb2b2b2
matteShadow = 0x4e4e4e
windowPosition = 7
windowFlags = 0
fontID = "fn"
fontName = "Sylfaen"
fontSize = 10
fontPadding = 2
fontBold = true
fontItalic = false
fontUnderline = false
fontStrikeout = false
fontCharset = 1
fontPitchAndFamily = 0
displaceOutput = false
ansiCodes = {"k", "r", "g", "y", "b", "m", "c", "w"}
ansiColors = {} -- Gets populated after capitalize() is defined.
customColourCodes = {}
-- Enumeration tables
textStyles = { plain = 0,
matte = 1,
raised = 2,
sunken = 4
}
barStyles = { textOnly = 0, -- Status line
sunken = 1, -- Frames
raised = 2,
raisedCap = 2^2, -- Caption is framed too.
flat = 2^3,
glass = 2^4,
solid = 2^5, -- Fills
gradientScale = 2^6, -- gradient midpoint is value/2. Fill shrinks
gradientFixed = 2^7, -- gradient midpoint is always threshold.
gradientShift = 2^8, -- Thermometer style; Fill remains 100%, gradient position shifts wth value
}
captionPlacements = { left = 0,
innerLeft = 1,
innerRight = 2,
right = 3,
center = 4,
centerCell = 5,
}
windowPositions = { NW = 4,
N = 5,
NE = 6,
E = 7,
SE = 8,
S = 9,
SW = 10,
W = 11,
}
-- End Enumerations
-- Bar Functions/Methods
Bar = { --Properties
textStyle = 0,
threshold = 30,
textColour = 0x222222 ,
"Caption", -- 1 = "Caption" see InsertBars() for the tricky code that this uses.
caption = "",
"Value", -- 2 = "Value" see InsertBars()
value = 0,
"GoodColour", -- 3 = ...
goodColour = 0x00DD00,
"BadColour", -- 4 = ...
badColour = 0x0000DD,
"AnchorRight", -- 5 = ...
anchorRight = false,
"BarStyle", -- 6 = ...
barStyle = 33,
gaugeLeft = 5 ,
captionPlacement = 1,
padding = 3,
cellPadding = 4,
--End Properties
}
setmetatable (Bar, {__index = _M, class = "bar"})
--[=[ Internal Function that writes captions ]=]--
function PrintText (self, vOffset, hOffset)
--TraceOut (string.format("%s: Window %s Bar %i", "PrintText", self.windowName, self.id))
local MatteText = strip_colours(self.caption)
local width = WindowTextWidth (self.parent.windowName, self.fontID, MatteText)
local side = 0
if self.matteHilight == nil then
self.matteHilight, self.matteShadow = self.backgroundColour,self.backgroundColour
for i = 1,10 do
self.matteHilight = AdjustColour(self.matteHilight,2)
self.matteShadow = AdjustColour(self.matteShadow,3)
end end
vOffset = vOffset + self.fontPadding
if self.captionPlacement == 0 then
side = hOffset + self.gaugeLeft - WindowTextWidth ( self.parent.windowName, self.fontID, MatteText) -2
elseif self.captionPlacement == 1 then
side = hOffset + self.gaugeLeft + 5
elseif self.captionPlacement == 2 then
side = hOffset + self.gaugeLeft + self.gaugeWidth - width - 5
elseif self.captionPlacement == 3 then
side = hOffset + self.gaugeLeft + self.gaugeWidth + 2
elseif self.captionPlacement == 4 then
side = hOffset + self.gaugeLeft + ( (self.gaugeWidth-WindowTextWidth ( self.parent.windowName, self.fontID, MatteText) )/2)
elseif self.captionPlacement == 5 then
side = math.floor(hOffset + (self.cellWidth-WindowTextWidth ( self.parent.windowName, self.fontID, MatteText) ) / 2)
end
if self.barStyle == 0 then
if self.captionPlacement > 3 then
side = hOffset + math.floor( (self.cellWidth-WindowTextWidth ( self.parent.windowName, self.fontID, MatteText) ) / 2)
elseif self.captionPlacement < 2 then
side = hOffset + 5
elseif self.captionPlacement > 1 then
side = hOffset + self.parent.cellWidth - width - 5
end end
if self.textStyle > 0 then
if (self.textStyle % 2) == 1 then -- Matted
WindowText (self.parent.windowName, self.fontID, MatteText, side +1 , vOffset -1, 0, 0, self.backgroundColour)
-- Upper Right Matte
WindowText (self.parent.windowName, self.fontID, MatteText, side -1 , vOffset +1, 0, 0, self.backgroundColour)
-- Lower Left Matte
if self.textStyle == self.textStyles.matte then -- Only Matted
WindowText (self.parent.windowName, self.fontID, MatteText, side -1 , vOffset -1, 0, 0, self.backgroundColour)
-- Top Left Matte
WindowText (self.parent.windowName, self.fontID, MatteText, side +1 , vOffset +1, 0, 0, self.backgroundColour)
-- Bottom Right Matte
end
end
if bit.band(self.textStyle, self.parent.textStyles.raised) == self.textStyles.raised then -- Raised
WindowText (self.parent.windowName, self.fontID, MatteText, side +1 , vOffset +1, 0, 0, self.matteShadow)
-- Bottom Left Shadow
WindowText (self.parent.windowName, self.fontID, MatteText, side -1 , vOffset -1, 0, 0, self.matteHilight )
-- Top Left hilight
end
if bit.band(self.textStyle, self.textStyles.sunken) == self.textStyles.sunken then -- Sunken
WindowText (self.parent.windowName, self.fontID, MatteText, side -1 , vOffset -1, 0, 0, self.matteShadow)
-- Top Left Shadow
WindowText (self.parent.windowName, self.fontID, MatteText, side +1 , vOffset +1, 0, 0, self.matteHilight)
-- Bottom Left Hilight
end end
if self.caption == MatteText then
WindowText (self.parent.windowName, self.fontID, self.caption, side , vOffset, side + width, vOffset + self.fontHeight, self.textColour) -- Text
else
ansiColors["~"] = self.textColour
local customColours = self.customColourCodes or {}
local Text = string.gsub (self.caption or "", "@@", "\0") -- change @@ to 0x00
if Text:sub (1, 1) ~= "@" then
Text = "@~" .. Text
end
for colour, text in Text:gmatch ("@([%a~])([^@]+)") do
text = text:gsub ("%z", "@") -- put any @ characters back
if #text > 0 then
side = side + WindowText (self.windowName, self.fontID, text, side, vOffset, hOffset + self.cellWidth, vOffset + self.fontHeight, customColours[colour] or ansiColors[colour] or ansiColors["~"])
end end end
return vOffset - self.fontPadding
end
--[=[ Internal Function that does bitmask checking ]=]--
function CheckStyle(self, style)
--TraceOut (string.format("%s: Window %s Bar %i", "CheckStyle", self.windowName, self.id))
if bit.band(self.barStyle, style) > 0 then
return true
else
return false
end end
--[=[ Internal Function that draws the pretty boxes ]=]--
function Draw (self, vOffset, hOffset)
--TraceOut (string.format("%s: Window %s Bar %i", "Draw", self.windowName, self.id))
self.cellTop, self.cellLeft = vOffset , hOffset
WindowRectOp (self.windowName , 2, self.cellLeft, self.cellTop - 1 , self.cellLeft + self.cellWidth , self.cellTop + self.cellHeight + self.padding - 2 , self.backgroundColour, nil)
local Percent = self.value or 0
local colour, fcolour
if Percent < self.threshold then
colour = self.badColour
elseif Percent > 100 then
Percent = 100
for i = 1,15 do
colour = AdjustColour(self.goodColour,4) -- 20% more luminence
end
else
colour = self.goodColour
end
if self.fade == true then
colour = DoFade(self)
fcolour = colour
end
local pixels = self.gaugeWidth * Percent / 100
if self.barStyle > 0 then
local bezel = {[true] = 10, [false] = 5}
local frameLeft = self.cellLeft + self.gaugeLeft
local frameTop = self.cellTop + self.cellHeight - self.gaugeHeight
local frameRight = self.cellLeft + self.gaugeLeft + self.gaugeWidth
local frameBottom = self.cellTop + self.cellHeight
local topX, topY, bottomX, bottomY = frameLeft + 2, frameTop + 2 , self.cellLeft + self.gaugeLeft + pixels, frameBottom - 2
local colorL, colorR = self.badColour, (fcolour or self.goodColour)
local ftopX, ftopY, fbottomX, fbottomY = bottomX + 1, frameTop, frameRight, frameBottom --fill region for fixed gradients
if self.anchorRight == true then
topX , topY = frameRight - pixels , frameTop + 2
bottomX, bottomY = frameRight - 2, frameBottom - 2
ftopX , ftopY = frameLeft , frameTop
fbottomX, fbottomY = frameRight - pixels - 2, frameBottom
colorL, colorR = (fcolour or self.goodColour), self.badColour
end
if bit.band(barStyles.raisedCap, self.barStyle) == barStyles.raisedCap then
if self.captionPlacement == 0 then
frameLeft = self.cellLeft + 3
elseif self.captionPlacement == 3 then
frameRight = self.cellLeft + self.cellWidth -3
end end
if Percent >= 1 then
if CheckStyle(self, self.barStyles.solid) then
check (WindowRectOp (self.parent.windowName, 2, topX , topY , bottomX , bottomY , colour) )
elseif CheckStyle(self, self.barStyles.gradientScale) then -- scaled gradient fills
check (WindowGradient (self.parent.windowName, topX, topY, bottomX, bottomY, colorL, colorR, 1 ) )
elseif CheckStyle(self, self.barStyles.gradientFixed) then -- fixed gradients.
local padWidth, padFillColour = self.gaugeWidth * ( (math.abs(50-self.threshold) *2)/100), (fcolour or self.goodColour)
if self.threshold > 50 then
padFillColour = self.badColour
end
check (WindowRectOp (self.parent.windowName, 2, topX , topY , bottomX, bottomY, padFillColour) )
if ( (self.anchorRight == false) and (self.threshold <= 50) ) or
( (self.anchorRight == true) and (self.threshold > 50) ) then --need more colorR than colorL
check (WindowGradient (self.parent.windowName, self.cellLeft + self.gaugeLeft, frameTop, frameRight - padWidth, bottomY, colorL, colorR, 1 ) )
elseif ( (self.anchorRight == true) and (self.threshold <= 50) ) or
( (self.anchorRight == false) and (self.threshold > 50) ) then --need more colorL than colorR
check (WindowGradient (self.parent.windowName, self.cellLeft + self.gaugeLeft + padWidth, topY, frameRight -2, bottomY, colorL, colorR, 1 ) )
end
check (WindowRectOp (self.parent.windowName, 2, ftopX , ftopY , fbottomX , fbottomY -1 , self.backgroundColour) )
elseif CheckStyle(self, self.barStyles.gradientShift) then -- Thermometer gradients.
local midpointX = bottomX
local pixel = self.gaugeWidth / 100
local gradientWidth = math.min(30, (100 - self.value) * pixel, self.value * pixel)
if self.anchorRight == true then
colorL, colorR = (fcolour or self.goodColour), self.badColour
midpointX = topX
end
check (WindowRectOp (self.parent.windowName, 2, topX , topY , bottomX , bottomY, (fcolour or self.goodColour) ) )
check (WindowRectOp (self.parent.windowName, 2, ftopX, ftopY , fbottomX, bottomY, self.badColour) )
check (WindowGradient (self.parent.windowName, midpointX - gradientWidth, topY, midpointX + gradientWidth, bottomY, colorR, colorL, 1 ) )
check (WindowRectOp (self.parent.windowName, 2, self.cellLeft + 1, self.cellTop, self.cellLeft + self.gaugeLeft, self.cellTop + self.cellHeight , self.backgroundColour) )
check (WindowRectOp (self.parent.windowName, 2, frameRight, frameTop, -2, frameBottom, self.backgroundColour) )
end
if CheckStyle(self, self.barStyles.glass) then -- glass frame
local glassWidth, dtX,dtY, dbX, dbY = pixels, topX, topY, bottomX, bottomY
if CheckStyle(self, self.barStyles.gradientShift) then
glassWidth = self.gaugeWidth
dtX, dtY, dbX, dbY = frameLeft + 2, frameTop + 1, frameRight - 2, frameBottom -1
end
check (WindowCreate ( self.parent.windowName .. "glass", 0,0, glassWidth, self.cellHeight - 2, 7, 0, 0x171717) )
check (WindowGradient(self.parent.windowName .. "glass", 0,0, 0, self.cellHeight * .50, 0x606060, 0xaaaaaa, 2) )
check (WindowGradient(self.parent.windowName .. "glass", 0,self.cellHeight * .51, 0, self.cellHeight -2, 0x7c7c7c, 0x393939, 2) )
check (WindowImageFromWindow ( self.parent.windowName, self.parent.windowName .. "glass", self.parent.windowName .. "glass") )
check (WindowBlendImage ( self.parent.windowName, self.parent.windowName .. "glass", dtX, dtY, dbX, dbY, 21, .45) )
elseif CheckStyle(self, barStyles.raised + barStyles.raisedCap + barStyles.sunken) then -- 3D frame rectangle
check (WindowRectOp (self.parent.windowName, 5, topX , topY , bottomX, bottomY, bezel[not CheckStyle(self, barStyles.sunken + barStyles.glass)] ,0x100F) )
end end
if bit.band( self.barStyle , 31 ) ~= 0 then -- frame rectangle on border
local pen = {[true]=1,[false]=5}
check (WindowRectOp (self.parent.windowName, pen[CheckStyle(self, self.barStyles.flat)], frameLeft, frameTop, frameRight, frameBottom, bezel[not CheckStyle(self, barStyles.raised + barStyles.raisedCap)], 15+0x1000) )
end end
PrintText (self, self.cellTop, self.cellLeft)
if type(self.button) == 'table' then
WindowAddHotspot(self.windowName, self.id, self.cellLeft, self.cellTop, self.cellLeft + self.cellWidth, self.cellTop + self.cellHeight,
self.button.mouseOver or "", self.button.cancelMouseOver or "", self.button.mouseDown or "", self.button.cancelMouseDown or "",
self.button.mouseUp or "", self.button.tooltipText or "", self.button.cursor or 0, 0 )
end
return self.cellTop + self.cellHeight + self.padding
end
--[=[ Internal Function that calculates different shades of colors ]=]--
function DoFade(self)
--TraceOut (string.format("%s: Window %s Bar %i", "DoFade", self.windowName, self.id))
local color
if self.shades == nil or (self.Bar.shades and self.goodColour ~= self.Bar.goodColour and self.badColour ~= self.Bar.badColour) then
self.shades = CalcShades(self, math.ceil(100 - self.threshold)/5)
end
if self.value >= self.threshold then
color = self.shades[math.ceil( (101-self.value)/5 )] or self.shades[#self.shades]
else
color = self.badColour
end
return color
end
--[=[ See :Doc( "Fade" ) --]=]--
function Bar:Fade (bool)
assert(bool and type(bool)=="boolean", "Boolean expected. Got " .. tostring( bool ) .. "(" .. type(bool) .. ")")
self.fade = bool
self.shades = nil
if bool then
DoFade(self)
end
end
--[=[ See :Doc( "TextColour" ) --]=]--
function Bar:TextColour (x)
--TraceOut (string.format("%s: Window %s Bar %i", "TextColour", self.windowName, self.id))
local n = tonumber(x) or tonumber(x,16) or ColourNameToRGB(x)
assert(n and n~= -1,'Bad value passed. Acceptable examples: "red", 255, 0x0000ff, "#FF0000" - Got '.. tostring(x) .. '('.. type(x) ..')')
self.textColour = n
end
--[=[ See :Doc( "ColourText" ) --]=]--
function Bar:ColourText(val, thresh, good, bad, neut)
--TraceOut (string.format("%s: Window %s Bar %i", "ColourText", self.windowName, self.id))
local n = tonumber(thresh)
if n then
if val == thresh then
self:TextColour(neut or good)
elseif val < thresh then
self:TextColour(bad)
else
self:TextColour(good)
end end end
--[=[ See :Doc( "GoodColour" ) --]=]--
function Bar:GoodColour (x)
--TraceOut (string.format("%s: Window %s Bar %i", "GoodColour", self.windowName, self.id))
local n = tonumber(x) or ColourNameToRGB(x)
assert(n and n~= -1,'Bad value passed. Acceptable examples: "red", 255, 0x0000ff, "#FF0000" - Got '.. tostring(x) .. '('.. type(x) ..')')
self.goodColour = n
self.shades = nil
end
--[=[ See :Doc( "BadColour" ) --]=]--
function Bar:BadColour (x)
--TraceOut (string.format("%s: Window %s Bar %i", "BadColour", self.windowName, self.id))
local n = tonumber(x) or ColourNameToRGB(x)
assert(n and n~= -1,'Bad value passed. Acceptable examples: "red", 255, 0x0000ff, "#FF0000" - Got '.. tostring(x) .. '('.. type(x) ..')')
self.badColour = n
self.shades = nil
end
--[=[ See :Doc( "AnchorRight" ) --]=]--
function Bar:AnchorRight (bool)
--TraceOut (string.format("%s: Window %s Bar %i", "AnchorRight", self.windowName, self.id))
if type(bool) == "boolean" then
bool = bool
else
bool = false
end
self.anchorRight = bool
if bool == false then
self.captionPlacement = 1
else
self.captionPlacement = 2
end end
--[=[ See :Doc( "Value" ) --]=]--
function Bar:Value (val)
--TraceOut (string.format("%s: Window %s Bar %i", "Value", self.windowName, self.id))
self.value = tonumber(val) or self.value
self.watchValue = nil
end
--[=[ See :Doc( "Caption" ) --]=]--
function Bar:Caption (txt)
--TraceOut (string.format("%s: Window %s Bar %i", "Caption", self.windowName, self.id))
self.watchCaption = nil
if tonumber(txt) then
self.caption = commas(txt)
elseif txt == nil then
self.caption = ""
else
self.caption = txt
end
end
--[=[ See :Doc( "TextStyle" ) --]=]--
function Bar:TextStyle (x)
--TraceOut (string.format("%s: Window %s Bar %i", "TextStyle", self.windowName, self.id))
self.textStyle = tonumber(x) or 1
end
--[=[ See :Doc( "Threshold" ) --]=]--
function Bar:Threshold (x)
--TraceOut (string.format("%s: Window %s Bar %i", "Threshold", self.windowName, self.id))
self.threshold = tonumber(x) or 30
self.shades = nil
end
--[=[ See :Doc( "BarStyle" ) --]=]--
function Bar:BarStyle (x)
--TraceOut (string.format("%s: Window %s Bar %i", "BarStyle", self.windowName, self.id))
assert ( tonumber(x) and tonumber(x) >= 0, "Invalid barstyle.")
self.barStyle = tonumber(x)
end
--[=[ See :Doc( "Padding" ) --]=]--
function Bar:Padding (x)
--TraceOut (string.format("%s: Window %s Bar %i", "Padding", self.windowName, self.id))
assert( tonumber(x), "Number of pixels needed. Got " .. type(x) )
local MiniWindow = self.parent or self
self.padding = tonumber(x)
MiniWindow:Resize()
end
--[=[ See :Doc( "WatchValue" ) --]=]--
function Bar:WatchValue (varName)
--TraceOut (string.format("%s: Window %s Bar %i", "WatchValue", self.windowName, self.id))
self.value = nil
self.watchValue = varName
end
--[=[ See :Doc( "WatchCaption" ) --]=]--
function Bar:WatchCaption (varName)
--TraceOut (string.format("%s: Window %s Bar %i", "WatchCaption", self.windowName, self.id))
self.caption = nil
self.watchCaption = varName
end
--[=[ Internal Function that hides in metatables ]=]--
local function idx (self, k)
local varName, val = rawget (self, "watch" .. capitalize(k) ) or ""
if (k == "value" or k == "caption" ) then
for z in varName:gmatch("([^%.%[%]]+)") do
local parent = val or Global
z = tonumber(z) or z
val = parent[z]
end
return val
else
local Parent = getmetatable(self)["parent"] --parent is hidden in a metatable so it doesn't tprint. Hooray for aesthetically pleasing wastes of processor cycles.
val = rawget (self, k)
val = val or Parent[k]
return val
end end
--[=[ Internal Function that hides in metatables ]=]--
local function newidx (self, k, v)
if (k == "value" and rawget (self,watchValue) ) or (k == "caption" and rawget (self, watchCaption) ) then
self["watch" .. capitalize(k)] = nil
end
rawset(self, k, v)
end
-- End Bar Functions
-- Global Functions
--[=[ See :Doc( "New" ) --]=]--
function _M:New (winName)
--TraceOut (string.format("%s: Window %s", "New", winName))
local mt = {
__index =
function (self, key)
if key == "cellWidth" then
return self.windowWidth / self.columns
elseif key == "gaugeWidth" then
return self.cellWidth - 10
else
return _M[key]
end end ,
__metatable = {class = "Box"}; }
local q = {}
setmetatable(q,mt) -- Window table inherits the module
q.Bars = {}
q.Bar = {}
for k,v in pairs(Bar) do q.Bar[k] = v end -- a separate instance of Bar so that...
setmetatable(q.Bar, {parent = q, class = "Bar", __index=
function (self, key)
local mt = getmetatable(self)
if key =="parent" then
return mt.parent
else
return mt.parent[key]
end end ,
}) -- Bar table instance inherits it's MiniWindow table instance
q.windowName = winName or CreateGUID() -- Will create a guid if none passed in. Fine for plugin use.
check (WindowCreate (q.windowName, 0, 0, 1, 1, q.windowPosition, q.windowFlags, q.backgroundColour) )
check (WindowFont (q.windowName, q.fontID, q.fontName, q.fontSize, q.fontBold, q.fontItalic, q.fontUnderline, q.fontStrikeout, q.fontCharset, q.fontPitchAndFamily) )
q.fontHeight = WindowFontInfo (q.windowName, q.fontID, 1) -- Default font height
q.Bar.cellHeight = q.fontHeight + q.Bar.cellPadding
q.Bar.gaugeHeight = q.Bar.cellHeight -- Default size of bars.
return q
end
--[=[ See :Doc( "BackgroundColour" ) --]=]--
function _M:BackgroundColour (col)
assert (col, "Nil passed. specify a colour.")
local n = tonumber(col) or ColourNameToRGB(col)
assert(n and n~= -1,'Bad value passed. Acceptable examples: "red", 255, 0x0000ff, "#FF0000" - Got '.. tostring(x) .. '('.. type(x) ..')')
self.backgroundColour = n
self.matteHilight, self.matteShadow = self.backgroundColour,self.backgroundColour
for i = 1,10 do
self.matteHilight = AdjustColour(self.matteHilight,2)
self.matteShadow = AdjustColour(self.matteShadow,3)
end
end
--[=[ See :Doc( "Rows" ) --]=]--
function _M:Rows(num)
assert(type(num)=="number" and num > 0, "How many rows? Need a positive number. Got " .. type(num) )
assert(self.parent == nil, "Pass a MiniWindow")
self.rows = num
self:Resize()
end
--[=[ See :Doc( "Columns" ) --]=]--
function _M:Columns(num)
assert(type(num)=="number" and num > 0, "How many columns? Need a positive number. Got " .. type(num) )
assert(self.parent == nil, "Pass a MiniWindow")
self.rows = math.ceil(#self.Bars/num)
self.columns = num
self:Resize()
end
--[=[ See :Doc( "AddBar" ) --]=]--
function _M:AddBar(...)
--TraceOut (string.format("%s: Window %s", "AddBar", self.windowName))
return self:InsertBar(#self.Bars + 1, ...)
end
--[=[ See :Doc( "InsertBar" ) --]=]--
function _M:InsertBar(index, ...)
--TraceOut (string.format("%s: Window %s Bar %i", "InsertBar", self.windowName, index))
assert(self ~= _M , "Pass a table created from ".. _NAME .. ":New(), not ".. _NAME .. " itself.")
assert(self.parent == nil, "Attempt to add a Bar to a Bar. Use table from ".. _NAME .. ":New() instead.")
assert(index, "Bars index must be passed.")
local MiniWindow = self.parent or self
--[[-- Expected possible values:
1= Caption,
2= Value,
3= GoodColor,
4= BadColor,
5= AnchoredRight, (whether the 0 value is on the left or right side of the gauge.)
6= BarStyle (enumerated in InfoBox.barStyles ) --]]--
local args={...}
table.insert (self.Bars, index, {}) -- Add a table for new bar.
setmetatable(self.Bars[index],{__metatable={parent = self.Bar}, __index = idx, __newindex= newidx})
for i = index, #self.Bars do
self.Bars[i].id = i -- a way to find our index in the future
end
for k,v in ipairs(args) do -- update with optional parameters passed in
MiniWindow.Bar[ MiniWindow.Bar[k] ] (MiniWindow.Bars[index], v) -- This line of code is why MW.Bar[1] = "Caption". Bar[1] thru Bar[5] define which function the optional parameters call. Neat, hunh?
end
self:Resize()
return self.Bars[index]
end
--[=[ See :Doc( "RemoveBar" ) --]=]--
function _M:RemoveBar(index)
--TraceOut (string.format("%s: Window %s", "RemoveBar", self.windowName))
assert(self ~= _M , "Call from a table created from ".. _NAME .. ":New(), not ".. _NAME .. " itself.")
assert(self.parent == nil, "Attempt to remove a Bar from a Bar. Use table from ".. _NAME .. ":New() instead.")
assert(index, "Bars index must be passed.")
table.remove (self.Bars, index)
local otheraxis = {columns = "rows", rows = "columns"} -- Shrink the grid if appropriate
if (self[self.axis] -1 ) * self[ otheraxis[self.axis] ] >= #self.Bars then
self[self.axis] = self[self.axis] -1
self:Resize()
end
for i = index, #self.Bars do
self.Bars[i].id = i
end end
--[=[ See :Doc( "Update" ) --]=]--
function _M:Update (force)
--TraceOut (string.format("%s: Window %s", "Update", self.windowName))
assert(self ~= _M , "Pass a table called from ".. _NAME .. ":New(), not ".. _NAME .. " itself.")
--Note: It is possible to pass any table instance into this. i.e. foo, foo.Bar, foo.Bars[i]
--It may be bad style, but it intentionally will work. foo.Bars[i] still updates the entire window, not just its region.
if os.time() > (self.lastUpdated or 0) or force then
local MiniWindow = self.parent or self
check (WindowCreate (self.windowName, 0, 0, self.windowWidth, self.windowHeight, self.windowPosition, self.windowFlags, GetInfo(278)) )
check (WindowRectOp (self.windowName, 2, 2, 2, -2, -2, self.backgroundColour) ) -- "erase" miniwindw
local vertical,horizontal, curRow, curCol, t = 6, 0, 1, 1
for _, curBar in ipairs(self.Bars) do
local finishY = vertical + curBar.cellHeight + curBar.padding
if curRow > self.rows or finishY > self.windowHeight then
vertical = 6
curCol = curCol + 1
curRow = 1
if curCol > self.columns then --Maybe Fonts are distorting the grid
self:Resize() -- Resize/CalcWindowHeight will fix it
self:Update() -- and we need to redraw.
return -- and stop execution of the outdated update.
end end
horizontal = (curCol -1) * self.cellWidth
vertical = Draw(curBar, vertical, horizontal)
curRow=curRow+1
end
check (WindowRectOp (self.windowName, 5, 0, 1, -1, -2, 10, 15) ) -- 3D border effect on canvas
WindowRectOp (self.windowName, 2, self.windowWidth - 2 , 0, 0, 0, GetInfo(278), nil)
WindowShow (self.windowName, true)
self.lastUpdated = os.time()
return true
else
return false
end end
--[=[ See :Doc( "WindowPosition" ) --]=]--
function _M:WindowPosition(pos)
assert(type(pos) == "number" and (pos >= 4 and pos <=11), "Invalid window position passed. Use " .. _NAME .. ".windowPositions[ <N,S,E,W,NE...> ] if unsure.")
assert(self ~= _M and self.parent == nil, "Pass a table created from ".. _NAME .. ":New(), not ".. _NAME .. " itself.")
world.WindowPosition(self.windowName, 0,0,pos, self.windowFlags)
if ( (self.windowPosition == 5 or self.windowPosition == 9) and (pos ~= 5 and pos ~=9) or
(self.windowPosition ~= 5 and self.windowPosition ~= 9) and (pos == 5 or pos ==9) ) then
self.displaceOutput = (pos == 5 or pos == 9)
self.columns, self.rows = self.rows, self.columns
self.axis = "columns"
end
self.windowPosition = pos
self:Resize()
end
--[=[ See :Doc( "ResizeOutput" ) --]=]--
function _M:ResizeOutput()
local maxWidth, maxHeight = 0,0
local left, top, width, height = GetInfo(272), GetInfo(273), GetInfo(274), GetInfo(275)
local bOff, bCol, bWid, oCol, oSty = GetInfo(276), GetInfo(282), GetInfo(277), GetInfo(278), GetInfo(279)
local t={}
for _, v in ipairs(WindowList() or {}) do
local tmpPos, shown = WindowInfo(v, 7), WindowInfo(v,5)
if shown then
t[tmpPos] = (t[tmpPos] or 0) + 1
end end
local function sum (...)
local args, res= {...}, 0
for _,n in ipairs(args) do
if type(t[n]) == "number" then res = (res + t[n]) end
end
return res
end
if self.displaceOutput then
if self.windowPosition == 9 or self.windowPosition == 10 then
if sum(9,10) == 1 then
height = -self.windowHeight - 8
else
height = -math.max(math.abs(height), self.windowHeight)
end
elseif self.windowPosition == 11 then
if t[11] == 1 then
left = self.windowWidth
else
left = math.max ( left, self.windowWidth)
end
elseif self.windowPosition == 4 or self.windowPosition == 5 then
if sum(4,5) == 1 then
top = self.windowHeight
else
top = math.max( top , self.windowHeight)
end
else
if sum(6,7,8) == 1 then
width = -1 * self.windowWidth
else
width = -1 * math.max( math.abs(width) , self.windowWidth )
end end end
if sum(11) == 0 then left = 4 end
if sum(9,10) == 0 then height = 0 end
if sum(5, 4) == 0 then top = 0 end
if sum(6, 7, 8) == 0 then width = 0 end
TextRectangle(left, top, width, height, bOff, bCol, bWid, oCol, oSty)
end
--[=[ See :Doc( "Resize" ) --]=]--
function _M:Resize()
assert (self ~= _M , "Resize a window, not the module.")
assert (not self.parent, "Resize() is for MiniWindows, not Bars.")
local sharedWindow, winwidths = {}, 0
local sidewindows = {}
CalcWindowHeight(self)
if self.windowPosition == 5 or self.windowPosition == 9 then
local padptrn = "padS[EW]$"
if self.windowPosition == 5 then padptrn = "padN[EW]$" end
for _,v in ipairs(WindowList() ) do
if v:find(padptrn) then
WindowDelete(v)
elseif WindowInfo(v,5) then
local Position = WindowInfo(v, 7)
sidewindows[Position] = sidewindows[Position] or {topmost = 100000, bottommost = 0, leftmost = 100000, rightmost = 0, topwidth = 0, leftheight = 0, rightheight = 0}
sidewindows[Position].topmost = math.min( sidewindows[Position].topmost, WindowInfo(v, 11) )
sidewindows[Position].bottommost = math.max( sidewindows[Position].bottommost, WindowInfo(v, 13) )
sidewindows[Position].leftmost = math.min(sidewindows[Position].leftmost, WindowInfo(v, 10) )
sidewindows[Position].rightmost = math.max(sidewindows[Position].rightmost, WindowInfo(v, 12 ) )
if WindowInfo(v,11) == sidewindows[Position].topmost then
sidewindows[Position].topwidth = WindowInfo(v,3)
end
if Position == self.windowPosition then
sharedWindow[#sharedWindow +1] = v
sidewindows[Position].leftmost = math.min(sidewindows[Position].leftmost, WindowInfo(v, 10) )
sidewindows[Position].rightmost = math.max(sidewindows[Position].rightmost, WindowInfo(v, 12) )
if sidewindows[Position].leftmost == WindowInfo(v, 10) then
sidewindows[Position].leftheight = WindowInfo ( v, 4 )
end
if sidewindows[Position].rightmost == WindowInfo(v, 12) then
sidewindows[Position].rightheight = WindowInfo ( v, 4 )
end end end end
--[[ Prevent overlapping Auto-centered windows.
Windows will be arranged:
LttttR
L R
L R
BBBBBB --]]
if self.windowPosition == 5 then
if sidewindows[11] and sidewindows[11].topmost < sidewindows[5].bottommost then
sidewindows[4] = sidewindows[4] or {topwidth = 0 }
sidewindows[4].topwidth = math.max(sidewindows[4].topwidth, sidewindows[11].topwidth )
WindowCreate(self.windowName .. "padNW", 0,0,sidewindows[11].topwidth,0, 4, 0, 0)
WindowShow(self.windowName .. "padNW")
end
if sidewindows[7] and sidewindows[7].topmost < sidewindows[5].bottommost then
sidewindows[6] = sidewindows[6] or {topwidth = 0 }
sidewindows[6].topwidth = math.max(sidewindows[6].topwidth, sidewindows[7].topwidth )
WindowCreate(self.windowName .. "padNE", 0,0,sidewindows[7].topwidth,0, 6, 0, 0)
WindowShow(self.windowName .. "padNE")
end
elseif self.windowPosition == 9 then
if sidewindows[11] and sidewindows[11].bottommost > GetInfo(280) - sidewindows[9].leftheight then
WindowCreate(self.windowName .. "padSW", 0,0,0,sidewindows[9].leftheight, 10, 0, 0)
WindowShow(self.windowName .. "padSW")
end
if sidewindows[7] and sidewindows[7].bottommost > GetInfo(280) - sidewindows[9].rightheight then
WindowCreate(self.windowName .. "padSE", 0,0,0, sidewindows[9].rightheight, 8, 0, 0)
WindowShow(self.windowName .. "padSE")
end end
sidewindows[self.windowPosition -1] = sidewindows[self.windowPosition -1] or {topwidth = 0}
sidewindows[self.windowPosition +1] = sidewindows[self.windowPosition +1] or {topwidth = 0}
self.windowWidth = math.floor( (GetInfo(281) - sidewindows[self.windowPosition -1].topwidth - sidewindows[self.windowPosition +1].topwidth ) / #sharedWindow)
if WindowInfo(self.windowName, 6) then -- we're hidden and a sharedwindow isn't an infobox that will resize itself.
for _,p in ipairs({4,6,8,10}) do
sidewindows[p] = sidewindows[p] or {topwidth = 0}
end
winwidths = (sidewindows[self.windowPosition - 1].topwidth) + (sidewindows[self.windowPosition + 1].topwidth)
for _,v in ipairs(sharedWindow) do
if v ~= self.windowName then
winwidths = winwidths + WindowInfo(v,3)
end end
self.windowWidth = GetInfo(281) - winwidths
end end
self:ResizeOutput()
end
--[=[ See :Doc( "CloseWindow" ) --]=]--
function _M:CloseWindow ()
local wins = {"", "glass", "NE", "NW", "SE", "SW"}
for i = 1,5 do
WindowDelete(self.windowName .. wins[i])
self:ResizeOutput()
end end
--[=[ See :Doc( "Font" ) --]=]--
function _M:Font(...)
--[=[ Optional parameters:
-- Name, string
-- Size, number
-- Bold, boolean
-- Italic, boolean
-- Underline, boolean
-- Strikeout, boolean
-- Charset, number
-- PitchAndFamily, number
--]=]
assert(self ~= _M , "Pass a table created from ".. _NAME .. ":New(), not ".. _NAME .. " itself.")
local fontParams = {}
local gaugeRatio = (self.gaugeHeight or self.Bar.gaugeHeight) / (self.cellHeight or self.Bar.cellHeight)
for k,v in ipairs(fontProps) do fontParams[k] = self[v] end
local MiniWindow = self.parent or self
local args = {...}
if args[1] == nil then
if self == MiniWindow then
local defaultFont = {}
for _,f in ipairs(fontProps) do self[f] = nil ; defaultFont[#defaultFont +1] = self[f] end
check( WindowFont (self.windowName, unpack(defaultFont) ) )
self.fontHeight = WindowFontInfo (self.windowName, self.fontID, 1)
self.Bar.cellHeight = self.fontHeight + self.Bar.cellPadding
self.Bar.gaugeHeight = self.Bar.cellHeight * gaugeRatio
else
for _,f in ipairs(fontProps) do self[f] = nil end
self.fontHeight = nil
self.cellHeight = nil
self.gaugeHeight = self.cellHeight * gaugeRatio
end
else
for k,v in ipairs(args) do
fontParams[k + 1] = v
self[fontProps[k + 1] ] = v
end
self.fontName, self.fontSize = name, fontParams[1]
if self == MiniWindow then
MiniWindow.windowHeight = _M.windowHeight
check(WindowFont(MiniWindow.windowName, unpack(fontParams) ) )
MiniWindow.Bar.cellHeight = WindowFontInfo (MiniWindow.windowName, MiniWindow.fontID, 1) + MiniWindow.Bar.cellPadding
else
fontParams[1]= "f" .. self.id
check(WindowFont(self.windowName, unpack(fontParams) ) )
for k,v in ipairs(fontProps) do if fontParams[k] ~= self.parent[v] then self[v] = fontParams[k] end end
self.fontHeight = WindowFontInfo (self.windowName, self.fontID, 1)
self.cellHeight = self.fontHeight + self.cellPadding
self.gaugeHeight = self.cellHeight * gaugeRatio
end end
MiniWindow:Resize()
end
--[=[ See :Doc( "ReopenWindow" ) --]=]--
function _M:ReopenWindow()
local MiniWindow = self.parent or self
check(WindowCreate(MiniWindow.windowName, 0,0,MiniWindow.windowWidth,MiniWindow.windowHeight, MiniWindow.windowPosition, MiniWindow.windowFlags, MiniWindow.backgroundColour) )
WindowShow(MiniWindow.windowName)
MiniWindow:Resize()
local font = {}
for k,v in ipairs (fontProps) do font[k] = MiniWindow[v] end
WindowFont (MiniWindow.windowName, unpack(font))
for _, Bar in ipairs(MiniWindow.Bars) do
if Bar.fontID ~= MiniWindow.fontID then
for k,v in ipairs (fontProps) do font[k] = Bar[v] end
WindowFont (Bar.windowName, unpack(font))
end end
MiniWindow:Update()
end
--[=[ See :Doc( "CaptionPlacement" ) --]=]--
function _M:CaptionPlacement (x)
assert((self.id and x>=0 and x<=5) or (not self.parent and x ==nil),"call MiniWindow:CaptionPlacement() or Bar:CaptionPlacement(x), x must be a value in the ".. _NAME .. ".captionPlacements table. Got ".. tostring(x) .. " (" .. type(x) ..")")
local m, MiniWindow = 5, self.parent or self
if self.parent then self.captionPlacement = x end
local startCol, endCol, barID
if self.id then -- Hit just the column
startCol = math.modf(self.id/self.rows)
endCol = startCol
else -- Hit em all
startCol = 0
endCol = MiniWindow.columns -1
end
for curCol = startCol, endCol do
local maxCaplength, capsPlaced = 5, {}
barID = (curCol * self.rows) + 1
while curCol == math.modf( ( barID -1)/self.rows ) and barID <= #MiniWindow.Bars do
local cBar = MiniWindow.Bars[ barID ]
if cBar.barStyle ~= 0 then
maxCaplength = math.max(maxCaplength,WindowTextWidth(MiniWindow.windowName, cBar.fontID, strip_colours(cBar.caption), false)+5 )
capsPlaced[cBar.captionPlacement] = true
end
barID = barID +1
end
barID = (curCol * self.rows) + 1
while curCol == math.modf( (barID - 1)/self.rows ) and barID <= #MiniWindow.Bars do
local cBar = MiniWindow.Bars[ barID ]
if capsPlaced[0] and not capsPlaced[3] then
cBar.gaugeLeft = maxCaplength
cBar.gaugeWidth = MiniWindow.cellWidth - cBar.gaugeLeft -8
elseif not capsPlaced[0] and capsPlaced[3] then
cBar.gaugeLeft = nil
cBar.gaugeWidth = MiniWindow.cellWidth - maxCaplength -8
elseif capsPlaced[0] and capsPlaced[3] then
cBar.gaugeLeft = maxCaplength
cBar.gaugeWidth = MiniWindow.cellWidth - (2 * maxCaplength) - 2
else
cBar.gaugeLeft = nil
cBar.gaugeWidth = nil
end
barID = barID + 1
end end end
--[[-- Friend Functions --]]--
--[=[ Internal Function ]=]--
function CalcWindowHeight (self)
local MiniWindow = self.parent or self
local tmpHeights = {(MiniWindow.Bar.cellHeight + MiniWindow.Bar.padding) * MiniWindow.rows, 0}
local modOffset = 0
local otheraxis = {columns = "rows", rows = "columns"}
if MiniWindow[ otheraxis[MiniWindow.axis] ] == 0 then
MiniWindow[ otheraxis[MiniWindow.axis] ] = 1
end
if #MiniWindow.Bars > (MiniWindow.rows * MiniWindow.columns) then -- Is the grid big enough?
repeat
MiniWindow[MiniWindow.axis] = MiniWindow[MiniWindow.axis] + 1
until #MiniWindow.Bars <= MiniWindow.columns * MiniWindow.rows
end
MiniWindow:CaptionPlacement()
for _,tmpBar in ipairs(MiniWindow.Bars or {}) do -- Calculate the single largest column size.
if ( (tmpBar.id + modOffset) -1 ) % MiniWindow.rows == 0 then
tmpHeights[ #tmpHeights + 1 ] = tmpBar.cellHeight + tmpBar.padding
else
tmpHeights[#tmpHeights] = tmpHeights[#tmpHeights] + tmpBar.cellHeight + tmpBar.padding
end
if (tmpBar.cellHeight + tmpBar.padding) > (MiniWindow.Bar.cellHeight + MiniWindow.Bar.padding) and
#MiniWindow.Bars < (MiniWindow.rows*MiniWindow.columns) then
modOffset = -(tmpBar.id % MiniWindow.rows)
end
end
MiniWindow.windowHeight = math.max(unpack(tmpHeights) ) + _M.windowHeight
check (WindowCreate (MiniWindow.windowName, 0, 0, MiniWindow.windowWidth, MiniWindow.windowHeight, MiniWindow.windowPosition, MiniWindow.windowFlags, MiniWindow.backgroundColour) )
WindowShow(MiniWindow.windowName)
end
--[=[ Internal Function used in DoFade ]=]--
function SplitRGB (Colour)
local _, r,g,b
if type(Colour) == "number" or colour_names[Colour] then
_,_, b,g,r = string.format("%06x", (colour_names[Colour] or Colour) ):find("(%x%x)(%x%x)(%x%x)")
elseif (type(Colour) == "string") then --HTML Formatted string "#RRGGBB" or something similar
_,_, r,g,b = Colour:find("(%x%x)(%x%x)(%x%x)")
end
r,g,b = tonumber(r, 16), tonumber(g,16), tonumber(b,16)
return r,g,b , string.format("#%02x%02x%02x", r,g,b):upper()
end
--[=[ Internal Function used in DoFade ]=]--
function CalcShades(self, shades) -- the number of shades including start and end
local c, step, Cs = {}, {}, {}
local r1, g1, b1 = SplitRGB(self.goodColour)
local r2, g2, b2 = SplitRGB(self.badColour)
step.r = (r1-r2)/shades
step.g = (g1-g2)/shades
step.b = (b1-b2)/shades
for i = 0, shades do
Cs[i+1] = tonumber(string.format("%02x%02x%02x", math.floor(b1 - (step.b * i) ) % 256 , math.floor(g1 - (step.g * i) ) % 256 , math.floor(r1 - (step.r * i) ) % 256 ),16)
end
Cs[#Cs] = self.badColour
return Cs, step, step.r, step.g, step.b
end
--[=[ Internal Function used for color codes ]=]--
function capitalize (s)
return s:gsub("%a",string.upper, 1)
end -- capitalize
--[=[ Internal Function used in PrintText ]=]--
function strip_colours (s)
s=s or ""
s = s:gsub ("@@", "\0") -- change @@ to 0x00
s = s:gsub ("@.([^@]*)", "%1")
return (s:gsub ("%z", "@") ) -- put @ back
end
--[=[ Internal Function used in Caption, graciously borrowed from Nick Gammon ]=]--
function commas (num)
assert (type (num) == "number" or
type (num) == "string")
local result = ""
-- split number into 3 parts, eg. -1234.545e22
-- sign = + or -
-- before = 1234
-- after = .545e22
local sign, before, after =
string.match (tostring (num), "^([%+%-]?)(%d*)(%.?.*)$")
-- pull out batches of 3 digits from the end, put a comma before them
while string.len (before) > 3 do
result = "," .. string.sub (before, -3, -1) .. result
before = string.sub (before, 1, -4) -- remove last 3 digits
end -- while
-- we want the original sign, any left-over digits, the comma part,
-- and the stuff after the decimal point, if any
return sign .. before .. result .. after
end -- function commas
--[=[ Populates color codes tables ]=]--
for i,v in ipairs(ansiCodes) do
ansiColors[v] = GetNormalColour (i)
ansiColors[capitalize(v)] = GetBoldColour (i)
end
--[=[ Documentation - MW:Doc() will be easier to read then the source of the table ]=]--
_Doc = {
AddBar = [[
Applies To: MiniWindow table
Prototype: myBar = MW:AddBar(caption, value, goodColour, badColour, anchorRight, barStyle)
Adds a new bar to the end of the [MiniWindow].Bars table and returns a reference to this table.
caption = string; if a number is passed, commas are added to the ciphers
value = number; the percentage of the bar that is filled. Overmax percentages are accepted, and goodColour is drawn 15% brighter.
goodColour = string|number; the string is checked against names of colours or HTML formatting, the long number value of a color is also accepted.
badColour = string|number; same as goodColour
anchorRight = boolean; if true, 100% is on the left, 0% on the right. Automatically sets caption to be innerRight if true.
barStyle = number; a bitmask of barStyles values that specify a frame and fill type.]],
CaptionPlacement = [[
Applies To: Miniwindow table, Bar
Prototype: Bar:CaptionPlacement(n) or MW:CaptionPlacement()
Sets where text is drawn in the bar and calculates gauge sizes as appropriate. If calling against the MiniWindow itself, do not pass a parameter in the function to recalculate all gauge sizes and placements.
Setting a value to the MiniWindow.Bar.captionPlacement value will change the default label placement.
n = number; a value of 0-5 which are enumerated in the captionPlacements table.]],
CloseWindow = [[
Applies To: Miniwindow
Prototype: MW:CloseWindow()
Intended to be called from OnPluginDisable or OnPluginClose. Deletes the MiniWindow from MushClient's list of miniwindows.
Call MW:ReopenWindow() in the OnPluginEnable function.
]], --']]
Columns = [[
Applies To: Bar
Prototype: Bar:Columns(n)
Sets the number of columns into which the miniwindow is divided.
n = number; The number of columns.]],
Font = [[
Applies To: MiniWindow, Bar
Prototype: MW:Font([fontName, fontSize, fontBold, fontItalic, fontUnderline, fontStrikeout, fontPitchAndFamily])
If no parameters are passed, deletes font settings on the specific object so it will reinherit from a parent object.
Parameters omitted will retrieve settings from those named values. Specified values set named value on object as well.
See MushClient documentation for "WindowFont" for information on parameters.]],
InsertBar = [[
Applies To: MiniWindow
Prototype: myBar = MW:InsertBar(n, caption, value, goodColour, badColour, anchorRight, barStyle)
Inserts a new bar at position n and returns a reference to this new bar table.
n = number; The 1-based index of the new bar.
caption = string; if a number is passed, commas are added to the ciphers
value = number; the percentage of the bar that is filled. Overmax percentages are accepted, and goodColour is drawn 15% brighter.
goodColour = string|number; the string is checked against names of colours or HTML formatting, the long number value of a color is also accepted.
badColour = string|number; same as goodColour
anchorRight = boolean; if true, 100% is on the left, 0% on the right. Automatically sets caption to be innerRight if true.
barStyle = number; a bitmask of barStyles values that specify a frame and fill type.]],
New = [[
Applies To: Module
Prototype: MW = ]] .. _NAME .. [[:New([name])
Creates a new miniwindow table for you to populate with bars. The returned table has a metatable with its __index value referencing ]] .. _NAME .. [[.
name = string; If specified, sets the name of the window returned by MushClient's WindowList function. Omitting this value generates a GUID for MushClient to use for the window name. This name is stored in the MW.windowName value.]], --']]
ReopenWindow = [[
Applies To: MiniWindow
Protoype: MW:ReopenWindow()
Intended to be used in the OnPluginEnabled callback so that fonts are reloaded after the miniwindow is deleted with WindowDelete() (i.e. OnPluginDisabled called MW:CloseWindow() (but not MW = nil) which deletes the window from WindowList(). ]],
RemoveBar = [[
Applies To: MiniWindow
Prototype: MW:RemoveBar(n)
Deletes the specified bar from the Bars table.
n = number; The index of the bar to delete. A bar stores its index in the Bar.id value for your convenience.]],
Resize = [[
Applies To: MiniWindow
Prototype: MW:Resize()
This is called internally when necessary, the only time you need to be aware of it is for windows positioned at the top-center or bottom-center of the screen since they grow to the available width. It is recommended to place a call in the OnPluginWorldOutputResized callback in this scenario.]],
ResizeOutput = [[
Applies To: MiniWindow
Prototype: MW:ResizeOutput()
Called internally when positioning miniwindows at the left-center, lower-left, or bottom-center positions. Ff you wish another window to resize the Mud's text area, call it manually after setting that Miniwindow's .displaceOutput value to true.]], --']]
Rows = [[
Applies To: MiniWindow
Prototype: MW:Rows(n)
Sets the number of rows in a miniwindow to a specific value. If the MW.axis value is equal to "rows" it may still grow when new bars exceed the available space.
n = number; A positive number of rows]],
Update = [[
Applies To: MiniWindow
Prototype: updated = MW:Update()
Draws or redraws the Miniwindow. This function will only redraw once a second to prevent excessive CPU utilization during speedwalk or lagbursts.
updated = boolean; True if the window was redrawn this call, false if it had been drawn already in the past second and not updated.]],
WindowPosition = [[
Applies To: MiniWindow
Prototype: MW:WindowPosition(n)
Sets the location for the MiniWindow.
n = number; a value between 4 and 11. Values are enumerated in the .windowPositions table.]],
axis = [[
Applies To: MiniWindow
ProtoType: MW.axis = <"rows"|"columns">
Determines which way the grid scales as new bars demand additional space. MW.windowHeight grows as new rows are added. MW.windowWidth remains the same as new columns divide that space into smaller pieces.]],
backgroundColour = [[
Applies To: MiniWindow
ProtoType: MW.backgroundColour = n
The colour of the miniwindow.
n = number; The number value of the colour desired. i.e. colour_names.indigo, not "indigo"]],
barStyles = [[
Applies To: All
Prototype: N/A
A table enumerating the possible barStyle fills and frames.
none = 0; no frame
sunken = 1; a raised bar in a recessed frame
raised = 2; a recessed bar in a raised frame
raisedCap = 4; a recessed bar in a raised frame that fills the whole cell, regardless of captionPlacement
flat = 8; a single pixel frame, untouched gauge effect
glass = 16; recessed frame and a pretty visual effect on the bar
solid = 32; a plain color fill
gradientScale = 64; a gradient that goes from 0 to .value
gradientFixed = 128; gradient goes from 0 to 100, with backgroundColour covering .value +1 to 100
gradientShift = 256; The thermometer effect. Gradient that has its midpoint at .value.]],
captionPlacements = [[
Applies To: ALL
Prototype: N/A
A table enumerating values for the location of labels.
left = 0; Text is drawn to the left of the gauge.
innerLeft = 1; Text is drawn left justified close to the 0 value
innerRight = 2; Text is drawn right justified by the 100 value
right = 3; Text is drawn to the right of the gauge
center = 4; Text is drawn centered within the gauge.
centerCell = 5; Text is drawn centered within the entire bar.]],
columns = [[Applies To: MiniWindow
Prototype: MW.columns = n
The number of columns the MW is divided into. Setting this value directly bypasses validation in the Columns function.
n = number; Expected to be a positive, non-zero integer.]],
customColourCodes = [[
Applies to: MiniWindow, Bar
Prototype: Bar.customColourCodes = {a=val, [b=val2, ...]}
A table used for drawing different colours within the caption. See caption for more information.
a = char; a single character key identifying the attached value.
val = number; the number value of the desired colour. i.e. {i= colour_names.indigo}]],
displaceOutput = [[
Applies To: MiniWindow
Prototype: MW.displaceOutput = bool
Set to true and call MW:ResizeOutput() if you want to shrink the area that mud text arrives in so your window doesn't float above it. MiniWindows placed at the left-center, lower-left, or bottom-center (W,SW,S positions) automatically set this value to true.]], --']]
fontBold = [[
Applies To: MiniWindow, Bar
Prototype: Bar.fontBold = bool
This property can be used to specify the default value for the Font function, or be used to retrieve its last set value.]],
fontCharset = [[
Applies To: MiniWindow, Bar
Prototype: Bar.fontCharset = bool
This property can be used to specify the default value for the Font function, or be used to retrieve its last set value.
See MushClient's WindowFont documentation for more information]], --']]
fontID = [[
Applies To: MiniWindow, Bar
Prototype: foo = Bar.fontID
This property is used internally and really shouldn't be needed in your scripting.]], --']]
fontItalic = [[
Applies To: MiniWindow, Bar
Prototype: Bar.fontItalic = bool
This property can be used to specify the default value for the Font function, or be used to retrieve its last set value.
See MushClient's WindowFont documentation for more information]], --']]
fontName = [[
Applies To: Module and MiniWindow, Bar
ProtoType: ]].._NAME..[[.fontName = "font name" | foo = MW.fontName | foo = Bar.fontName
You can change the fontName property on the module which will affect MiniWindows subsequently returned by the New() function.
This property on MiniWindows and Bars shouldn't be changed by your scripting directly. Use the Font() function to change the loaded font.]], --']]
fontPadding = [[
Applies To: ALL
Prototype: MW.fontPadding = n
The number of pixels to offset the font placement from the top of the cell.]],
fontPitchAndFamily = [[
Applies To: MiniWindow, Bar
Prototype: Bar.fontPitchAndFamily = number
This property can be used to specify the default value for the Font function, or be used to retrieve its last set value.
See MushClient's WindowFont documentation for more information]], --']]
fontSize = [[
Applies To: MiniWindow, Bar
Prototype: Bar.fontSize = bool
This property can be used to specify the default value for the Font function, or be used to retrieve its last set value.
See MushClient's WindowFont documentation for more information]], --']]
fontStrikeout = [[
Applies To: MiniWindow, Bar
Prototype: Bar.fontStrikeout = bool
This property can be used to specify the default value for the Font function, or be used to retrieve its last set value.
See MushClient's WindowFont documentation for more information]], --']]
fontUnderline = [[
Applies To: MiniWindow, Bar
Prototype: Bar.fontUnderline = bool
This property can be used to specify the default value for the Font function, or be used to retrieve its last set value.
See MushClient's WindowFont documentation for more information]], --']]
rows = [[
Applies To: MiniWindow
Prototype: MW.rows = n
The number of rows. Setting this value directly bypasses validation in the Rows function.
n = number; Expected to be a positive, non-zero integer.]],
textStyle = [[
Applies To: Bar
Prototype: Bar.textStyle = n
The adornment on the caption text.
n = number; Expected values range from 0-5. See textStyles for more information]],
textStyles = [[
Applies To: All
Prototype: N/A
The table that enumerates textStyle values. Matting can be added to the other text styles for a little more clarity.
plain = 0;
matte = 1;
raised = 2;
sunken = 4;]],
windowFlags = [[
Applies To: Module, MiniWindow
Prototype: ]].._NAME..[[.windowFlags = n
Flags passed to the MushClient WindowCreate function. See MushClient documentation for more information.]],
windowHeight = [[
Applies To: Module, MiniWindow
Prototype: N/A
The MiniWindow.windowHeight is set dynamically to be .rows * .cellHeight + some padding. Setting this value manually is not recommended.]],
windowPosition = [[
Applies To: Module, MiniWindow
Prototype: ]].._NAME..[[.windowPosition = n
Setting this on the module will set the initial placement of all MiniWindows returned by the New() function. It is recommended to not set this value directly on MiniWindows, instead use the MW:WindowPosition() function.
n= number; Expected values range from 4-11. See windowPositions table for more information.]],
windowPositions = [[
Applies To: All
Prototype: N/A
Table enumerating windowPosition values. Placements are named after the cardinal points of the compass.
NE = 4;
N = 5;
NW = 6;
E = 7;
SE = 8;
S = 9;
SW = 10;
W = 11;]],
windowWidth = [[
Applies To: Module, MiniWindow
Prototype: MW.windowWidth = n
The Number of pixels the miniwindow is wide.
n = number; Expected to be a positive integer]],
AnchorRight = [[
Applies To: Bar
Prototype: Bar:AnchorRight(bool)
If True, then the 0 value is drawn on the right side of the gauge and captionPlacement, if set to innerLeft, is set to innerRight.]],
BadColour = [[
Applies To: Bar
Prototype: Bar:BadColour(colour)
Set the color for close to 0 values.
colour = string|number; colour can be the name of a color ("red"), an HTML formatted string ("#FF0000"), or a number (255)]],
BarStyle = [[
Applies To: Bar
Prototype: Bar:BarStyle(n)
A bitmask number representing the frame + fill desired for the bar.
n = number; 0 is text only, see barStyles for other values]],
Caption = [[
Applies To: Bar
Prototype: Bar:Caption(label)
Sets the text for the bar. If label is a number, commas are added for readability.
label = string|number; The desired text to print.]],
ColourText = [[
Applies To: Bar
Prototype: Bar:ColourText(val, threshold, good, bad [, neut])
Sets the textColour value to specified color.
Last three paramters can be specified as a name of a color ("red"), an HTML formatted string ("#FF0000"), or a number (255)
val = number; The value to be checked.
threshold = number; the value to check against.
good :: val > threshold (or val >=threshold if neut is not specified)
bad :: val < threshold
neut :: val == threshold]],
GoodColour = [[
Applies To: Bar
Prototype: Bar:GoodColour(val)
The color for solid bars with value > threshold, or the non-zero side of gradients.]],
Padding = [[
Applies To: Bar
Prototype: Bar:Padding(n)
The number of pixels to add to the bottom of a gauge before drawing the next gauge in the column.
n = number; A number of pixels. Negative values will cause overlapping gauges.]],
TextColour = [[
Applies To: Bar
Prototype: Bar:TextColour(val)
The default color for text to be printed in if no other colour codes are specified in the caption value. If colour codes are specified, this function sets the value of the @~ identifier.
val = string|number; name of a color ("red"), an HTML formatted string ("#FF0000"), or a number (255)]],
TextStyle = [[
Applies To: Bar
Prototype: Bar:TextStyle(n)
Sets the adornments to printed text.
n = number; value from 0-5. see textStyles for more information.]],
Threshold = [[
Applies To: Bar
Prototype: Bar:Threshold(n)
Sets the value that goodColour transitions to badColour for bars with a solid barstyle, or sets where the midpoint of the gradient is for gradientFixed barstyles.
n = number; a value from 0 to 100.]],
Value = [[
Applies To: Bar
Prototype: Bar:Value(n)
Sets what percent of the gauge is filled. Values over 100 will be drawn as 100% with goodColour 15% brighter, if possible.
n = number; Percent to draw in the gauge. ]],
WatchCaption = [[
Applies To: Bar
Prototype: Bar:WatchCaption(varname)
Specifies the name of a variable that contains the text displayed for a given bar. Unlike the Caption function, a variable containing a number will not display the additional commas.
varname = string; the name of a variable. ("foo", "tbl.foo")]],
WatchValue = [[
Applies To: Bar
Prototype: Bar:WatchValue(varname)
Specifies the name of a variable that contains the percent of the gauge to draw.
varname = string; the name of a variable. ("percentHP", "tbl.nested[fighterhealth]")]],
anchorRight = [[
Applies To: Bar
Prototype: Bar.anchorRight = bool
Value that determines which side of the gauge has the 0 value. You can set this value directly safely to a boolean value. ]],
badColour = [[
Applies To: Bar
Prototype: Bar.badColour = n
The number value of the color drawn near 0 values for gradients, or when value is less than or equal to threshold for solid fills. The BadColour function sets this value to the number equivalent to a color.
Bar:BadColour("indigo") -- ok
Bar.badColour = 8519755 -- ok
Bar.badColour = "indigo" -- NOT OK!!!!
n = number; the number value for a colour.]],
barStyle = [[
Applies To: Bar
Prototype: Bar.barStyle = n
The bitmasked number determining how a bar is drawn. See barStyles for the enumeration of possible styles.
n= number;]],
caption = [[
Applies To: Bar
Prototype: Bar.caption = txt
The text to display for a bar's label. This value does not modify numbers when printing.
Bar:Caption(1000) --> prints 1,000
Bar.caption = 1000 --> prints 1000
Additional color settings can be specified with an "@x" delimiter where x is a single character. The 16 default ansi colors are taken from MushClient's settings and are set to c,m,y,k,r,g,b,w and their bold values C,M,Y,K... The colour that's in the textColour value can be referred to with "@~". Additional colours can be specified by adding a table to the customColourCodes value with a single character key, and the number value of the colour.
"@@" is needed to actually print an @ symbol in a caption.
i.e.:
MW.customColourCodes = {z = 255}
MW.Bars[2].customColourCodes = {z = 128}
MW.Bars[1].caption = "@zVery Red"
MW.Bars[2].caption = "@zNot so red"
MW.Bars[3].caption = "@zVery Red @~textColour Colour"
txt = string; the label.]], --']]
captionPlacement = [[
Applies To: Bar
Prototype: Bar.captionPlacement = n
The value of where the caption is placed. It is recommended to set this value with the CaptionPlacement() function instead of setting the value directly.
n = number; a value ranging from 0 to 5]],
cellHeight = [[
Applies To: Bar
Prototype: Bar.cellHeight = n
The number of pixels from the top of the bar to the bottom of the gauge. Note that the padding value is added to this to determine the top of the following gauge. This value is calculated dynamically when a font is loaded.
This should be a lesser used value since it is mostly handled internally.
n = number; ]],
cellPadding = [[
Applies To: Bar
Prototype: Bar.cellPadding = n
Used mostly internally, this value is added to the height of a font to make the gauge a little bigger. It is only used to calculate cellHeight when a font is loaded.
n = number; number of pixels]],
gaugeHeight = [[
Applies To: Bar
Prototype: Bar.gaugeHeight = n
Value that determines the top edge of the gauge. The internal draw routine uses cellHeight - gaugeHeight to determine coordinates for drawing so that gauges are naturally aligned to the bottom of the cell.
n = number; the number of pixels high a gauge should be]],
gaugeLeft = [[
Applies To: Bar
Prototype: Bar.gaugeLeft = n
The number of pixels the gauge appears from the edge of the cell. Recalculated automatically by the CaptionPlacement function.
n = number; number of pixels.]],
goodColour = [[
Applies To: Bar
Prototype: Bar.goodColour = n
The number value of the color drawn near 100% values for gradients, or when value is greater than threshold for solid fills. The GoodColour function sets this value to the number equivalent to a color.
Bar:GoodColour("indigo") -- ok
Bar.goodColour = 8519755 -- ok
Bar.goodColour = "indigo" -- NOT OK!
n = number; the number value for a colour.]],
padding = [[
Applies To: Bar
Prototype: Bar.padding = n
The number of pixels from the bottom of this gauge to the top of the next cell.
n = number; numberof pixels]],
textColour = [[
Applies To: Bar
Prototype: Bar.textColour = n
The number value of the colour for printed text. If there are no colour codes specified in the caption. See caption for more information.]],
textStyle = [[
Applies To: Bar
Prototype: Bar.textStyle = n
n = number; 0-5. see textStyles for details.]],
threshold = [[
Applies To: Bar
Prototype: Bar.threshold = n
The percentage value where goodColour changes to badColour for solid fills, or where the midpoint of the gradient is for gradientFixed fills.
n = number; ]],
value = [[
Applies To: Bar
Prototype: Bar.value = n
The percentage of the bar to draw. Values greater than 100% will draw as 100% with the colour 15% brighter, if possible. Values lower than 0 don't draw.
n = number; percent of gauge to draw]], --']]
id = [[
Applies To: Bar
Prototype: Bar.id = n
It is recommended that you don't change this value. It is maintained internally. This value contains the index of this bar in the Bars table. It is useful for situations where you're dynamically adding and removing bars but kept a variable reference returned from the AddBar or InsertBar functions and may have lost track of which index this bar may be.]],
Bar = [[
Applies To: MiniWindow, Module
Prototype: MW.Bar = {...}
The Bar table contains the default values for newly created bars. A newly created Miniwindow (returned from the ]].._NAME..[[:New() function) has a new table with the values from ]].._NAME..[[.Bar copied into it. This table has a metatable with its __index value referring to the miniwindow that contains it.]],
Bars = [[
Applies To: MiniWindow
Prototype: MW.Bars = {...}
The Bars table contains nested tables of duplicated Bar tables with customized values. The Update function gets the settings for what to draw by looping through the Bars table's subtables. ]], --']]
gaugeWidth = [[Applies To: Bar
Prototype: Bar.gaugeWidth = n
The number of pixels the gauge extends within a bar. This property's default value is implemented in a metatable to dynamically return the appropriate width. Setting a value trumps the __index calculation, of course.
n = number; the number of pixels from the left edge ot the gauge to the right edge.]], --']]
["*Inheritance"] = [[Applies To: General Info
Prototype: N/A
This topic does not relate to one specific value or function. Its purpose is to document how the the metatables __index link.
Assuming:
MW = InfoBox:New()
MW:AddBar("Test")
print (MW.Bars[1].foo)
will search for a value foo in
MW.Bars[1],
MW.Bar,
MW,
then InfoBox
]],
["button"] =[[
Applies To: Bar
Protoype: bar.button = {mouseUp = "funcname", ...}
This value expects a table with certain keys paired to the _names_ of functions you write. If there is a table present a Hotspot is added that is drawn over the entire region of the cell (not just the gauge).
Missing keys are fine. (i.e. {mouseUp="f"} is OK. {mouseUp="f", mouseDown="", cancelMouseDown=""... is not necessary.)
Expected keys are named:
mouseUp
mouseDown
cancelMouseDown
tooltipText
mouseOver
cancelMouseOver
cursor
Your functions should be structured as:
function myClickFunction (flags, strBarsIndex)
local id = tonumber(strBarsIndex) -- because tbl["1"] is a different key than tbl[1]
...
MW.Bars[id].caption = "Clicked"
end
]],
["cellTop"] =[[
Applies To: Bar
Protoype: x = bar.cellTop
This value is set by the Update function. It is provided if you wish to add any custom coding and need to know where a bar was drawn. Setting this value produces no effect and will be overwritten the next time the Update function is called.
]],
["cellLeft"] =[[
Applies To: Bar
Protoype: x = bar.cellLeft
This value is set by the Update function. It is provided if you wish to add any custom coding and need to know where a bar was drawn. Setting this value produces no effect and will be overwritten the next time the Update function is called.
]],
["Fade"] =[[
Applies To: Bar
Protoype: bar:Fade(bool)
Calling this function sets the fade value and recalculates the shades table to which the fade effect refers. It's not required to call this function, since the draw routine will recalculate the shades table if necessary. There is an interesting bug that can be exploited by calling this function however.
Try the following:
for x = 100,30,-10 do
local foo = MW:AddBar("", x, "green", "red", false, MW.barStyles.glass + MW.barStyles.gradientScale)
foo:Fade(true)
foo.badColour = colour_names.dodgerblue
end
It's a bug, but it looks cool. Also this is why using the functions to set values is recommended!
]],
["cellWidth"] =[[
Applies To: Bar
Protoype: x = bar.cellWidth
The value for this property is generated by the Miniwindow's __index metamethod. The function in the metamethod returns (windowWidth / columns) for you. You should never have a reason to set your own value, and doing so will probably break things in uncool ways.
]], --']]
["*AddYourOwn"] =[[
Applies To: ALL
Protoype: N/A
A Bar is a table, after all, and you might find it useful to attach your own data, or a reference, to a bar. This is mostly safe to do as the module uses the pairs() and ipairs() functions in limited places.
pairs() is used to copy InfoBox.Bar to MW.Bar
ipairs() is used on the MW.Bar table to find the functions for the default parameters for AddBar.
ipairs() is used on the MW.Bars table to draw the bars.
Other than avoiding numerical indices in the 2 Bar(s) tables, it should be safe to add to a table as you see fit.
]],
--[=[
[""] =[[
Applies To:
Protoype:
]],
--]=]-- ']]
}
function Doc (self, topic)
if topic and _Doc[topic] then
print()
print (_Doc[topic],"\n")
else
local p = require "pairsByKeys"
print ("\nAdditional help can be found on the following functions and values\n")
local t , i={}, 0
for k,v in p(_Doc) do -- t[#t+1] = k end
local s= "%-20s"
i = i+1
Tell(s:format(k))
if i == 4 then
print()
i=0
end end
print("\n\nSyntax: ".. GetInfo(36) .. _NAME .. ':Doc("topic")\n')
end end