The cplus module provides additional canvas-related functions that can be useful when writing scripts with a graphical user interface. See Modules/cplus/init.lua for the implementation details.

Graphics functions
Text functions
User interface functions
Some useful tables
Adjustable settings

The examples below assume a script has included this line:

local cp = require "cplus"

 
Graphics functions

cp.fill_ellipse(x, y, w, h, borderwd, fillrgba)
Draw an ellipse (or circle) inside the given rectangle. If borderwd is greater than zero then an antialiased ellipse of the given thickness is drawn using the current color. If fillrgba is not an empty table then the ellipse is filled with the given color.

Example: cp.fill_ellipse(200, 450, 140, 99, 2, {255,255,0,200})

cp.round_rect(x, y, w, h, radius, borderwd, fillrgba)
Draw a rounded rectangle where the given radius determines the curvature of each corner. If borderwd is greater than zero then an antialiased border of the given thickness is drawn using the current color. If fillrgba is not an empty table then the rectangle is filled with the given color.

Example: cp.round_rect(200, 300, 60, 30, 15, 0, {255,0,0,128})

 
Text functions

cp.minbox(clipname, wd, ht)
Find the minimal bounding box of non-transparent pixels in the given clip. This can be used to determine the real dimensions of some text. If all pixels in the clip are transparent then the returned values are all zero.

Example: local minx, miny, minwd, minht = cp.minbox("textclip", 80, 10)

cp.maketext(text, clipname, color, shadowx, shadowy, shadowcolor)
A simpler and more powerful way of creating text that can then be drawn with pastetext. Only the first argument, text, is required.

Example:
-- draw the string "Hello World" at 0, 0 on the current render target
cp.maketext("Hello World")
cp.pastetext(0, 0)

Usually the text clip is created using the current rgba values. This can be overridden by specifying the color argument. The text may also be given a shadow at a specific x, y pixel offset defined with the shadowx and shadowy arguments. The shadow will be drawn in opaque black unless a different color is specified with the shadowcol argument.

Example:
-- create a clip named "hello" with the text "Hello World" in blue with a yellow shadow offset by -1, -2 pixels.
local w, h = cp.maketext("Hello World", "hello", cp.blue, -1, -2, cp.yellow)

The function returns the width and height of the created clip (including any shadow).

cp.pastetext(x, y, transform, clipname)
Paste text onto the current render target. Only the first two arguments x, y are required. Returns the clip name.

Example:
-- paste a text clip at 0, 0 on the current render target
local textclip = cp.pastetext(0, 0)

The transform argument can be used to specify an affine transformation. The clipname may be used to specify a non-default clipname.

Example:
-- paste the clip named "hello" at 0, 0 and rotated clockwise
cp.pastetext(0, 0, cp.rcw, "hello")

 
User interface functions

cp.button(label, onclick, args)
Create and return a table representing a button. The button width depends on the given label text. If the cp.process function detects a click in this button then the given onclick function will be called with optional arguments. If supplied, args must be a table (the table will be unpacked and the results passed to the onclick function).

Example: cancel_button = cp.button("Cancel", glu.exit, {"bye bye"})

The button will only appear after its show function is called.

Example: cancel_button.show(10, 10)

cp.checkbox(label, onclick)
Create and return a table representing a check box. The label text will be drawn to the right of a tickable button. If the cp.process function detects a click in this check box (including the label) then the given onclick function will be called.

Example: line_box = cp.checkbox("Show lines", ToggleLines)

The check box will only appear after its show function is called.

Example: line_box.show(10, 40, true)

cp.radiobutton(label, onclick)
Create and return a table representing a radio button. The label text will be drawn to the right of a radio button. If the cp.process function detects a click in this radio button (including the label) then the given onclick function will be called.

Example:
draw_option = cp.radiobutton("Draw", SetDrawMode)
select_option = cp.radiobutton("Select", SetSelectMode)
move_option = cp.radiobutton("Move", SetMoveMode)

The radio button will only appear after its show function is called.

Example:
draw_option.show(10, 40, mode == "draw")
select_option.show(10, 40, mode == "select")
move_option.show(10, 40, mode == "move")

cp.slider(barwidth, minval, maxval, onclick)
Create and return a table representing a slider. The bar width must be greater than 0. The minimum and maximum values of the slider can be any integer values, as long as minval is less than maxval. If the cp.process function detects a click in this slider, and the slider value has changed, then the given onclick function will be called with two arguments: the new value and the slider table.

Example: red_slider = cp.slider(64, 0, 255, ColorChanged)

The slider will only appear after its show function is called.

Example: red_slider.show(10, 70, 255)

cp.menubar()
Create and return a table representing a menu bar. You can then use its addmenu function to add menus to the menu bar, then the additem function to append items to these menus. If the cp.process function detects a click in this menu bar then it will track the mouse and call the specified callback function if a menu item is selected.

Example:
mbar = cp.menubar()
-- add some menus
mbar.addmenu("File")
mbar.addmenu("Edit")
-- add items to File menu
mbar.additem(1, "New File", NewFile)
mbar.additem(1, "Open File...", OpenFile)
mbar.additem(1, "Save File...", SaveFile)
mbar.additem(1, "---", nil) -- separator
mbar.additem(1, "Exit", glu.exit)
-- add items to Edit menu
mbar.additem(2, "Undo", Undo)
mbar.additem(2, "Redo", Redo)

The menu bar will appear when its show function is called. Menu items can be enabled or disabled using the enableitem function, and ticked or unticked using the tickitem function.

Example:
mbar.enableitem(2, 1, CanUndo())
mbar.enableitem(2, 2, CanRedo())
mbar.show(0, 0, 600, 30)

cp.process(event)
Process the given event (normally the string returned by getevent). If it detects an unmodified left-click in a menu bar, button, check box, radio button or slider then it will call the appropriate onclick function and return an empty string to indicate the event was handled, otherwise it returns a copy of the given string so the caller can process the event.

Example: local event = cp.process( glu.getevent() )

cp.disable_all()
Use this call to prevent cp.process from detecting clicks in any existing menu bars, buttons, check boxes, radio buttons or sliders. This is typically followed by a matching cp.enable_all call at some later time. See the dialogs.lua code for how these calls are used to help implement a custom modal dialog.

Example: cp.disable_all()

cp.enable_all()
Use this call to undo the effects of the most recent cp.disable_all call. Clicks in the menu bars, buttons, check boxes, radio buttons or sliders that were disabled will now be detected.

Example: cp.enable_all()

cp.popupmenu()
Create and return a table representing a pop-up menu. You can then use its additem function to append an item with a given label and a callback function that will be called if the user selects the item.

Example:
editmenu = cp.popupmenu()
editmenu.additem("Cut", CutSelection)
editmenu.additem("Copy", CopySelection)
editmenu.additem("Clear", ClearSelection)
editmenu.additem("---", nil)
editmenu.additem("Paste", PasteClipboard)

The pop-up menu will appear when its show function is called. If necessary, the menu's position is automatically adjusted to ensure the entire menu is visible within the canvas.

Example:
editmenu.setbackcolor{0,128,0,255} -- dark green background
editmenu.show(mousex, mousey)

 
Some useful tables

The cplus module defines a number of tables for use in various functions.

Opaque colors:

cp.white = {255,255,255,255}
cp.gray = {128,128,128,255}
cp.black = {0,0,0,255}
cp.red = {255,0,0,255}
cp.green = {0,255,0,255}
cp.blue = {0,0,255,255}
cp.cyan = {0,255,255,255}
cp.magenta = {255,0,255,255}
cp.yellow = {255,255,0,255}

Affine transformations:

cp.identity = {1,0,0,1}
cp.flip = {-1,0,0,-1}
cp.flip_x = {-1,0,0,1}
cp.flip_y = {1,0,0,-1}
cp.swap_xy = {0,1,1,0}
cp.swap_xy_flip = {0,-1,-1,0}
cp.rcw = {0,-1,1,0}
cp.rccw = {0,1,-1,0}
cp.racw = cp.rccw
cp.r180 = cp.flip

 
Adjustable settings

The cplus module has a number of settings that control the appearance of buttons, menus, etc. Scripts can easily change these settings, as in the following examples:

cp.buttonht = 30 -- height of buttons
cp.radius = 5 -- curvature of button corners
cp.border = 3 -- thickness of button border (no border if 0)
cp.menubg = {40,40,40,255} -- dark gray background for menu bar and items
cp.menufont = "mono-bold" -- font for menu and item text
cp.menufontsize = 11 -- font size for menu and item text

A complete list of the settings you might want to adjust can be seen near the start of init.lua.