Difference between revisions of "Lua API:Interface"
(Add note about sdl constants) |
(Add interface.textbox and interface.window) |
||
(7 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | The Interface API includes objects for UI components such as buttons, labels and checkboxes and methods for access to the very primitive window manager and input events. | + | The Interface API includes objects for UI components such as buttons, labels, and checkboxes and methods for access to the very primitive window manager and input events. |
== Classes == | == Classes == | ||
=== Component === | === Component === | ||
− | + | ||
+ | All classes in here extend Component. That means the methods in here are standard and can be used in any component type. | ||
+ | |||
+ | Component is abstract, and cannot be created directly. | ||
+ | |||
==== Component:visible ==== | ==== Component:visible ==== | ||
− | + | flag = Component:visible() | |
− | + | Component:visible(flag) | |
− | + | * <code>flag</code>: boolean true / false on whether the component is visible or not | |
− | |||
− | |||
==== Component:size ==== | ==== Component:size ==== | ||
− | + | width, height = Component:size() | |
− | + | Component:size(width, height) | |
− | + | * <code>width</code>: The width of the component | |
− | + | * <code>height</code>: The height of the component | |
− | |||
==== Component:position ==== | ==== Component:position ==== | ||
− | + | x, y = Component:position() | |
− | + | Component:position(x, y) | |
− | + | * <code>x</code>: The x coordinate of the component | |
− | + | * <code>y</code>: The y coordinate of the component | |
− | |||
=== Button === | === Button === | ||
− | + | Create a button with [[#interface.button|interface.button]] | |
− | |||
==== Button:action ==== | ==== Button:action ==== | ||
− | + | Button:action(callback) | |
− | Sets the listener for button actions | + | * <code>callback</code>: The callback, a function which receives one argument, <code>sender</code> |
− | + | ** <code>sender</code>: The button itself | |
+ | |||
+ | Sets the listener for button actions. The action is triggered whenever the button is left clicked (clicked means a full mousedown + mouseup happened inside the button). | ||
+ | |||
+ | Example: | ||
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
− | local newButton = | + | local newButton = ui.button(10, 10, 100, 17, "Times clicked: 0") |
− | newButton:action(function(sender) sender:text(" | + | local counter = 0 |
+ | newButton:action(function(sender) | ||
+ | counter = counter + 1 | ||
+ | sender:text("Times clicked: " .. counter) | ||
+ | end) | ||
interface.addComponent(newButton) | interface.addComponent(newButton) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== Button:text ==== | ==== Button:text ==== | ||
− | + | text = Button:text() | |
− | + | Button:text(text) | |
− | + | * <code>text</code>: The button text | |
− | |||
− | |||
==== Button:enabled ==== | ==== Button:enabled ==== | ||
− | + | flag = Button:enabled() | |
− | + | Button:enabled(flag) | |
+ | * <code>flag</code>: Whether or not the button is enabled | ||
− | + | Controls the enabled flag of the button. Buttons that are disabled cannot be clicked, change to gray, and don't respond to mousemove events either. | |
− | |||
=== ProgressBar === | === ProgressBar === | ||
− | + | Create a progress bar with [[#interface.progressBar|interface.progressBar]] | |
− | |||
==== ProgressBar:progress ==== | ==== ProgressBar:progress ==== | ||
− | + | progress = ProgressBar:progress() | |
− | + | ProgressBar:progress(progress) | |
+ | * <code>progress</code>: The current progress value | ||
− | + | Gets or sets the current progress. Progress ranges from 0 to 100, but a special case of <tt>-1</tt> will change the behavior of the progress bar to intermediate (constantly scrolling to indicate progress) | |
− | |||
− | |||
− | Progress ranges from 0 to 100, but a special case of <tt>-1</tt> will change the | ||
==== ProgressBar:status ==== | ==== ProgressBar:status ==== | ||
− | + | text = ProgressBar:status() | |
− | + | ProgressBar:status(text) | |
+ | * <code>text</code>: The progress bar status | ||
− | + | Status is simply a text representation of the current action being performed, for example, "Working" or just a percentage | |
− | |||
− | |||
− | Status is | ||
=== Slider === | === Slider === | ||
− | + | Create a slider with [[#interface.slider|interface.slider]] | |
− | |||
==== Slider:onValueChanged ==== | ==== Slider:onValueChanged ==== | ||
− | + | Slider:onValueChanged(callback) | |
− | + | * <code>callback</code>: The callback, a function which receives two arguments, <code>sender</code> and <code>value</code> | |
+ | ** <code>sender</code>: The slider itself | ||
+ | ** <code>value</code>: The value the slider was set to | ||
+ | |||
+ | Called whenever the slider value changes due to a change by the user. Changes made to the value from Lua don't trigger the callback. | ||
==== Slider:value ==== | ==== Slider:value ==== | ||
− | + | value = Slider:value() | |
− | + | Slider:value(value) | |
− | + | * <code>value</code>: The value of the slider, which is a number in the range [0, steps] | |
− | |||
− | |||
==== Slider:steps ==== | ==== Slider:steps ==== | ||
− | + | steps = Slider:steps() | |
− | + | Slider:steps(steps) | |
− | + | * <code>steps</code>: The number of steps the slider has. Must be a positive number. If the current slider position is greater than the new number of steps, the slider position will be set to the new max. | |
− | |||
− | |||
=== Checkbox === | === Checkbox === | ||
− | + | Create a checkbox with [[#interface.checkbox|interface.checkbox]] | |
− | |||
==== Checkbox:action ==== | ==== Checkbox:action ==== | ||
− | + | Checkbox:action(callback) | |
− | + | * <code>callback</code>: The callback, a function which receives two arguments, <code>sender</code> and <code>checked</code> | |
+ | ** <code>sender</code>: The checkbox itself | ||
+ | ** <code>checked</code>: A true / false flag on whether the checkbox is currently checked | ||
+ | |||
+ | Called whenever the checkbox state is toggled due to a change by the user. | ||
==== Checkbox:text ==== | ==== Checkbox:text ==== | ||
− | + | text = Checkbox:text() | |
− | + | Checkbox:text(text) | |
+ | * <code>text</code>: The checkbox's text | ||
− | + | Text is drawn to the right of the checkbox, see for example the options menu. The text that responds to mouseover events is part of the checkbox itself. (The text underneath is a separate Label) | |
− | |||
==== Checkbox:checked ==== | ==== Checkbox:checked ==== | ||
− | + | flag = Checkbox:checked() | |
− | + | Checkbox:checked(flag) | |
− | + | * <code>flag</code>: The checked state of the checkbox, a boolean true / false flag | |
− | |||
− | |||
=== Label === | === Label === | ||
− | + | Create a label with [[#interface.label|interface.label]] | |
− | |||
==== Label:text ==== | ==== Label:text ==== | ||
− | + | text = Label:text() | |
− | + | Label:text(text) | |
− | + | * <code>text</code>: The label text | |
− | |||
− | |||
=== Textbox === | === Textbox === | ||
− | + | Create a label with [[#interface.textbox|interface.textbox]] | |
− | |||
==== Textbox:onTextChanged ==== | ==== Textbox:onTextChanged ==== | ||
− | + | Textbox:onTextChanged(callback) | |
− | + | * <code>callback</code>: The callback, a function which receives one argument, <code>sender</code> | |
+ | ** <code>sender</code>: The textbox itself | ||
+ | |||
+ | Called whenever the textbox is changed due to an action by the user. For example, typing or deleting a character, cutting or pasting text. Changes via Lua do not trigger this callback. | ||
==== Textbox:text ==== | ==== Textbox:text ==== | ||
− | + | text = Textbox:text() | |
− | + | Textbox:text(text) | |
+ | * <code>text</code>: The textbox's text | ||
− | + | When setting text, the old selection is cleared, the text is replaced, and the cursor is set to the end. | |
− | |||
==== Textbox:readonly ==== | ==== Textbox:readonly ==== | ||
− | + | flag Textbox:readonly() | |
− | + | Textbox:readonly(flag) | |
+ | * <code>flag</code>: true / false flag for the textbox's readonly flag. | ||
− | + | readonly textboxes can't have their text changed in any way. The only possible user interaction is moving the cursor and text selection operations. | |
− | |||
=== Window === | === Window === | ||
− | + | Create a window with [[#interface.window|interface.window]] | |
− | |||
==== Window:addComponent ==== | ==== Window:addComponent ==== | ||
− | + | Window:addComponent(component) | |
− | + | * <code>component</code>: The component to add | |
+ | |||
+ | Adds a component to the window. The component must not have already been added to another Window object. | ||
==== Window:removeComponent ==== | ==== Window:removeComponent ==== | ||
− | + | Window:removeComponent(component) | |
− | Remove a component from the window | + | * <code>component</code>: The component to remove |
+ | |||
+ | Remove a component from the window. If this component isn't part of the window, does nothing. | ||
+ | |||
+ | == Component Creation == | ||
+ | The following methods create new instances of the component classes. | ||
+ | |||
+ | === interface.button === | ||
+ | Construct a new Button | ||
+ | button = interface.button(x, y, width, height, [text, [tooltip]]) | ||
+ | * <code>button</code>: The button, see class Button documentation for details on its methods | ||
+ | * <code>x</code>: The x position of the button. This is relative to the parent window's top left corner. | ||
+ | * <code>y</code>: The y position of the button. This is relative to the parent window's top left corner. | ||
+ | * <code>width</code>: The width of the button | ||
+ | * <code>height</code>: The height of the button | ||
+ | * <code>text</code>: The text displayed inside the button. Defaults to empty string. | ||
+ | * <code>tooltip</code>: The tooltip. Only valid for buttons placed directly on the main screen, it will show up in the bottom right corner where element descriptions normally show. Defaults to empty string. | ||
+ | |||
+ | The button won't be automatically placed down, for that see Window::addComponent or ui.addComponent. You will almost certainly also want to add a callback with Button::action to set what happens when the button is clicked. | ||
+ | |||
+ | === interface.progressBar === | ||
+ | Construct a new ProgressBar | ||
+ | progressBar = interface.progressBar(x, y, width, height, [progress, [status]]) | ||
+ | * <code>progressBar</code>: The progress bar, see class ProgressBar documentation for details on its methods | ||
+ | * <code>x</code>: The x position of the progress bar. This is relative to the parent window's top left corner. | ||
+ | * <code>y</code>: The y position of the progress bar. This is relative to the parent window's top left corner. | ||
+ | * <code>width</code>: The width of the progress bar | ||
+ | * <code>height</code>: The height of the progress bar | ||
+ | * <code>progress</code>: The initial progress. Must be between -1 and 100. Defaults to 0. -1 is a special case which will show a constant progress animation. | ||
+ | * <code>status</code>: The status. Will be drawn as text inside the progress bar. | ||
+ | |||
+ | The progress bar won't be automatically placed down, for that see Window::addComponent or ui.addComponent. Progress bars don't make any progress by default. You're responsible in your code for setting progress to a value between 0 and 100 to indicate the percentage. To show a constant scrolling animation, progress can be set to -1. This can be seen, for example, after downloading an update and it begins unpacking. | ||
+ | |||
+ | === interface.slider === | ||
+ | Construct a new Slider | ||
+ | slider = interface.slider(x, y, width, height, steps) | ||
+ | * <code>x</code>: The x position of the slider. This is relative to the parent window's top left corner. | ||
+ | * <code>y</code>: The y position of the slider. This is relative to the parent window's top left corner. | ||
+ | * <code>width</code>: The width of the slider | ||
+ | * <code>height</code>: The height of the slider | ||
+ | * <code>steps</code>: The number of steps this slider has. AKA how many "notches" will it stop at while the user drags it. | ||
+ | |||
+ | The progress bar won't be automatically placed down, for that see Window::addComponent or ui.addComponent. For an example of a slider in TPT's ui, check the color picker in the deco editor. | ||
+ | |||
+ | === interface.checkbox === | ||
+ | Construct a new Checkbox | ||
+ | checkbox = interface.checkbox(x, y, width, height, [text]) | ||
+ | * <code>x</code>: The x position of the checkbox. This is relative to the parent window's top left corner. | ||
+ | * <code>y</code>: The y position of the checkbox. This is relative to the parent window's top left corner. | ||
+ | * <code>width</code>: The width of the checkbox mouseover area | ||
+ | * <code>height</code>: The height of the checkbox mouseover area | ||
+ | * <code>text</code>: Text displayed to the right of the checkbox. Optional, the default is empty string. | ||
+ | |||
+ | The checkbox won't be automatically placed down, for that see Window::addComponent. The size of the square "checkbox" itself is always constant. <code>width</code> / <code>height</code> are instead used to control the area where mouseover events allow you to check / uncheck the box. 16 is recommended for height, for width you should ensure it's at least as wide as the text. | ||
+ | |||
+ | === interface.label === | ||
+ | Construct a new Label | ||
+ | label = interface.label(x, y, width, height, text) | ||
+ | * <code>x</code>: The x position of the label. This is relative to the parent window's top left corner. | ||
+ | * <code>y</code>: The y position of the label. This is relative to the parent window's top left corner. | ||
+ | * <code>width</code>: The width of the label | ||
+ | * <code>height</code>: The height of the label | ||
+ | * <code>text</code>: Label text | ||
+ | |||
+ | The label won't be automatically placed down, for that see Window::addComponent. Label text that extends past the width or height of the label will be cut off at the border. For that reason, and to ensure proper text selection via mouse events, please make sure the label size fits the text being displayed. | ||
+ | |||
+ | Labels are horizontally centered by default. This creates problems for left-aligned text in interfaces. To properly left align text, you should get the exact width with [[Lua_API:Graphics#graphics.textSize|gfx.textSize]]. For single line labels, height should be set to 16. | ||
+ | |||
+ | === interface.textbox === | ||
+ | Construct a new Textbox | ||
+ | textbox = interface.textbox(x, y, width, height, [text, [placeholder]]) | ||
+ | * <code>x</code>: The x position of the textbox. This is relative to the parent window's top left corner. | ||
+ | * <code>y</code>: The y position of the textbox. This is relative to the parent window's top left corner. | ||
+ | * <code>width</code>: The width of the textbox | ||
+ | * <code>height</code>: The height of the textbox | ||
+ | * <code>text</code>: Optional default text | ||
+ | * <code>placeholder</code>: Optional text that is shown when the textbox is both unfocused and empty. Used, for example, to show [password] in the login ui. | ||
+ | |||
+ | The textbox won't be automatically placed down, for that see Window::addComponent. A border will be drawn around the textbox. The text length is unrestricted, if the text typed by the user is too long, then it will scroll to the left and right. Pressing enter in a textbox will no-op, and newlines are not accepted. Limits on length and valid characters can be applied by listening to the text change event. | ||
+ | |||
+ | === interface.window === | ||
+ | Construct a new Window | ||
+ | window = interface.window(x, y, width, height) | ||
+ | * <code>x</code>: The x position of the window. Use -1 to center it horizontally. | ||
+ | * <code>y</code>: The y position of the window. Use -1 to center it vertically. | ||
+ | * <code>width</code>: The width of the window. Must be at least 10. | ||
+ | * <code>height</code>: The height of the window. Must be at least 10. | ||
+ | |||
+ | The window won't be automatically placed down, for that see [[#interface.showWindow|interface.showWindow]]. Once the window is added, it takes focus and receives all input events, and the main TPT window is no longer active. Parent windows are drawn underneath and dimmed out. Parents windows (aka the main window) don't run any tick events either. This means all Lua scripts will stop processing until the window closes, including your own. To handle input events like mouse, keyboard, and ticks, special window event handlers need to be registered instead. | ||
+ | |||
+ | By default, the Window can't be closed. Clicking outside of the window and ESC normally close windows, but this behavior needs to be added manually by listening to the <code>onTryExit</code> event. Clicking the "X" button from your OS still closes the window. | ||
== Methods == | == Methods == | ||
Line 172: | Line 262: | ||
From the API user's perspective, the grabTextInput-dropTextInput pair implements an on-off switch. The purpose of this switch is to help the interface engine determine when to enter and exit text input state. In this state, the engine asks for OS help with text input (which may or may not involve enabling an Input Method) and delivers textinput events received from the OS to the API user. | From the API user's perspective, the grabTextInput-dropTextInput pair implements an on-off switch. The purpose of this switch is to help the interface engine determine when to enter and exit text input state. In this state, the engine asks for OS help with text input (which may or may not involve enabling an Input Method) and delivers textinput events received from the OS to the API user. | ||
− | The engine should only enter text input state when the API user expects textinput events (for example, when a textbox is in focus). To correctly communicate this, grabTextInput should be called when processing of textinput events starts and dropTextInput when it ends. Note that textinput events may be delivered regardless of the state of this on-off switch, most likely as a result of another API user calling grabTextInput and dropTextInput. | + | The engine should only enter the text input state when the API user expects textinput events (for example, when a textbox is in focus). To correctly communicate this, grabTextInput should be called when the processing of textinput events starts and dropTextInput when it ends. Note that textinput events may be delivered regardless of the state of this on-off switch, most likely as a result of another API user calling grabTextInput and dropTextInput. |
=== interface.dropTextInput === | === interface.dropTextInput === | ||
Line 180: | Line 270: | ||
=== interface.addComponent === | === interface.addComponent === | ||
nil interface.addComponent(Component newComponent) | nil interface.addComponent(Component newComponent) | ||
− | + | Adds a component to the master game window. | |
=== interface.removeComponent === | === interface.removeComponent === | ||
nil interface.removeComponent(Component newComponent) | nil interface.removeComponent(Component newComponent) | ||
− | + | Removes a component from the master game window. | |
=== interface.showWindow === | === interface.showWindow === | ||
Line 196: | Line 286: | ||
=== interface.textInputRect === | === interface.textInputRect === | ||
nil interface.textInputRect(number x, number y, number w, number h) | nil interface.textInputRect(number x, number y, number w, number h) | ||
− | Enables composition, for multi-byte | + | Enables composition, for multi-byte Unicode characters. |
+ | |||
+ | === interface.beginConfirm === | ||
+ | |||
+ | Opens a confirm prompt, and runs a callback after the user's input. | ||
+ | |||
+ | <pre>interface.beginConfirm(title, message, buttonText, callback)</pre> | ||
+ | * <code>title</code>: Header message for the confirm prompt. Default "Title" | ||
+ | * <code>message</code>: Body message for the confirm prompt, can be multiple lines. Default "Message" | ||
+ | * <code>buttonText</code>: Text to display for the confirm button. Default "Confirm" | ||
+ | * <code>callback</code>: Callback function to run after the user gives input. Receives a single boolean as an argument. | ||
+ | |||
+ | Only the <code>callback</code> argument is required. The rest are optional. The final arg to the function will be used as the callback. If the user clicks "Confirm" or presses enter, <code>true</code> is passed in. If the user clicks "Cancel", presses escape, or closes the dialog any other way, <code>false</code> is passed. | ||
+ | |||
+ | === interface.beginInput === | ||
+ | |||
+ | Opens an input prompt, and runs a callback after the user's input. | ||
+ | |||
+ | <pre>interface.beginInput(title, prompt, text, shadow, callback)</pre> | ||
+ | * <code>title</code>: Header message for the input prompt. Default "Title" | ||
+ | * <code>prompt</code>: Body message for the input prompt, can be multiple lines. Default "Enter some text:" | ||
+ | * <code>text</code>: Default text for the textbox. Defaults to empty string. | ||
+ | * <code>shadow</code>: Default shadow text displayed when textbox is empty and defocused. Defaults to empty string. | ||
+ | * <code>callback</code>: Callback function to run after the user gives input. Receives either a string or nil as the only argument. | ||
+ | |||
+ | Only the <code>callback</code> argument is required. The rest are optional. The final arg to the function will be used as the callback. If the user clicks "Okay" or presses enter, the textbox's text is passed. If the user clicks "Cancel", presses escape, or closes the dialog any other way, <code>nil</code> is passed. | ||
+ | |||
+ | === interface.beginMessageBox === | ||
+ | |||
+ | Opens a message box, and runs a callback after the user closes it. | ||
+ | |||
+ | <pre>interface.beginMessageBox(title, message, large, callback)</pre> | ||
+ | * <code>title</code>: Header message for the message box. Default "Title" | ||
+ | * <code>message</code>: Body message for the message box, can be multiple lines. Default "Message" | ||
+ | * <code>large</code>: boolean that controls if the message box should be a fixed-size larger variant, that is both taller and wider. Default false. | ||
+ | * <code>callback</code>: Callback function to run after the user closes the message box. Runs no matter how it is closed, and takes no arguments. | ||
+ | |||
+ | All arguments are optional. The final arg to the function will be used as the callback. | ||
+ | |||
+ | === interface.beginThrowError === | ||
+ | |||
+ | Opens an error dialog box, and runs a callback after the user closes it. | ||
+ | |||
+ | <pre>interface.beginThrowError(errorMessage, callback)</pre> | ||
+ | * <code>errorMessage</code>: Body message for the error prompt, can be multiple lines. Default "Error Text" | ||
+ | * <code>callback</code>: Callback function to run after the user closes the error prompt. Runs no matter how it is closed, and takes no arguments. | ||
+ | |||
+ | All arguments are optional. The final arg to the function will be used as the callback. | ||
+ | |||
+ | === interface.activeMenu === | ||
+ | |||
+ | Gets or sets the active menu. | ||
+ | |||
+ | interface.activeMenu(menuSection) | ||
+ | menuSection = interface.activeMenu() | ||
+ | * <code>menuSection</code>: The menusection. See the reference of menusection constants in the [[Lua_API:Elements#Menu_sections|elements api]]. | ||
+ | |||
+ | === interface.menuEnabled === | ||
+ | |||
+ | Controls whether menusections are enabled (shown) in the UI. | ||
+ | |||
+ | interface.menuEnabled(menuSection, enabled) | ||
+ | enabled = interface.menuEnabled(menuSection) | ||
+ | * <code>menuSection</code>: The menusection. See the reference of menusection constants in the [[Lua_API:Elements#Menu_sections|elements api]]. | ||
+ | * <code>enabled</code>: boolean true/false describing if the menu section is enabled. | ||
+ | |||
+ | If using an invalid menusection, an Invalid Menu error is raised. | ||
+ | |||
+ | === interface.numMenus === | ||
+ | |||
+ | Returns the number of menus | ||
+ | |||
+ | numMenus = interface.numMenus() | ||
+ | * <code>numMenus</code>: The number of enabled menus. | ||
+ | |||
+ | Menus that aren't enabled don't count towards this limit. | ||
+ | |||
+ | === interface.activeTool === | ||
+ | |||
+ | Gets or sets an active element selection. | ||
+ | |||
+ | interface.activeTool(toolIndex, identifier) | ||
+ | identifier = interface.activeTool(toolIndex) | ||
+ | * <code>toolIndex</code>: The tool index. Should be between 0 and <code>interface.NUM_TOOLINDICES</code>. The indices correspond to: | ||
+ | ** <code>0</code>: Left click | ||
+ | ** <code>1</code>: Right click | ||
+ | ** <code>2</code>: Middle click | ||
+ | ** <code>3</code>: "Replace Mode" element | ||
+ | * <code>identifier</code>. The tool identifier. This is a string that uniquely identifies a tool, for example <code>"DEFAULT_PT_BGLA"</code> or <code>"DEFAULT_TOOL_HEAT"</code>. | ||
+ | |||
+ | === interface.brushID === | ||
+ | |||
+ | Gets or set the brush index. | ||
+ | |||
+ | interface.brushID(brushIndex) | ||
+ | brushIndex = interface.brushID() | ||
+ | * <code>brushIndex</code>: The index of the brush to set. Should be between 0 and <code>sim.NUM_BRUSHES</code>. For default brushes, the following constants can be used: | ||
+ | ** <code>sim.BRUSH_CIRCLE</code>: Circle brush | ||
+ | ** <code>sim.BRUSH_SQUARE</code>: Square brush | ||
+ | ** <code>sim.BRUSH_TRIANGLE</code>: Triangle brush | ||
+ | ** <code>sim.NUM_DEFAULTBRUSHES</code>: Number of default brushes, excluding custom brushes | ||
+ | |||
+ | === interface.brushRadius === | ||
+ | |||
+ | Gets or sets the radius of the brush | ||
+ | |||
+ | interface.brushRadius(w, h) | ||
+ | w, h = interface.brushRadius() | ||
+ | * <code>w</code>: Brush width | ||
+ | * <code>h</code>: Brush height | ||
+ | |||
+ | === interface.perfectCircleBrush === | ||
+ | |||
+ | Gets / Sets the "Perfect Circle" option | ||
+ | |||
+ | interface.perfectCircleBrush(flag) | ||
+ | flag = interface.perfectCircleBrush() | ||
+ | * <code>flag</code>: boolean true / false on whether the setting is enabled or not | ||
+ | |||
+ | === interface.mousePosition === | ||
+ | |||
+ | Returns the current mouse position | ||
+ | |||
+ | mouseX, mouseY = interface.mousePosition() | ||
+ | * <code>mouseX</code>: mouse x position | ||
+ | * <code>mouseY</code>: mouse y position | ||
+ | |||
+ | This is the position of the mouse in the full interface, so it ignores zoom window and can be outside of sim bounds. To convert into sim coords and adjust for zoom window, see [[Lua_API:Simulation#simulation.adjustCoords|sim.adjustCoords]]. | ||
+ | |||
+ | === interface.console === | ||
+ | |||
+ | Control or check whether the console is open | ||
+ | |||
+ | interface.console(shown) | ||
+ | shown = interface.console() | ||
+ | * <code>shown</code>: boolean true/false on whether or not the console is shown. | ||
+ | |||
+ | If you set it to false while in the console, it will close. Scripts can also use it to open the console. This action is non-blocking, so script execution will continue. But as soon as control is returned to the engine, further Lua callbacks will stop (because no event handlers run while the console is open). | ||
== Constants == | == Constants == | ||
Line 203: | Line 430: | ||
== Example == | == Example == | ||
− | This code has examples of some of the features of the interface API, it shows a window with various components with some testing | + | This code has examples of some of the features of the interface API, it shows a window with various components with some testing behavior. |
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
-- Test Window | -- Test Window |
Latest revision as of 04:10, 7 June 2024
The Interface API includes objects for UI components such as buttons, labels, and checkboxes and methods for access to the very primitive window manager and input events.
Contents
- 1 Classes
- 2 Component Creation
- 3 Methods
- 3.1 interface.grabTextInput
- 3.2 interface.dropTextInput
- 3.3 interface.addComponent
- 3.4 interface.removeComponent
- 3.5 interface.showWindow
- 3.6 interface.closeWindow
- 3.7 interface.textInputRect
- 3.8 interface.beginConfirm
- 3.9 interface.beginInput
- 3.10 interface.beginMessageBox
- 3.11 interface.beginThrowError
- 3.12 interface.activeMenu
- 3.13 interface.menuEnabled
- 3.14 interface.numMenus
- 3.15 interface.activeTool
- 3.16 interface.brushID
- 3.17 interface.brushRadius
- 3.18 interface.perfectCircleBrush
- 3.19 interface.mousePosition
- 3.20 interface.console
- 4 Constants
- 5 Example
Classes
Component
All classes in here extend Component. That means the methods in here are standard and can be used in any component type.
Component is abstract, and cannot be created directly.
Component:visible
flag = Component:visible() Component:visible(flag)
-
flag
: boolean true / false on whether the component is visible or not
Component:size
width, height = Component:size() Component:size(width, height)
-
width
: The width of the component -
height
: The height of the component
Component:position
x, y = Component:position() Component:position(x, y)
-
x
: The x coordinate of the component -
y
: The y coordinate of the component
Button
Create a button with interface.button
Button:action
Button:action(callback)
-
callback
: The callback, a function which receives one argument,sender
-
sender
: The button itself
-
Sets the listener for button actions. The action is triggered whenever the button is left clicked (clicked means a full mousedown + mouseup happened inside the button).
Example:
local newButton = ui.button(10, 10, 100, 17, "Times clicked: 0")
local counter = 0
newButton:action(function(sender)
counter = counter + 1
sender:text("Times clicked: " .. counter)
end)
interface.addComponent(newButton)
Button:text
text = Button:text() Button:text(text)
-
text
: The button text
Button:enabled
flag = Button:enabled() Button:enabled(flag)
-
flag
: Whether or not the button is enabled
Controls the enabled flag of the button. Buttons that are disabled cannot be clicked, change to gray, and don't respond to mousemove events either.
ProgressBar
Create a progress bar with interface.progressBar
ProgressBar:progress
progress = ProgressBar:progress() ProgressBar:progress(progress)
-
progress
: The current progress value
Gets or sets the current progress. Progress ranges from 0 to 100, but a special case of -1 will change the behavior of the progress bar to intermediate (constantly scrolling to indicate progress)
ProgressBar:status
text = ProgressBar:status() ProgressBar:status(text)
-
text
: The progress bar status
Status is simply a text representation of the current action being performed, for example, "Working" or just a percentage
Slider
Create a slider with interface.slider
Slider:onValueChanged
Slider:onValueChanged(callback)
-
callback
: The callback, a function which receives two arguments,sender
andvalue
-
sender
: The slider itself -
value
: The value the slider was set to
-
Called whenever the slider value changes due to a change by the user. Changes made to the value from Lua don't trigger the callback.
Slider:value
value = Slider:value() Slider:value(value)
-
value
: The value of the slider, which is a number in the range [0, steps]
Slider:steps
steps = Slider:steps() Slider:steps(steps)
-
steps
: The number of steps the slider has. Must be a positive number. If the current slider position is greater than the new number of steps, the slider position will be set to the new max.
Checkbox
Create a checkbox with interface.checkbox
Checkbox:action
Checkbox:action(callback)
-
callback
: The callback, a function which receives two arguments,sender
andchecked
-
sender
: The checkbox itself -
checked
: A true / false flag on whether the checkbox is currently checked
-
Called whenever the checkbox state is toggled due to a change by the user.
Checkbox:text
text = Checkbox:text() Checkbox:text(text)
-
text
: The checkbox's text
Text is drawn to the right of the checkbox, see for example the options menu. The text that responds to mouseover events is part of the checkbox itself. (The text underneath is a separate Label)
Checkbox:checked
flag = Checkbox:checked() Checkbox:checked(flag)
-
flag
: The checked state of the checkbox, a boolean true / false flag
Label
Create a label with interface.label
Label:text
text = Label:text() Label:text(text)
-
text
: The label text
Textbox
Create a label with interface.textbox
Textbox:onTextChanged
Textbox:onTextChanged(callback)
-
callback
: The callback, a function which receives one argument,sender
-
sender
: The textbox itself
-
Called whenever the textbox is changed due to an action by the user. For example, typing or deleting a character, cutting or pasting text. Changes via Lua do not trigger this callback.
Textbox:text
text = Textbox:text() Textbox:text(text)
-
text
: The textbox's text
When setting text, the old selection is cleared, the text is replaced, and the cursor is set to the end.
Textbox:readonly
flag Textbox:readonly() Textbox:readonly(flag)
-
flag
: true / false flag for the textbox's readonly flag.
readonly textboxes can't have their text changed in any way. The only possible user interaction is moving the cursor and text selection operations.
Window
Create a window with interface.window
Window:addComponent
Window:addComponent(component)
-
component
: The component to add
Adds a component to the window. The component must not have already been added to another Window object.
Window:removeComponent
Window:removeComponent(component)
-
component
: The component to remove
Remove a component from the window. If this component isn't part of the window, does nothing.
Component Creation
The following methods create new instances of the component classes.
interface.button
Construct a new Button
button = interface.button(x, y, width, height, [text, [tooltip]])
-
button
: The button, see class Button documentation for details on its methods -
x
: The x position of the button. This is relative to the parent window's top left corner. -
y
: The y position of the button. This is relative to the parent window's top left corner. -
width
: The width of the button -
height
: The height of the button -
text
: The text displayed inside the button. Defaults to empty string. -
tooltip
: The tooltip. Only valid for buttons placed directly on the main screen, it will show up in the bottom right corner where element descriptions normally show. Defaults to empty string.
The button won't be automatically placed down, for that see Window::addComponent or ui.addComponent. You will almost certainly also want to add a callback with Button::action to set what happens when the button is clicked.
interface.progressBar
Construct a new ProgressBar
progressBar = interface.progressBar(x, y, width, height, [progress, [status]])
-
progressBar
: The progress bar, see class ProgressBar documentation for details on its methods -
x
: The x position of the progress bar. This is relative to the parent window's top left corner. -
y
: The y position of the progress bar. This is relative to the parent window's top left corner. -
width
: The width of the progress bar -
height
: The height of the progress bar -
progress
: The initial progress. Must be between -1 and 100. Defaults to 0. -1 is a special case which will show a constant progress animation. -
status
: The status. Will be drawn as text inside the progress bar.
The progress bar won't be automatically placed down, for that see Window::addComponent or ui.addComponent. Progress bars don't make any progress by default. You're responsible in your code for setting progress to a value between 0 and 100 to indicate the percentage. To show a constant scrolling animation, progress can be set to -1. This can be seen, for example, after downloading an update and it begins unpacking.
interface.slider
Construct a new Slider
slider = interface.slider(x, y, width, height, steps)
-
x
: The x position of the slider. This is relative to the parent window's top left corner. -
y
: The y position of the slider. This is relative to the parent window's top left corner. -
width
: The width of the slider -
height
: The height of the slider -
steps
: The number of steps this slider has. AKA how many "notches" will it stop at while the user drags it.
The progress bar won't be automatically placed down, for that see Window::addComponent or ui.addComponent. For an example of a slider in TPT's ui, check the color picker in the deco editor.
interface.checkbox
Construct a new Checkbox
checkbox = interface.checkbox(x, y, width, height, [text])
-
x
: The x position of the checkbox. This is relative to the parent window's top left corner. -
y
: The y position of the checkbox. This is relative to the parent window's top left corner. -
width
: The width of the checkbox mouseover area -
height
: The height of the checkbox mouseover area -
text
: Text displayed to the right of the checkbox. Optional, the default is empty string.
The checkbox won't be automatically placed down, for that see Window::addComponent. The size of the square "checkbox" itself is always constant. width
/ height
are instead used to control the area where mouseover events allow you to check / uncheck the box. 16 is recommended for height, for width you should ensure it's at least as wide as the text.
interface.label
Construct a new Label
label = interface.label(x, y, width, height, text)
-
x
: The x position of the label. This is relative to the parent window's top left corner. -
y
: The y position of the label. This is relative to the parent window's top left corner. -
width
: The width of the label -
height
: The height of the label -
text
: Label text
The label won't be automatically placed down, for that see Window::addComponent. Label text that extends past the width or height of the label will be cut off at the border. For that reason, and to ensure proper text selection via mouse events, please make sure the label size fits the text being displayed.
Labels are horizontally centered by default. This creates problems for left-aligned text in interfaces. To properly left align text, you should get the exact width with gfx.textSize. For single line labels, height should be set to 16.
interface.textbox
Construct a new Textbox
textbox = interface.textbox(x, y, width, height, [text, [placeholder]])
-
x
: The x position of the textbox. This is relative to the parent window's top left corner. -
y
: The y position of the textbox. This is relative to the parent window's top left corner. -
width
: The width of the textbox -
height
: The height of the textbox -
text
: Optional default text -
placeholder
: Optional text that is shown when the textbox is both unfocused and empty. Used, for example, to show [password] in the login ui.
The textbox won't be automatically placed down, for that see Window::addComponent. A border will be drawn around the textbox. The text length is unrestricted, if the text typed by the user is too long, then it will scroll to the left and right. Pressing enter in a textbox will no-op, and newlines are not accepted. Limits on length and valid characters can be applied by listening to the text change event.
interface.window
Construct a new Window
window = interface.window(x, y, width, height)
-
x
: The x position of the window. Use -1 to center it horizontally. -
y
: The y position of the window. Use -1 to center it vertically. -
width
: The width of the window. Must be at least 10. -
height
: The height of the window. Must be at least 10.
The window won't be automatically placed down, for that see interface.showWindow. Once the window is added, it takes focus and receives all input events, and the main TPT window is no longer active. Parent windows are drawn underneath and dimmed out. Parents windows (aka the main window) don't run any tick events either. This means all Lua scripts will stop processing until the window closes, including your own. To handle input events like mouse, keyboard, and ticks, special window event handlers need to be registered instead.
By default, the Window can't be closed. Clicking outside of the window and ESC normally close windows, but this behavior needs to be added manually by listening to the onTryExit
event. Clicking the "X" button from your OS still closes the window.
Methods
interface.grabTextInput
nil interface.grabTextInput()
Signal to the interface engine that textinput events are expected and will be handled (for example, your textbox just gained focus and is ready for such events). Once called, it should not be called again until after calling interface.dropTextInput; see below.
From the API user's perspective, the grabTextInput-dropTextInput pair implements an on-off switch. The purpose of this switch is to help the interface engine determine when to enter and exit text input state. In this state, the engine asks for OS help with text input (which may or may not involve enabling an Input Method) and delivers textinput events received from the OS to the API user.
The engine should only enter the text input state when the API user expects textinput events (for example, when a textbox is in focus). To correctly communicate this, grabTextInput should be called when the processing of textinput events starts and dropTextInput when it ends. Note that textinput events may be delivered regardless of the state of this on-off switch, most likely as a result of another API user calling grabTextInput and dropTextInput.
interface.dropTextInput
nil interface.dropTextInput()
Signal to the interface engine that textinput events are not expected and will not be handled (for example, your textbox just lost focus and will not handle such events anymore). Once called, it should not be called again until after calling interface.grabTextInput; see above.
interface.addComponent
nil interface.addComponent(Component newComponent)
Adds a component to the master game window.
interface.removeComponent
nil interface.removeComponent(Component newComponent)
Removes a component from the master game window.
interface.showWindow
nil interface.showWindow(Window newWindow)
Push a Window into the top of the modal stack
interface.closeWindow
nil interface.closeWindow(Window newWindow)
Pop a Window off the top of the modal stack. If the given Window is not the top item in the stack, this will have no effect.
interface.textInputRect
nil interface.textInputRect(number x, number y, number w, number h)
Enables composition, for multi-byte Unicode characters.
interface.beginConfirm
Opens a confirm prompt, and runs a callback after the user's input.
interface.beginConfirm(title, message, buttonText, callback)
-
title
: Header message for the confirm prompt. Default "Title" -
message
: Body message for the confirm prompt, can be multiple lines. Default "Message" -
buttonText
: Text to display for the confirm button. Default "Confirm" -
callback
: Callback function to run after the user gives input. Receives a single boolean as an argument.
Only the callback
argument is required. The rest are optional. The final arg to the function will be used as the callback. If the user clicks "Confirm" or presses enter, true
is passed in. If the user clicks "Cancel", presses escape, or closes the dialog any other way, false
is passed.
interface.beginInput
Opens an input prompt, and runs a callback after the user's input.
interface.beginInput(title, prompt, text, shadow, callback)
-
title
: Header message for the input prompt. Default "Title" -
prompt
: Body message for the input prompt, can be multiple lines. Default "Enter some text:" -
text
: Default text for the textbox. Defaults to empty string. -
shadow
: Default shadow text displayed when textbox is empty and defocused. Defaults to empty string. -
callback
: Callback function to run after the user gives input. Receives either a string or nil as the only argument.
Only the callback
argument is required. The rest are optional. The final arg to the function will be used as the callback. If the user clicks "Okay" or presses enter, the textbox's text is passed. If the user clicks "Cancel", presses escape, or closes the dialog any other way, nil
is passed.
interface.beginMessageBox
Opens a message box, and runs a callback after the user closes it.
interface.beginMessageBox(title, message, large, callback)
-
title
: Header message for the message box. Default "Title" -
message
: Body message for the message box, can be multiple lines. Default "Message" -
large
: boolean that controls if the message box should be a fixed-size larger variant, that is both taller and wider. Default false. -
callback
: Callback function to run after the user closes the message box. Runs no matter how it is closed, and takes no arguments.
All arguments are optional. The final arg to the function will be used as the callback.
interface.beginThrowError
Opens an error dialog box, and runs a callback after the user closes it.
interface.beginThrowError(errorMessage, callback)
-
errorMessage
: Body message for the error prompt, can be multiple lines. Default "Error Text" -
callback
: Callback function to run after the user closes the error prompt. Runs no matter how it is closed, and takes no arguments.
All arguments are optional. The final arg to the function will be used as the callback.
interface.activeMenu
Gets or sets the active menu.
interface.activeMenu(menuSection) menuSection = interface.activeMenu()
-
menuSection
: The menusection. See the reference of menusection constants in the elements api.
Controls whether menusections are enabled (shown) in the UI.
interface.menuEnabled(menuSection, enabled) enabled = interface.menuEnabled(menuSection)
-
menuSection
: The menusection. See the reference of menusection constants in the elements api. -
enabled
: boolean true/false describing if the menu section is enabled.
If using an invalid menusection, an Invalid Menu error is raised.
interface.numMenus
Returns the number of menus
numMenus = interface.numMenus()
-
numMenus
: The number of enabled menus.
Menus that aren't enabled don't count towards this limit.
interface.activeTool
Gets or sets an active element selection.
interface.activeTool(toolIndex, identifier) identifier = interface.activeTool(toolIndex)
-
toolIndex
: The tool index. Should be between 0 andinterface.NUM_TOOLINDICES
. The indices correspond to:-
0
: Left click -
1
: Right click -
2
: Middle click -
3
: "Replace Mode" element
-
-
identifier
. The tool identifier. This is a string that uniquely identifies a tool, for example"DEFAULT_PT_BGLA"
or"DEFAULT_TOOL_HEAT"
.
interface.brushID
Gets or set the brush index.
interface.brushID(brushIndex) brushIndex = interface.brushID()
-
brushIndex
: The index of the brush to set. Should be between 0 andsim.NUM_BRUSHES
. For default brushes, the following constants can be used:-
sim.BRUSH_CIRCLE
: Circle brush -
sim.BRUSH_SQUARE
: Square brush -
sim.BRUSH_TRIANGLE
: Triangle brush -
sim.NUM_DEFAULTBRUSHES
: Number of default brushes, excluding custom brushes
-
interface.brushRadius
Gets or sets the radius of the brush
interface.brushRadius(w, h) w, h = interface.brushRadius()
-
w
: Brush width -
h
: Brush height
interface.perfectCircleBrush
Gets / Sets the "Perfect Circle" option
interface.perfectCircleBrush(flag) flag = interface.perfectCircleBrush()
-
flag
: boolean true / false on whether the setting is enabled or not
interface.mousePosition
Returns the current mouse position
mouseX, mouseY = interface.mousePosition()
-
mouseX
: mouse x position -
mouseY
: mouse y position
This is the position of the mouse in the full interface, so it ignores zoom window and can be outside of sim bounds. To convert into sim coords and adjust for zoom window, see sim.adjustCoords.
interface.console
Control or check whether the console is open
interface.console(shown) shown = interface.console()
-
shown
: boolean true/false on whether or not the console is shown.
If you set it to false while in the console, it will close. Scripts can also use it to open the console. This action is non-blocking, so script execution will continue. But as soon as control is returned to the engine, further Lua callbacks will stop (because no event handlers run while the console is open).
Constants
All SDL scancodes, keycodes, and button codes are exposed as constants. For a full list, check out LuaSDLKeys.h
Example
This code has examples of some of the features of the interface API, it shows a window with various components with some testing behavior.
-- Test Window
local testWindow = Window:new(-1, -1, 300, 200)
local currentY = 10
--Example label
local testLabel = Label:new(10, currentY, (select(1, testWindow:size())/2)-20, 16, "This is a test label")
--Example button
local buttonPresses = 1
currentY = currentY + 20
local testButton = Button:new(10, currentY, (select(1, testWindow:size())/2)-20, 16, "This is a test button")
testButton:enabled(false)
testButton:action(
function(sender)
sender:text("Pressed " .. buttonPresses .. " times")
buttonPresses = buttonPresses + 1
end
)
--Example Textbox
currentY = currentY + 20
local textboxInfo = Label:new(10+((select(1, testWindow:size())/2)-20), currentY, (select(1, testWindow:size())/2)-20, 16, "0 characters")
local testTextbox = Textbox:new(10, currentY, (select(1, testWindow:size())/2)-20, 16, "", "[place text here]")
testTextbox:onTextChanged(
function(sender)
textboxInfo:text(sender:text():len().." characters");
end
)
--Example Checkbox
currentY = currentY + 20
local testCheckbox = Checkbox:new(10, currentY, (select(1, testWindow:size())/2)-20, 16, "Unchecked");
testCheckbox:action(
function(sender, checked)
if(checked) then
sender:text("Checked")
else
sender:text("Unchecked")
end
testButton:enabled(checked);
end
)
--Example progress bar
currentY = currentY + 20
local testProgressBar = ProgressBar:new(10, currentY, (select(1, testWindow:size())/2)-20, 16, 0, "Slider: 0");
--Example slider
currentY = currentY + 20
local testSlider = Slider:new(10, currentY, (select(1, testWindow:size())/2)-20, 16, 10);
testSlider:onValueChanged(
function(sender, value)
testProgressBar:progress(value * 10)
testProgressBar:status("Slider: " .. value)
end
)
-- Close button
local closeButton = Button:new(10, select(2, testWindow:size())-26, 100, 16, "Close")
closeButton:action(function() interface.closeWindow(testWindow) end)
testWindow:onTryExit(function() interface.closeWindow(testWindow) end) -- Allow the default exit events
testWindow:onMouseMove(
function(x, y, dx, dy)
testLabel:text("Mouse: "..x..", "..y)
end
)
testWindow:addComponent(testLabel)
testWindow:addComponent(testButton)
testWindow:addComponent(testTextbox)
testWindow:addComponent(testCheckbox)
testWindow:addComponent(testProgressBar)
testWindow:addComponent(testSlider)
testWindow:addComponent(textboxInfo)
testWindow:addComponent(closeButton)
interface.showWindow(testWindow)