Easy DIY Miniwindows - fiendish/aardwolfclientpackage GitHub Wiki

Note: This is still experimental. Elements may change or improve. Watch this space.

The Aardwolf MUSHclient Package now includes special Lua code modules to bypass most or all of the horrible complexity of making miniwindows for your plugins.

Instructions

First, your plugin must be written in Lua. If you don't like Lua, I'm sorry.

Prefer learning by example? Explore the aard_ingame_help_window.xml plugin.

Step 1: Add require "themed_miniwindows" at the top of your plugin's script section.

To make a basic miniwindow, call:

my_window = ThemedBasicWindow(
   id,                    -- string, required, a unique identifier for this window
   default_left_position, -- integer, required, where to put it if the player hasn't moved it
   default_top_position,  -- integer, required, where to put it if the player hasn't moved it
   default_width,         -- integer, required, how big to make it if the player hasn't moved it
   default_height,        -- integer, required, how big to make it if the player hasn't moved it
   title,                 -- string, optional (nil means no titlebar), text to put into the title
   title_alignment,       -- string, optional (default is "center"), "left", "center", or "right"
   is_temporary,          -- boolean, optional (default is false), true adds a close button in the top left
   resizer_type,          -- nil/integer, optional (default is nil), nil for non-resizeable, 1 for demi, 2 for full
   do_while_resizing,     -- function, optional (default is nil), display function to call while resizing
   do_after_resizing,     -- function, optional (default is nil), display function to call after resizing is done
   do_on_delete,          -- function, optional (default is nil), cleanup function to call when closed/deleted
   title_font_name,       -- string, optional (default is Dina), override the default font name
   title_font_size,       -- integer, optional (default is 10), override the default font size
   defer_showing,         -- boolean, optional (default is false), true will prevent the window from appearing until you call :show() on it
   body_is_transparent    -- boolean, optional (default is false), if true, pixels the same color as Theme.PRIMARY_BODY will be transparent
)

Then draw whatever you want within the bounds of my_window.bodyleft, my_window.bodytop, my_window.bodyright, and my_window.bodybottom using the standard miniwindow drawing functions. There is no protection against you drawing on top of the window frame (titlebar, border, etc). You must protect yourself and stay within the lines. Get your magnifying glass out and make sure you don't accidentally draw over the edges.

As a best practice, put all of your custom drawing code into one function. If your miniwindow is resizable, assign that function as do_after_resizing and do_while_resizing above. If your drawing is too slow for smooth resizing, make a smaller faster function and assign that as do_while_resizing instead.

Normally changing color themes (see: Miniwindow-Color-Themes) restarts all plugins. This may not be desirable. To prevent your plugin from being restarted and instead just redraw your miniwindows when the player switches color themes, add this function to your plugin:

function OnPluginThemeChange()
   if WindowInfo(my_window_id, 1) ~= nil then
      -- Miniwindow color themes are applied at their creation, so create
      -- existing windows again here if the theme changes.
   end
end

Drawing to miniwindows does not automatically update the screen, so after your custom drawing code is finished, you may want to do CallPlugin("abc1a0944ae4af7586ce88dc", "BufferedRepaint"). This code will handle it for you during resizing, but not necessarily at other times.

If you need to paint over the body of a basic window with the background color, call

my_window:blank()

This does not remove any custom hotspots you might have added yourself.

If you need to reapply the window dressing (maybe you want to paint content partially behind the resizer widget), call

my_window:dress_window()

Or to change the window's title, call dress_window with the new title information

my_window:dress_window(new_title)

To delete my_window, call

my_window:delete()

Creating a new easy miniwindow with the same unique identifier as an existing one will automatically delete the previous one, so if you're just re-creating an existing one, there's no need to call delete() on it first. Setting is_temporary to true during window creation will add a button to the top-left corner of the window that deletes the window when clicked.

To reset my_window to its default position and size, call

my_window:reset()

Easy miniwindows will remember their size and location automatically if the player moves them around or resizes them.

To programmatically resize my_window without dragging the corner, call

my_window:resize(width, height)

To show/hide my_window, call

my_window:show()

or

my_window:hide()

If you created my_window with defer_showing set to true, you will need to call my_window:show() for it to be visible.

To get my_window's special WindowMenu objects, call

my_window:get_menu_items()

Easy miniwindows come pre-made with a right-click menu with commands to move the window to the front/back. If you want to override the hotspot that creates the right-click menu, you can optionally use these to migrate the built-in menu options into your own custom menu. This function returns a string and a numerically-indexed table of functions. You are expected to always prefix your WindowMenu strings with "!" in order to use the numeric indexes. If you don't know what this means or are confused, feel free to ask Fiendish in-game.

To change Z-order (front/back placement) of my_window without using the right-click menu, call

my_window:bring_to_front()

or

my_window:send_to_back()

To add clickable buttons to easy miniwindows, call

right, bottom = my_window:add_button(
   id,                 -- string, required, a unique identifier for this button
   left,               -- integer, optional (default is my_window.bodyleft), where to put it in the window
   top,                -- integer, optional (default is my_window.bodytop), where to put it in the window
   text,               -- string, optional (default is nothing), what to write on the button
   utf8,               -- boolean, optional (default is false), whether the text should be interpreted as utf8
   tooltip,            -- string, optional (default is none), mouseover tooltip
   mousedown_callback, -- function, optional (default is none), function to call when the button is pressed down
   mouseup_callback,   -- function, optional (default is none), function to call when the pressed button is released
   font,               -- string, optional (default is my_win.title_font), which font to use
   x_padding,          -- integer, optional, how much space between the button text and the edge of the button
   y_padding,          -- integer, optional, how much space between the button text and the edge of the button
   width,              -- integer, optional, negates x_padding, manually set the button width
   height,             -- integer, optional, negates y_padding, manually set the button height
   style               -- string, optionally one of Theme.STYLE_3D, Theme.STYLE_FLAT, Theme.STYLE_TRANSPARENT (default is flat), chooses the style of button (body_is_transparent must be set on the window for the transparent style to work)
)

(right and bottom are the bottom-right corner of the created button so you know how big the button is if you let it automatically set its own size from your text)

To make a text miniwindow, call:

my_window = ThemedTextWindow(
   id,                    -- string, required, a unique identifier for this window
   default_left_position, -- integer, required, where to put it if the player hasn't moved it
   default_top_position,  -- integer, required, where to put it if the player hasn't moved it
   default_width,         -- integer, required, how big to make it if the player hasn't moved it
   default_height,        -- integer, required, how big to make it if the player hasn't moved it
   title,                 -- string, optional (nil means no titlebar), text to put into the title
   title_alignment,       -- string, optional (default is "center"), "left", "center", or "right"
   is_temporary,          -- boolean, optional (default is false), true adds a close button in the top left
   resizeable,            -- boolean, optional (default is false), make the window resizeable
   text_scrollable,       -- boolean, optional (default is false), add a scrollbar and mousewheel scrolling
   text_selectable,       -- boolean, optional (default is false), make the text selectable
   text_copyable,         -- boolean, optional (default is false), make the text copyable via right-click
   url_hyperlinks,        -- boolean, optional (default is false), turn detected URLs into clickable links
   autowrap,              -- boolean, optional (default is false), automatically wrap text lines that are too wide
   title_font_name,       -- string, optional (default is Dina), override the title font name
   title_font_size,       -- integer, optional (default is 10), override the title font size
   text_font_name,        -- string, optional (default is Dina), override the body text font name
   text_font_size,        -- integer, optional (default is 10), override the body text font size
   text_max_lines,        -- integer, optional (default is 1000), maximum number of text lines to keep
   text_padding,          -- integer, optional (default is 5 pixels), space between text and miniwindow frame
   defer_showing,         -- boolean, optional (default is false), true will prevent the window from appearing until you call :show() on it
   body_is_transparent    -- boolean, optional (default is false), if true, pixels the same color as Theme.PRIMARY_BODY will be transparent
)

In addition to all of the functions from ThemedBasicWindow, ThemedTextWindow also has:

To add text to a ThemedTextWindow, call

my_window:add_text(
   string_or_styles,      -- string or table, required, text string with Aardwolf color codes or table of MUSHclient style tables
   draw_after,            -- boolean, optional (default is true), set to false to not immediately draw the new condition of the window
   links,                 -- table, optional, set as shown below to add clickable links within the line text
)

Links structure:

{
   {
       label=<tooltip text to show>,
       start=<index on line where link begins>,
       stop=<index on line where link ends>,
       text=<script text to perform>,
   },
   ...
}

See example of use down below.

This also accepts multiple lines at once either by embedding "\n" in your text string or nesting multiple stylerun tables in an enclosing table. (There are demos of this below.)

To clear the text contents of a ThemedTextWindow, call

my_window:clear(
   draw_after             -- boolean, optional (default is true), set to false to not immediately draw the new condition of the window
)

If you set draw_after to false and later want to draw the additions, call

my_window:draw()

To return the contents of a ThemedTextWindow in styles table format, call

my_styles = my_window:get_styles()

To return the contents of a ThemedTextWindow in color-coded text format, call

my_text = my_window:get_text()

To determine how much horizontal space is needed to display some text in a ThemedTextWindow, call

my_window:text_width(
   string_or_styles      -- string or table, required, text string with Aardwolf color codes or table of MUSHclient style tables
)

To scroll a ThemedTextWindow to a certain line, call

my_window:set_scroll(
   line_number           -- integer, required, which wrapped line of text to scroll to (the first line is 1, second line is 2, etc)
)

To resize a ThemedTextWindow to fit certain content metrics without needing to know the size of the window dressing, call

my_window:fit_size(
   content_width,        -- integer, optional, how wide you want the text content area to be not including the window dressing
   num_content_lines,    -- integer, optional, how many lines you want the text content area to hold
   max_width,            -- integer, optional, upper limit on window width if the content width plus the window dressing would be too wide
   max_height            -- integer, optional, upper limit on window height if the height of the lines plus the window dressing would be too wide
)

To resize a ThemedTextWindow to fit its current contents, call

my_window:fit_contents(
   max_width,            -- integer, optional, upper limit on window width
   max_height            -- integer, optional, upper limit on window height
)

Examples

Basic Window Example

require "themed_miniwindows"
my_window = ThemedBasicWindow("testwindow", 100, 100, 200, 150, "Hello", "center", true, 1)

Screenshot of miniwindow demo


Multiline Title With Colors Example

You can have colorized window titles with multiple lines (use $C to set color back to the theme default).

require "themed_miniwindows"
my_window = ThemedBasicWindow(
   "testwindow", 100, 100, 200, 150, "He@x123llo\n@GHe$Cllo", "center", true, 1
)

Screenshot of miniwindow demo


Title From Styles Table Example

Titles can also be one or more lines of styles.

require "themed_miniwindows"
title_styles = {
   {
      {
         bold=false,
         backcolour=0,
         length=11,
         textcolour=12632256,
         text="Hello"
      }
   },{
      {
         bold=true,
         backcolour=0,
         length=10,
         textcolour=255,
         text="Hello"
      }
   }
}
my_window = ThemedBasicWindow("testwindow", 100, 100, 200, 150, title_styles, "center", true, 1)

Screenshot of miniwindow demo


Resizer Style and Title Alignment

Alternate resizer style and title aligned to the left.

require "themed_miniwindows"
my_window = ThemedBasicWindow("testwindow", 100, 100, 200, 150, "Hello", "left", false, 2)

This is just a demo. Don't actually use resizer type 2 without a scrollbar. It's ugly.

Screenshot of miniwindow demo


Basic Drawing Example

You can draw whatever you want on the window as long as you follow a few rules like staying within the body boundaries. Because this window is resizable, we're going to put all of our drawing code into a special drawing function that will get run during and immediately after resizing.

require "themed_miniwindows"

function my_draw()
   my_window:blank()
   WindowRectOp(
      my_window.id, 2, my_window.bodyleft+20, my_window.bodytop+20, 
      my_window.bodyright-20, my_window.bodybottom-20, ColourNameToRGB("green")
   )
end

my_window = ThemedBasicWindow(
   "testwindow", 100, 100, 200, 150, "Hello", "center", true, 1, my_draw, my_draw
)

-- Draw our stuff manually the first time.
my_draw()

-- While the resize routine re-dresses the window after calling my_draw automatically,
-- calling my_draw manually doesn't.
-- And since my_draw draws over the area where the resize widget is, calling dress_window 
-- once manually here draws the resize widget again.
my_window:dress_window()

Screenshot of miniwindow demo


Clickable Buttons Example

Need clickable 3D buttons? There's a special function for adding them. This window isn't resizable so here we don't need to use a special drawing function.

require "themed_miniwindows"
my_window = ThemedBasicWindow("testwindow", 100, 100, 200, 150, "Hello", "center", true)
my_window:add_button(
   "testbutton",
   my_window.bodyleft + 20,
   my_window.bodytop + 20,
   "BUTTON GO ZOOM",
   false,
   nil,
   function(flags, button_id) print("CLICKED", button_id) end,
   function(flags, button_id) print("RELEASED", button_id) end,
   nil,
   5,
   5,
   nil,
   nil,
   Theme.STYLE_3D
)

Screenshot of miniwindow demo

Clicking the button prints:

CLICKED testbutton

RELEASED testbutton


Text Capturing Example

If all you want is to simply capture text, ThemedTextWindow is a specialized window just for that.

require "themed_miniwindows"
my_window = ThemedTextWindow("testwindow", 200, 200, 200, 200, "Hello Hello", "center", false, true, false)
for i=1,10 do
   my_window:add_text(tostring(i).."@WFoo@RFoo@MFoo@CFoo")
end

Screenshot of miniwindow demo


Scrollable Text Example

To make the text scrollable, activate scrolling.

require "themed_miniwindows"
my_window = ThemedTextWindow("testwindow", 200, 200, 200, 200, "Hello Hello", "center", false, true, true)
for i=1,100 do
   my_window:add_text(tostring(i).."@WFoo@RFoo@MFoo@CFoo")
end

Screenshot of miniwindow demo


Links In Text Example

require "themed_miniwindows"
my_window = ThemedTextWindow("testwindow", 200, 200, 200, 200, "Hello Hello", "center", false, true, false)
my_window:add_text(
   "test test test",
   true,
   {
      {label="First", start=1, stop=4, text="a='look';Execute(a)"},
      {label="Second", start=6, stop=9, text="a='scan';Execute(a)"}
   }
)

Screenshot of miniwindow demo


Multiline Text Example

Added text doesn't have to be one line. You can include "\n" newlines too or use a sequence of lines of styles instead.

require "themed_miniwindows"
my_window = ThemedTextWindow("testwindow", 200, 200, 200, 200, "Hello Hello", "center", false, true, true)
for i=1,100 do
   my_window:add_text("@w1234567890\n@R09876@G54321")  -- \n means new line
end

or

require "themed_miniwindows"
my_window = ThemedTextWindow("testwindow", 200, 200, 200, 200, "Hello Hello", "center", false, true, true)
styles = {                    -- begin multiple lines
   {                          -- begin line
      {                       -- begin style in line
         bold=false,
         backcolour=0,
         length=5,
         textcolour=12632256,
         text="1234567890"
      },                      -- end style in line
   },{                        -- end line, begin new line
      {                       -- begin style in line
         bold=true,
         backcolour=0,
         length=10,
         textcolour=255,
         text="09876"
      },                      -- end style in line
      {                       -- begin another style in same line
         bold=true,
         backcolour=0,
         length=10,
         textcolour=65280,
         text="54321"
      }                       -- end style in line
   }                          -- end line
}                             -- end multiple lines
for i=1,100 do
   my_window:add_text(styles)
end

Screenshot of miniwindow demo