[Home] [Downloads] [Search] [Help/forum]

Miniwindows in MUSHclient - Drawing text

Written by Nick Gammon - July 2008. Updated September 2010.

On this page:

See also:

Examples on this page

Most of the examples on this page are drawn in a 220 x 100 pixel window, with a grid drawn every 20 pixels to make it clearer the effect of the example code. The code for producing the grid is described in Creating miniwindows.

Introduction to text

The MUSHclient miniwindows let you draw text in:

Text basics

To draw text accurately you need to know where the text will appear in your window. In particular, if you are mixing large and small fonts on the same line, you need to understand where the "baseline" of the text is.

Let's look at an example of some rather large text, so we can clearly see the component parts of a line of text:

We see the following:

If we wanted to put more text at the right, in a different font, we need to allow for the different likely position of the baseline. In this example, the baseline was 36 pixels down from where we started drawing. If we wanted some text next to it which had a baseline of 26 pixels down, we would need to start the next lot of text 10 pixels lower to compensate (that is, add 10 to the starting y position).

Getting started with text - loading the fonts

Before text can be drawn to the miniwindow, the font must be "loaded". This specifies that this miniwindow is going to use this font. Later on you simply refer to the "font id" you gave when loading the font.

WindowFont function prototype:

This loads the specified font into the miniwindow, and remembers it by the nominated "font id". The font id is later used for drawing in this font. You do not need to specify a colour, that is done when the text is actually drawn.

Example of loading a font

List all fonts

WindowFontList function prototype:

This returns a list of all fonts loaded into this miniwindow. You could use this to find which fonts have been loaded, and then use WindowFontInfo to find information about each one.


Get information about a font

WindowFontInfo function prototype:

This returns information about a loaded font. You need to specify the name of the miniwindow, and the font id you used when loading the font.

Probably the most important item is number 1 - the height of the font. If you are drawing multiple lines of a particular font, you should add the font height to the "y" parameter of the rectangle, to move the pixel position down the window an appropriate amount.

Writing text

WindowText function prototype:

This draws some text in miniwindow.

If successful, returns the number of pixels the text took horizontally. Thus, by adding that to the "Left" parameter, you can draw some more text to the right of what was just drawn.

If unsuccessful, returns a negative number as follows:

Examples of writing text

-- load fonts (do this once only)
WindowFont (win, "f", "Trebuchet MS", 14, true, false, false, false)
WindowFont (win, "f2", "Trebuchet MS", 30, false, true, false, false)

-- Draw in 14 point Trebuchet MS bold
WindowText (win, "f", "Quest Log",
            5, 10, 0, 0,  -- rectangle
            ColourNameToRGB ("blue"), 
            false) -- not Unicode
-- Draw in 30 point Trebuchet MS italic
WindowText (win, "f2", "MUSHclient",
            5, 40, 0, 0,  -- rectangle
            ColourNameToRGB ("saddlebrown"), 
            false) -- not Unicode

In this example I have used 0,0 as the right,bottom parameters for the rectangle. As explained on the Drawing shapes page, that actually means the bottom-right corner. Thus the text has the rest of the window to be drawn in. This is normally safe to do, unless you want to "clip" the text, so it does not spill over onto some other graphic object to the right of it, or below it.

Calculate text width

WindowTextWidth function prototype:

This calculates how many pixels a particular piece of text will take up, which can help in calculating how big a window to create, or where to put other things. Note that WindowText returns the number of pixels when it draws text, so you don't need to call WindowTextWidth simply to work out where to draw the very next item.

If successful, returns the number of pixels the text would take horizontally, when drawn in this font.

If unsuccessful, returns a negative number as follows:

Examples of calculating width

-- load font (do this once only)
WindowFont (win, "f", "Trebuchet MS", 14, true, false, false, false)

width   = WindowTextWidth (win, "f", "Lazy dog yawns")  -- width of text  (140)

Hints for working with text

Hint 1

When writing a plugin, create your window initially in OnPluginInstall, as a 1 x 1 pixel window, simply to load in the font (ready for future use), and find out the font height.

function OnPluginInstall ()

  win = "A" .. GetPluginID ()
  font_id = "fn"
  bold_font_id = "fb"
  font_name = "Sylfaen"    -- the actual font
  font_size = 8

  -- make window so I can grab the font info
  WindowCreate (win, 
                0, 0, 0, 0,  -- empty window
                miniwin.pos_top_left,   -- position (irrelevant)
                0,   -- flags (none)
                0)   -- background colour (black - irrelevant)
  -- add font in normal and bold styles                 
  WindowFont (win, font_id, font_name, font_size, 
              false, false, false, false,  -- normal
              miniwin.font_charset_ansi, miniwin.font_family_any)
  WindowFont (win, bold_font_id, font_name, font_size, 
              true, false, false, false,   -- bold
              miniwin.font_charset_ansi, miniwin.font_family_any)
  -- find height of font for future calculations             
  font_height = WindowFontInfo (win, font_id, 1)  -- height
end -- OnPluginInstall

The above example creates the window (which is initially invisible), adds two fonts ("Sylfaen" normal, and bold), and finds the height of the normal font (we assume the bold one will be the same height).

Later on, if we need to display (say) 10 lines of text, then we can re-create the window with vertical size of 10 x font_height (plus a few extra pixels for a border, if wanted).

Hint 2

In a plugin, as you capture information (for example, quest instructions), do not display it as each line arrives, as that will cause flicker. Instead, batch it up in a table, and calculate the maximum size window you will need when it has all arrived, like this:

table.insert (quest_info, text)  -- store text for later
max_width = math.max (max_width, WindowTextWidth (win, font_id, text))  -- find max width

This assumes you set max_width to zero at the start of the operation (eg. when the first trigger fires at the start of the quest message).

Hint 3

When you have collected all the text, re-create the window, using the number of lines gathered to specify the vertical size, and the maximum width for the horizontal size, plus a bit extra for margins:

-- recreate the window the correct size
WindowCreate (win, 
             0, 0,   -- left, top (auto-positions)
             max_width + 10,     -- width
             ((#quest_info + 2) * font_height) + 5,  -- height
             miniwin.pos_center_right,       -- position
             0,  -- flags
             0x696969)   -- yellow background

In the above example I am planning to output a heading line ("Current quest") and a "time to go" line, so I have taken the number of captured text lines (#quest_info) and added 2, then multiplied by the font height. Finally I added 5 to give a small vertical margin.

Then for the width, I took the maximum width as calculated as each line arrived, added 10 for a margin, and used that.

Hint 4

Now we can make a helper function Display_Line to handle displaying each line easily. This function merely takes line number, subtracts 1 to make it zero-relative, and multiplies by the line height. That gives the pixel position vertically. It then displays the text in the requested font id and colour.

function Display_Line (line, text, id, colour)

local left = 5
local top = (line - 1) * font_height

  WindowText (win, id, text, left, top, 0, 0, colour)

end -- Display_Line

Hint 5

Displaying style runs is almost as easy. Style runs are available to triggers (in Lua) or can be obtained by calling GetStyleInfo in other languages. Each style run already has the text to be displayed, and the text colour. A simple loop will display each style, one after the other. The loop below makes use of the fact that each call to WindowText gives you the number of pixels you need to move to the right.

function Display_Styled_Line (line, styles, id)

local left = 5
local top = (line - 1) * font_height

  for _, v in ipairs (styles) do
    left = left + WindowText (win, id, v.text, left, top, 0, 0, v.textcolour)
  end -- for each style run                 

end -- Display_Styled_Line

Other pages about miniwindows


Written by Nick Gammon - 5K

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( http://www.gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Internet Contents Rating Association (ICRA) - 2K]    [Web site powered by FutureQuest.Net]