-- mw.lua -- Helper functions for miniwindows -- -- Author: Nick Gammon - 8th September 2008 --[[ Exposed functions are: mw.colourtext - show a string with imbedded colour codes mw.colour_conversion - table with colour codes in it - add more if you want mw.strip_colours - remove colour codes from a string mw.popup - make a popup window mw.tooltip - make a tooltip window EXAMPLE OF MAKING A POPUP WINDOW: require "mw" -- SET UP FOR POPUP WINDOWS - define colours, add fonts, make window id -- (DO THIS ONCE ONLY, eg. in OnPluginInstall) -- our window frame/background colours border_colour = 0xCCD148 background_colour = 0x222222 -- a unique ID infowin = GetPluginID () .. ":info" -- font IDs font_id = "popup_font" heading_font_id = "popup_heading_font" font_size = 8 -- use 8 pt Dina or 10 pt Courier local fonts = utils.getfontfamilies () -- choose a font that exists if fonts.Dina then font_name = "Dina" elseif fonts ["Lucida Sans Unicode"] then font_name = "Lucida Sans Unicode" else font_size = 10 font_name = "Courier" end -- if -- load fonts WindowCreate (infowin, 0, 0, 0, 0, 0, 0, 0) -- make initial window -- install the fonts WindowFont (infowin, font_id, font_name, font_size, false, false, false, false, miniwin.font_charset_ansi, miniwin.font_family_modern + miniwin.font_pitch_fixed) WindowFont (infowin, heading_font_id, font_name, font_size + 2, false, false, false, false, miniwin.font_charset_ansi, miniwin.font_family_modern + miniwin.font_pitch_fixed) -- NOW DISPLAY A WINDOW -- what to say - one line per table entry, with imbedded colour codes info = { "@Ctesting 1 2 3", "@GThis is a heading", "Line @Mwith @Bmultiple @Rcolours", } heading = "@MHello, @Yworld" left, top = 40, 50 align_right = false align_bottom = false capitalize = true -- show it mw.popup (infowin, -- window name to use heading_font_id, -- font to use for the heading font_id, -- font to use for each line heading, -- heading text info, -- table of lines to show (with colour codes) left, top, -- where to put it border_colour, -- colour for round rectangle line background_colour, -- colour for background capitalize, -- if true, force the first letter to upper case align_right, -- if true, align right side on "Left" parameter align_bottom) -- if true, align bottom side on "Top" parameter EXAMPLE OF MAKING A TOOLTIP WINDOW: -- SET UP FOR TOOLTIP WINDOWS - define colours, add fonts, make window id -- (DO THIS ONCE ONLY, eg. in OnPluginInstall) -- Example setup code: require "mw" -- get ready for tooltips win_tooltip = GetPluginID () .. "_tooltip" font_tooltip = "tf" bold_font_tooltip = "tfb" -- make the window WindowCreate (win_tooltip, 0, 0, 0, 0, 0, 0, 0) -- load some fonts into it WindowFont (win_tooltip, font_tooltip, "Tahoma", 8) WindowFont (win_tooltip, bold_font_tooltip, "Tahoma", 8, true) -- NOW DISPLAY A tooltip (Put bold lines inside asterisks) mw.tooltip (win_tooltip, -- window name to use font_tooltip, -- font to use for each line bold_font_tooltip, -- bold font "*Info*\nHello, world!\nHave fun.", -- tooltip text 45, 75, -- where to put it (x, y) 0, -- colour for text (black) 0, -- colour for border (black) ColourNameToRGB ("#FFFFE1")) -- colour for background --]] module (..., package.seeall) DEFAULT_COLOUR = "@w" TRANSPARENCY_COLOUR = 0x080808 BORDER_WIDTH = 2 local BLACK = 1 local RED = 2 local GREEN = 3 local YELLOW = 4 local BLUE = 5 local MAGENTA = 6 local CYAN = 7 local WHITE = 8 -- colour styles (eg. @r is normal red, @R is bold red) -- @- is shown as ~ -- @@ is shown as @ -- This table uses the colours as defined in the MUSHclient ANSI tab, however the -- defaults are shown on the right if you prefer to use those. colour_conversion = { k = GetNormalColour (BLACK) , -- 0x000000 r = GetNormalColour (RED) , -- 0x000080 g = GetNormalColour (GREEN) , -- 0x008000 y = GetNormalColour (YELLOW) , -- 0x008080 b = GetNormalColour (BLUE) , -- 0x800000 m = GetNormalColour (MAGENTA) , -- 0x800080 c = GetNormalColour (CYAN) , -- 0x808000 w = GetNormalColour (WHITE) , -- 0xC0C0C0 K = GetBoldColour (BLACK) , -- 0x808080 R = GetBoldColour (RED) , -- 0x0000FF G = GetBoldColour (GREEN) , -- 0x00FF00 Y = GetBoldColour (YELLOW) , -- 0x00FFFF B = GetBoldColour (BLUE) , -- 0xFF0000 M = GetBoldColour (MAGENTA) , -- 0xFF00FF C = GetBoldColour (CYAN) , -- 0xFFFF00 W = GetBoldColour (WHITE) , -- 0xFFFFFF -- add custom colours here } -- end conversion table -- displays text with colour codes imbedded -- -- win: window to use -- font_id : font to use -- Text : what to display -- Left, Top, Right, Bottom : where to display it -- Capitalize : if true, turn the first letter into upper-case function colourtext (win, font_id, Text, Left, Top, Right, Bottom, Capitalize, utf8) if Text:match ("@") then local x = Left -- current x position local need_caps = Capitalize Text = Text:gsub ("@%-", "~") -- fix tildes Text = Text:gsub ("@@", "\0") -- change @@ to 0x00 -- make sure we start with @ or gsub doesn't work properly if Text:sub (1, 1) ~= "@" then Text = DEFAULT_COLOUR .. Text end -- if for colour, text in Text:gmatch ("@(%a)([^@]+)") do text = text:gsub ("%z", "@") -- put any @ characters back if need_caps then local count text, count = text:gsub ("%a", string.upper, 1) need_caps = count == 0 -- if not done, still need to capitalize yet end -- if if #text > 0 then x = x + WindowText (win, font_id, text, x, Top, Right, Bottom, colour_conversion [colour] or GetNormalColour (WHITE), utf8) end -- some text to display end -- for each colour run return x end -- if if Capitalize then Text = Text:gsub ("%a", string.upper, 1) end -- if leading caps wanted return WindowText (win, font_id, Text, Left, Top, Right, Bottom, colour_conversion [DEFAULT_COLOUR] or GetNormalColour (WHITE)) end -- colourtext -- converts text with colour styles in it into style runs function ColoursToStyles (Text) if Text:match ("@") then astyles = {} Text = Text:gsub ("@%-", "~") -- fix tildes Text = Text:gsub ("@@", "\0") -- change @@ to 0x00 -- make sure we start with @ or gsub doesn't work properly if Text:sub (1, 1) ~= "@" then Text = DEFAULT_COLOUR .. Text end -- if for colour, text in Text:gmatch ("@(%a)([^@]+)") do text = text:gsub ("%z", "@") -- put any @ characters back if #text > 0 then table.insert (astyles, { text = text, length = #text, textcolour = colour_conversion [colour] or GetNormalColour (WHITE), backcolour = GetNormalColour (BLACK) }) end -- if some text end -- for each colour run. return astyles end -- if any colour codes at all -- No colour codes, create a single style. return { { text = Text, length = #Text, textcolour = GetNormalColour (WHITE), backcolour = GetNormalColour (BLACK) } } end -- function ColoursToStyles -- take a string, and remove colour codes from it (eg. "@Ghello" becomes "hello" function strip_colours (s) s = s:gsub ("@%-", "~") -- fix tildes s = s:gsub ("@@", "\0") -- change @@ to 0x00 s = s:gsub ("@%a([^@]*)", "%1") return (s:gsub ("%z", "@")) -- put @ back end -- strip_colours function popup (win, -- window name to use heading_font_id, -- font to use for the heading font_id, -- font to use for each line heading, -- heading text info, -- table of lines to show (with colour codes) Left, Top, -- where to put it border_colour, -- colour for round rectangle line background_colour, -- colour for background capitalize, -- if true, force the first letter to be upper case align_right, -- if true, align right side on "Left" parameter align_bottom) -- if true, align bottom side on "Top" parameter assert (WindowInfo (win, 1), "Window " .. win .. " must already exist") assert (WindowFontInfo (win, heading_font_id, 1), "No font " .. heading_font_id .. " in " .. win) assert (WindowFontInfo (win, font_id, 1), "No font " .. font_id .. " in " .. win) local font_height = WindowFontInfo (win, font_id, 1) local font_leading = WindowFontInfo (win, font_id, 4) + WindowFontInfo (win, font_id, 5) local heading_font_height = WindowFontInfo (win, heading_font_id, 1) -- find text width - minus colour codes local infowidth = 0 local infoheight = 0 -- calculate heading width and height if heading and #heading > 0 then infowidth = WindowTextWidth (win, heading_font_id, strip_colours (heading)) infoheight = heading_font_height end -- have a heading -- calculate remaining width and height for _, v in ipairs (info) do infowidth = math.max (infowidth, WindowTextWidth (win, font_id, strip_colours (v))) infoheight = infoheight + font_height end -- for infowidth = infowidth + (2 * BORDER_WIDTH) + -- leave room for border WindowFontInfo (win, font_id, 6) -- one character width extra infoheight = infoheight + (2 * BORDER_WIDTH) + -- leave room for border font_leading + -- plus leading below bottom line, 10 -- and 5 pixels top and bottom if align_right then Left = Left - infowidth end -- if align_right if align_bottom then Top = Top - infoheight end -- if align_bottom WindowCreate (win, Left, Top, -- where infowidth, -- width (gap of 5 pixels per side) infoheight, -- height miniwin.pos_top_left, -- position mode: can't be 0 to 3 miniwin.create_absolute_location + miniwin.create_transparent, TRANSPARENCY_COLOUR) -- background (transparent) colour WindowCircleOp (win, miniwin.circle_round_rectangle, BORDER_WIDTH, BORDER_WIDTH, -BORDER_WIDTH, -BORDER_WIDTH, -- border inset border_colour, miniwin.pen_solid, BORDER_WIDTH, -- line background_colour, miniwin.brush_solid, -- fill 5, 5) -- diameter of ellipse local x = BORDER_WIDTH + WindowFontInfo (win, font_id, 6) / 2 -- start 1/2 character in local y = BORDER_WIDTH + 5 -- skip border, and leave 5 pixel gap -- heading if wanted if heading and #heading > 0 then colourtext (win, heading_font_id, heading, x, y, 0, 0, capitalize) y = y + heading_font_height end -- have a heading -- show each line for _, v in ipairs (info) do colourtext (win, font_id, v, x, y, 0, 0, capitalize) y = y + font_height end -- for -- display popup window WindowShow (win, true) end -- popup -- -------------------------------------------------------------- -- Displays a tooltip (small box with "arrow" pointing to something of interest) -- Bold lines are surrounded by asterisks (eg. *Info*) -- -------------------------------------------------------------- function tooltip (win, -- window name to use font_id, -- font to use for each line bold_font_id, -- font to use for bold lines text, -- tooltip text Left, Top, -- where to put it (x, y) text_colour, -- colour for text border_colour, -- colour for border background_colour) -- colour for background assert (WindowInfo (win, 1), "Window " .. win .. " must already exist") assert (WindowFontInfo (win, font_id, 1), "No font " .. font_id .. " in " .. win) assert (WindowFontInfo (win, bold_font_id, 1), "No font " .. bold_font_id .. " in " .. win) local font_height = WindowFontInfo (win, font_id, 1) local bold_font_height = WindowFontInfo (win, bold_font_id, 1) local MARGIN = 8 local TIPSIZE = 12 -- break text into lines local t = utils.split (text, "\n") -- tooltip height local height = MARGIN * 2 -- margin at top and bottom -- tooltip width local width = TIPSIZE * 2 -- must be at least large enough for the tip part for k, v in ipairs (t) do -- bold lines start and end with an asterisk local boldText = string.match (v, "^%*(.*)%*$") if boldText then width = math.max (width, WindowTextWidth (win, bold_font_id, boldText, true)) height = height + bold_font_height else width = math.max (width, WindowTextWidth (win, font_id, v, true)) height = height + font_height end -- if end -- for width = width + (MARGIN * 2) -- margin per side -- the tooltip pointer starts TIPSIZE pixels to the right and descends TIPSIZE pixels WindowCreate (win, Left - TIPSIZE, Top - height - TIPSIZE, width + 2, height + TIPSIZE + 2, -- 2 pixels margin to allow for border + TIPSIZE downwards for the tip miniwin.pos_top_left, -- position miniwin.create_absolute_location + miniwin.create_transparent + miniwin.create_ignore_mouse, TRANSPARENCY_COLOUR) -- mucking around here to get rounded rectangle local points = { -- top LH corner 1, 6, 2, 6, 2, 4, 3, 4, 3, 3, 4, 3, 4, 2, 6, 2, 6, 1, -- top RH corner width - 5, 1, width - 5, 2, width - 3, 2, width - 3, 3, width - 2, 3, width - 2, 4, width - 1, 4, width - 1, 6, width, 6, -- bottom RH corner width, height - 5, width - 1, height - 5, width - 1, height - 3, width - 2, height - 3, width - 2, height - 2, width - 3, height - 2, width - 3, height - 1, width - 5, height - 1, width - 5, height, (TIPSIZE * 2) + 1, height, -- RH side of tip TIPSIZE + 1, height + TIPSIZE, -- bottom of tip TIPSIZE + 1, height, -- LH side of tip -- bottom LH corner 6, height, 6, height - 1, 4, height - 1, 4, height - 2, 3, height - 2, 3, height - 3, 2, height - 3, 2, height - 5, 1, height - 5, } -- make the tooltip polygon WindowPolygon(win, table.concat (points, ","), border_colour, -- pen colour miniwin.pen_solid, 1, -- pen (1 pixel wide) background_colour, -- brush colour miniwin.brush_solid, -- brush true) -- close it -- put the text into it local top = MARGIN + 1 for _, v in ipairs (t) do -- bold lines start and end with an asterisk local boldText = string.match (v, "^%*(.*)%*$") if boldText then WindowText (win, bold_font_id, boldText, MARGIN + 1, top, 0, 0, text_colour, true) top = top + bold_font_height else WindowText (win, font_id, v, MARGIN + 1, top, 0, 0, text_colour, true) top = top + font_height end -- if end -- for WindowShow (win, true) end -- tooltip