Feel free to post bug reports, suggestions, ideas and questions.
Note: I'm not working on this program anymore. It might or might not be incompatible with newer CC versions.
Motivation-o-meter:
A simple bar showing the possibility whether I'm working on this program or school's keeping me busy.
low………….high
| - - - - - - - - - - |
| - - - - - - - - - - |
Features:
- Create an unlimited amount of windows.
- Add objects to those windows (see "Supported Objects")!
- Easily change the design and attributes of each object.
- Switch between windows using only one function.
- Use an API to interact with the windows that have been created with the editor.
- Button
- Text
- Variable
- ProgressBar
- Input
- List
- CheckBox
- RadioButton
- Slider
- Panel
- ScrollView
Download & Changelog
v1.7.2: reMpHkBb
Older versions
Spoiler
v1.7.1: YE2WetaX- Bugfix: Graffiti crashes when you use it via a monitor and try to change certain attributes.
- Added CheckBox object.
- Added RadioButton object.
- Added Slider object.
- Added DropDownList object.
- Added converter.
- Added color theme "Fire".
- Added "load" attribute to some objects and removed the "getVariableValue" and "getProgressBarValue" functions.
- Improved editor GUI.
- Implemented "mouse_drag" support for objects and the editor.
- Changed the event handling of buttons: The user-function with the button's objID attribute (instead of the param attribute) as its name gets called now.
- Added a list editor and removed the old way of setting the elements of a List object.
- Renamed "elements" attribute of the List object to "Items" (Just trying out whether it looks better that way)
- Renamed most tables to have an uppercase first letter.
- Bugfix: Graffiti crashes after writing something into an Input-object.
- Bugfix: The move- and scale-pixels of a ProgressBar object have an incorrect position when it is facing up.
- Bugfix: The Input object isn't reading correctly when it is inside a container.
- Bugfix: The program crashes when you try to load a non-existent window.
- Bugfix: The ScrollView wan't scrollable in edit mode.
- Added "GraffitiData" folder which contains all data for Graffiti.
- Windows get saved separately now.
- Added Settings-file.
- Added multi-language support.
- Added "toggle" function type for buttons.
- Added ScrollView container (a container with a vertical and/or a horizontal scroll bar).
- Improved the save-file.
- Added container objects.
- Added color themes.
- Renamed almost everything from "screen" to "window".
- Renamed the "Slider" object to "ProgressBar".
- Renamed most "show" functions to "draw".
- Completely changed event handling and displaying of the objects.
- Removed AddOn support.
- Bugfix: Elements of List-objects couldn't be changed. (This time it does work!)
- Included the GraffitiAPI into the main program (i.e. the file doesn't exist anymore: use os.loadAPI("Graffiti") instead of "GraffitiAPI")
- Fixed a bug where input objects acted strange when the program got used as an API.
- Added advanced computer support.
- Removed the automatic adaption of objects to the screen size after making it more complicated and realizing that it didn't do what I wanted anymore.
- Added an API
- Added the attributes "horizontalAlignment" and "verticalAlignment" to the editor. They allow you to automatically move/scale certain objects when the screen-size changes. (More details under instructions/Editor/Design below)
- Bugfix: Lists with enabled "isMultiselect" attribute couldn't be changed.
- As usual, more bugfixes.
- Included AddOn support.
- Bugfix:
The elements of the list-object couldn't be changed.(Fixed in v1.3) - More bugfixes
- The direction of a slider can be changed in "Design"-mode now. (You have to click exactly on the same x- or y-coordinate)
- Improved the input-object. Now it shouldn't matter what kind of keyboard you use.
- Added a "set parent" button. It determines which window will be shown when the "Back"-button gets pressed.
- Bugfix: The "message"-attribute of the list wasn't shown when using the editor.
- Lots of annoying bugfixes.
- Added a screen-editor with (almost) full touchscreen support!
- Added a new object: List
- Improved the Input-object (Now it also supports upper-case letters! Wooho!)
- Probably much more but I forgot what it was.
- Bugfixes
- Initial release
Note: You can load Graffiti as an API since Version 1.5! The API file itself is outdated!
v1.1: http:JACwSMXk
- Same as the v1.4 update.
- initial release
Bugs and planned features
Progress
Next (planned) version: 1.8
- Lots of cleaning
- Bugfixes: 0%*
Screenshots
Spoiler
This is what the objects look like:[attachment=1413:2013-12-15_21.28.29.png]
Usage examples:
Ghosty: (more informations somewhere below)
[attachment=1295:2013-08-01_23.23.19.png]
Login window:
[attachment=1414:2013-12-15_21.05.51.png]
Here's how I've been using the program:
- Overview over the energy in the base.
- Security window (controls the important stuff in the base)
- Using AddOns: Sending rednet-messages and files to other computers.
Videos
Object overview
Spoiler
[media]http://www.youtube.com/watch?v=FebXgbxbhN8[/media]Editor tutorial/demonstration
Spoiler
[media]http://www.youtube.com/watch?v=urxOzGyaXTY[/media]Official AddOns and programs which use the API
Warning: I haven't tested these programs with newer versions of Graffiti. Make sure that you have the correct version before using one of them. (No, I don't know which version they need.)
Note to myself: This is obsolete… make some new stuff.Instructions
Partially moved/copied to GitHub.
Complete tutorial for Graffiti v1.7
Spoiler
Getting StartedFirstly you have to download and install Graffiti. I recommend you to always download the newest version which you can find under the "Changelog" section above or on pastebin and GitHub.
Use an advanced computer and download Graffiti. The easiest way to do this is to enable the http API and enter "pastebin get <replace with pastebin code> Graffiti".
Press enter and after a few seconds a message should appear telling you whether Graffiti has successfully been downloaded.
Now you have to run Graffiti with the parameter "edit".
Example: Graffiti edit (Of course you have to replace "Graffiti" with something else if you downloaded it with another name).
[attachment=1432:2014-01-11_17.12.20.png]
Press enter and now you should see the setup. It allows you to specify following settings:
- Language (right now only "en-US" is supportet)
- Color Theme
- Show/Hide data folder (the data folder will be in the root directory by default and contains all data for Graffiti including the languages, color themes, settings, logs and your projects)
Warning 2: Due to another but in version 1.7 the options "Show Data Folder" and "Hide Data Folder" are swapped. Choose "Hide Data Folder" if you want to show it. This will be fixed in the next version!
[attachment=1434:2014-01-12_22.44.08.png]
[attachment=1435:2014-01-12_22.44.20.png]
After finishing the setup you should see a blank screen with four buttons in the edges. Before you continue I would recommend you to close Graffiti and restart it with the parameter "edit" (just like you did before). That way the data folder will be created and the setup won't appear again. For now there is no better way of saving everything. I guess I'm just lazy…
The Window Manager
Exept for the setup, you will always see the window manager first when you start Graffiti in edit mode. As the name suggests you will be able to manage your windows. That includes…
- creating new windows,
- editing their content,
- renaming and deleting existing windows,
- setting a parent (choosing the window that should be shown after pressing the integrated "back" button) and
- setting the startup window (the window that will show up first when starting Graffiti normally).
Note that before creating or renaming windows you have to specify a name in the "Input" field above.
[attachment=1433:2014-01-11_17.49.16.png]
Create your first window by entering a name in the (depending on the color theme you are using) gray "Input" field above and press the "New" button. Now select the window in the list to the left and press the "Edit" button. This will bring you to the next editor:
The Window Editor
This is probably one of the two most important parts and therefore very long. If you don't like reading then I'm surprised that you even got that far and I would recomment you to try the "design" part out for yourself (it shouldn't be very difficult but I can't really tell because I created the program) and skip to the "coding" part.
Note: You can use Graffiti with the computer or with an adjacent monitor. If the latter then some features might not be available and some might work differently than on the computer itself! All of those differences will be explained below.
The editor allows you to do the following things:
- Create new objects,
- Move and (depending on the object) scale them.
- Remove objects.
- Edit the attributes of existing objects.
- Enable and disable the default buttons "Back" and "Refresh" by clicking on them.
- Go back to the window manager using the "Options" button.
Just click on an empty pixel. A list of objects should appear. Click on one of them to add them to the window.
Yep, that's it.
Moving and scaling objects
Click an object to select it. You will see a pixel in the upper left corner of this object which I call the "Move"-pixel. Depending on the object you might also see a second pixel in the lower right corner. I'm calling this one the "Scale"-pixel. It shouldn't be too hard to guess what they do. You can either…
- click one pixel and then click somewhere else to set the new position/size of the object, or
- drag a pixel.
"Options" List
This list allows you to access extra features. As for 1.7 they are called "Attributes" and "Delete". Again, they should be self explanatory.
If you are using Graffiti directly via a computer then you can simply right click an object to access this list.
Otherwise, if you are using it via a monitor then you need to click a selected object anywhere except the marked pixels (which means that you basically need to doubleclick a non-selected object).
Deleting objects
… should be obvious after reading how to open the "Options" list.
Attributes
After selecting "Attributes" in the "Options" list you will get to Graffiti's attribute editor. Again, as with the previous editors, the purpose of this one should be fairly obvious.
The attribute editor hasn't improved very much since version 1.0 but it should be easy to use nontherless. Just click an attribute to edit it.
First of all, there are different types of attributes:
- text
- After clicking it you can either directly enter a new value or, if you use Graffiti via a monitor, a yellow pixel will appear which indicates that you have to go to the computer and enter the new value.
- Usage example: the "object ID" attribute or the text of the "Text" object (creative, right?).
- number
- Just like the text but only numbers will be accepted.
- Usage example: Setting the maximum size for certain objects.
- list
- Goes through a list and selects the next item each time you click the attribute.
- Usage example: Changing what a button should do when it got clicked.
- bool
- Simply changes a value from true to false, or vice versa.
- Usage example: Set whether an "Input" object should show the input or treat it like a password. (I mean that it shows stars instead of text, if that makes more sense.)
- table
- Switches to the list editor which allows you to edit tables. The list editor itself should be self explanatory so I'm not going to explain it. You can, however write a post or contact me via PM if you do want me to write instructions for it.
- Usage example: Editing the items of a "List" or "DropDownList" object.
There are currently 4 default buttons:
- Quit (upper right, always shown)
- Back (upper left, toggleable)
- Refresh (lower right, toggleable)
- Options (lower left, only shown in editor mode)
The "Back" button switches to the parent window that has been set in the window manager. If no parent window is set then nothing will happen.
The "Refresh" button simply redraws the window, causing all objects to be updated.
Objects
Additionally to the description, I'm also writing what each object returns if the user interacts with it.
Note: While I'm trying to keep all lists as accurate as possible, the objects that are described here might not be up to date with newer versions than 1.7.
Button
The "Button" object does something when the user clicks it.
Attributes:
- Object ID
- Used to identify an object. In this case it's used to call functions or switch windows when it got clicked and it will be returned by Graffiti's "pullEvent" function if it has been loaded as an API.
- Text
- The text of the button. It is always centered and automatically trimmed if the length of the text is longer than the button's width.
- Function type
- Determines what should happen if the button gets clicked. Those function types are available:
- "function" calls the function in the "UserData" table which has the button's object ID as its name. Example: If the button's name is "Testbutton" and it got clicked then the function "UserData.Testbutton" will be called.
- "toggle function" is the same as "function" with the difference that the button will be toggled each time it got clicked. It will also return the new state of the button (again, more explanations in the "Code" section below).
- "switch" is the only type that accesses the "Window" attribute instead of the button's ID. If the button got clicked then the specified window will automatically be shown if it exists.
- Window
- The name of the window that should be switched to if a button with a "swich" function type gets clicked.
- "button_clicked" event
- its ID
Text
The "Text" object is the simplest one of all. It only displays text and therefore it has only one attribute:
- "text"
The "Variable" object is similar to the Text object with the only difference that its text gets assigned dynamically (i.e. a function which returns the text gets called each time the object gets drawn).
- Object ID
- It doesn't really have any use for this object but I didn't remove it in case that I needed it later. (Yes, that's an excuse for my lazyness.)
- Load Function
- This attribute exists for all objects that have a dynamic text or content. As I mentioned above the Variable object gets its text from a function. The "Load Funciton" attribute needs to be the name of the function and the function itself needs to be in the UserData table.
The "ProgressBar" object is similar to the Variable object but instead of text you can only specify a number between 0 and 100 and the ProgressBar will automatically display it in form of a beautiful, colored stripe. It has the same attributes as the Variable object:
- Object ID
- Load Function
- It may only return a number between 0 and 100 but it should at least not crash if the number is negative or bigger than 100.
The "Input" object is basically like the TextBox of Windows (the OS). You click it and then you can enter something.
- Object ID
- This attribute can be used to get the text that has been entered. The text will always be saved in the table "ObjectData.Input" with the Object ID as the key.
- Example: If the user enters "Hi there!" into the Input object with the ID "Testinput" then the table "Input" inside the table "ObjectData" will contain the key "Testinput" with the value "Hi there!".
- Message
- If the user runs Graffiti via a monitor then he has to go to the computer after touching the Input object. The message will be displayed there. (Which means that this attribute is useless in case that you only intend to use it via the computer itself.)
- Is password
- Determines whether the input should be replaced by stars. The text in the ObjectData.Input table isn't affected by this attribute.
"text_changed" event, objID, text
- "text_changed" event
- its ID
- the entered text
List
The "List" object contains a table of items and displays them in form of, you guessed it, a list. You can also select one or more items depending on the "Is multiselect" attribute.
- Object ID
- Used to identify a list after throwing an event because the item selection has changed.
- Items
- The list/table of items that should be displayed. It can be edited using the List Editor which can be accessed via the Attribute Editor.
- Is multiselect
- Determines whether only one or more items can be selected at once.
- "selection_changed" event,
- its ID,
- the key of the selected/deselected item,
- whether the selected item has been selected or deselected (true means selected and it will always be returned if "Is multiselect" is set to false)
CheckBox
The "CheckBox" object is very simple and probably everyone already knows what it does. You click it and the little X to the left gets toggled. You can access its state (whether it's checked or not) under "ObjectData.CheckBox.<CheckBox ID>".
- Object ID
- Text
- Checked by default
- Determines whether… you won't believe it: the CheckBox will be checked on startup.
- "checked" event,
- its ID
- whether it is checked now
RadioButton
The "RadioButton" object has similarities with the CheckBox. The main difference is that each RadioButton has a group. In this group only one RadioButton at a time can be checked. If the user clicks one of the RadioButtons then this one will be checked and all the other ones will automatically be unchecked. This object is useful if you want to have the simplicity of a CheckBox but more options than just "true" and "false".
- Object ID
- Used to save its state under "ObjectData.RadioButton.<ID>". Therefore you have to give each RadioButton a unique ID. I might implement a feature which gives each object a unique ID automatically.
- Text
- Group
- Only one RadioButton per group can be checked.
- Checked by default
- The RadioButton will be checked on startup if this attribute is set to true. If more than one RadioButton per group has this attribute set to true then the last one will be checked.
- "radio_changed" event,
- its ID
Slider
The "Slider" object is a simple bar with a cursor (I honestly don't have a better description) which points at a number between the minimum and maximum value of the Slider object.
- Object ID
- Minimum Value
- The smallest possible number which will be selected when the cursor is all the way to the left.
- Maximum Value
- The biggest possible number which will be selected when the cursor is all the way to the right. The length of the slider is the difference between the maximum and the minimum value and will automatically be adapted if one of the attributes get changed.
- "slider_changed" event (This one might change in a later version.)
- its ID
- its current value
DropDownList
The "DropDownList" object has similarities to the List object, referring to the attributes. The only difference is that, instead of displaying all objects, it only shows the selected object. When the user clicks it, the list expands and he can select one of the shown items.
- Object ID
- Items
- Load Function
- All attributes are equal to the List attributes.
- "selection_changed" event
- its ID
- the key of the selected item
Containers
Containers are a special form of the object. You can interact with them just like with any other object but you can also create objects inside of them. You should also be able to nest containers (place containers inside of other containers) up to 255 times. As for version 1.7 those are available:
Panel
The "Panel" object is the most simple container and the base for every other container. It simply is a box which can contain objects, automatically moves them if it gets moved and prevents them from appearing outside of its border. You can use it to limit the size of objects which change their size automatically, to emphasize one or more objects or simply for astethic purposes.
ScrollView
The "ScrollView" object can have 0 to 2 scrollbars which the user can use to view content which is outside of its borders. It's probably easier to understand than I can explain it. Everybody has already used scrollbars before.
Using them in the editor:
As I mentioned above, each ScrollView can have 0, 1 or 2 scrollbars. Enabled scrollbars will be shown normally. Disabled ones will be hightlighted (by default: lime). Simply click a scrollbar to en- or disable it.
To move its content, simply click on one of the arrows of a scrollbar (the ScrollView needs to be selected).
Man, I wrote much more than I expected… Have some fun experimenting with all the objects.
Now, lets get to the interesting part.
Writing a program with Graffiti
There are two ways of writing a program with Graffiti:
1) Directly editing Graffiti to add functions and variables
In my opinion this way of coding is becoming obsolete and I might remove it in a later version. For now it should still work and if you want to use this way then I would recommend using it for small and simple programs which only you are going to use.
2) Writing a program that uses Graffiti as an API
Yes, you can also load Graffiti as an API. Neat, right? It has the following advantages over the other mentioned way of writing programs:
- You keep a better overview over all functions and variables if you work on more than just one project (the current project can be changed in the settings file which you can find under "GraffitiData/Graffiti.cfg").
- It also makes it easier to share programs because you would have to give others only your program and they could download Graffiti by themselves without having to change anything.
- The API provides the function "pullEvent" which allows you to get any kind of event. Except for the Text and Variable objects as well as containers, most objects throw events when the user interacts with them (see "Objects" section above). By editing Graffiti itself you can only get "button_clicked" events.
- If you find a bug or have problems with your program then it's much easier for me to help you because you won't have to specify any changes or the code that is at a certain line number.
- Definitely more, but I can't think of any other ones right now.
Using Graffiti by editing it
Start by editing Graffiti (enter "edit Graffiti" or use an editor like Notepad++ or another one of those nice editors). Now you will have scroll a bit down past all those variables, settings and color themes until you reach the "UserData" section. I really recommend that you only edit the "UserData" table!
You can include variables and two types of functions into the table:
- Those which get called when a button gets clicked and
- those which return a value when an object gets loaded.
Example if the button's "Object ID" attribute is "test" or "toggleTest": (you can also find some in the program itself)
function UserData.test()
sleep(1) -- that way the button will be green for one second
end
-- or
UserData.toggleTest = function(state)
rs.setOutput("front", state)
end
Note: The argument "state" is only necessary if the button can be toggled (this can be changed by editing the "Function type" attribute) and it is used to determine whether the button has been toggled on or off. If the button can't be toggled then the state will always be "true".
Making a function which returns a value for an object when it's getting drawn:
As I mentioned in the "Objects" section, you can dynamically assign the values of certain objects like the Variable, ProgressBar, List or DropDownList objects by specifying their "Load function" attribute. Just like with the buttons, the function in the UserData table needs to have the same name as the one that was specified in the attribute.
Example if a ProgressBar object has the "Load function" attribute "getRandomNumber": (again, an example is in the program)
function UserData.getRandomNumber()
return math.random(100)
end
-- Another example for the "Variable" object
function UserData.getTime()
return textutils.formatTime(os.time(), true)
end
Using Graffiti as an API
There are three things that you need to know if you want to use Graffiti as an API:
- How to control your windows,
- how to acces information about objects and
- how to handle events.
os.loadAPI("Graffiti") -- "Graffiti" is the name of the program, you might need to specify a path.
Controlling the windows
Those functions are available:
- drawWindow(windowID)
- If you specify the windowID argument then this window will be drawn and set as the current window.
- If you don't specify this argument then the window will simply be redrawn.
- getCurrentWindow()
- Returns the ID of the current window.
- clearScreen()
- Has the same function as the "clear" program.
Most objects save temporary data in the "ObjectData" table (that's why it's useful to give every object its own ID).
Warning: The ObjectData might not contain any information about an object if the user didn't interact with it.
Example: All buttons that can be toggled save their state under "ObjectData.Button.<object ID>"
More examples:
- Input: The text that the user entered.
- List and DropDownList: A table containing the keys of the selected Items (careful: The table might not exist if the user didn't click it. Always check whether it's null before you try to access it!)
- CheckBox: A boolean value indicating whether the CheckBox is checked or not. (Again, it might be null. Last warning!)
- RadioButton: Contains the ID of the currently selected RadioButton. The key is the "group" attribute. Also make sure that each RadioButton has its own unique ID per group.
- Slider: An integer representing the currently selected value.
os.loadAPI("Graffiti")
local btnTestState = Graffiti.ObjectData.toggleTest or false
rs.setOutput("front", btnTestState) -- example
Note: You can, of course, access the UserData table and insert functions which can be used for the feature which I've explained in above ("Making a function which returns a value for an object when it's getting drawn")
local function getRandomNumber()
return math.random(100)
end
Graffiti.UserData.getRandomNumber = getRandomNumber
Handling Events
Getting events from Graffiti is very similar to getting events from the os.pullEvent(). The difference? Here:
Graffiti.pullEvent("requested event")
Simply replace "requested event" with a certain event (They are listed in the "Objects" section) or leave it empty to get every event.Note: Graffiti's pullEvent function will only return custom events and not the ones which os.pullEvent would return.
Note 2 (Important!): The "quit" event gets thrown when the user presses the quit button. It ignores the "requestedEvent" attribute, therefore it can always be thrown!
Here's an example about how to get input from any button. Read the "Objects" section if you want to know more about the different event types and the returned parameters.
os.loadAPI("Graffiti")
local finished = false
Graffiti.drawWindow("main") -- "main" is just an example, you can name it however you want.
while not finished do -- Loops until the user presses the quit button.
-- Waits until the user clicks any button or the quit button.
--(The "quit" event can always be returned even if you specify a requested event!)
local event, objID = Graffiti.pullEvent("button_clicked")
if (event == "quit") then -- If the user pressed the quit button, stop the loop.
finished = true
elseif (event == "button_clicked") then -- If he clicked a button, check the ID of the button to see which one has been clicked.
if (objID == "Testbutton") then
-- Code that does stuff when a button got pressed goes here.
elseif (objID == "anotherButton") then
-- Code that does stuff when another button got pressed goes here.
end
end
end
Graffiti.clearScreen()
os.unloadAPI("Graffiti")
Graffiti 1.7 paths and object accessing via code
Spoiler
Please note that I've originally written these instructions for pastebin due to the fact that the CC Forum was down when I tried to access it. Therefore the formatting might not be very good.How paths work:
First of all, remember the following:
- Each object is a table inside a window or other container, which are a tables too.
- Each object has its own ID, which is automatically generated upon its creation.
- Each object inside its container has a unique ID. That means that objects which are in different containers can have the same ID, but…
- Each object has its own unique path.
A path is a table and it can look like this:
{1}
(The 1 is the ID of the object.)or like this
{1, 1}
(The first number represents the ID of the object's container, the second number is, again, the ID of the object)… or like this
{1, 1, 1}
(In this case the object is inside a container inside another container inside the window.)Of course the number can be anything, depending on how many objects you have.
Example:
If you have a "ScrollView" container with the ID 2 which contains an empty panel with the ID 3 then an object that gets created in there would have the path {2, 3, 1}.
How do I find out the path of my object?
Just look inside the window-file.
But how does that help me access my objects?
I'm glad you asked, here's another answer which is way too long:
The ID of an object is also the key under which it has been saved inside its container.
That means that an object with the ID 2 can be accessed via "Container.Children[2]". ("Container" is just an example. Don't look for it inside Graffiti's code.)
The "Container" can either be a window or a container-object like the Panel or ScrollView objects. Therefore, if you nest containers then you can use the path to access an object through several containers via the appropriate key inside the "path"-table. I really hope that made sense. Otherwise here's another exapmle:
YAY! AN ACTUAL CODING EXAMPLE! (Also, in case of TL;DR)
We've got a window which contains:
- A button (ID: 1, path: {1})
- A Panel (container) (ID: 2, path: {2}) which contains
- another button (ID: 1, path: {2, 1})
local button1 = Windows.Children["yourWindowName"].Children[1]
orlocal button1_viaApi = Graffiti.Windows.Children["yourWindowName"].Children[1]
… if you use Graffiti as an API.If you want to access the second button then you need to acces it the following way:
local button2 = Windows.Children["yourWindowName"].Children[2].Children[1]
Now that you know how to acces objects, let us try to edit them via your code.
Example: Changing the Items of a List or DropDownList object.
os.loadAPI("Graffiti")
local list = Graffiti.Windows.Children["TestWindow"].Children[1]
local listNested = Graffiti.Windows.Children["TestWindow"].Children[2].Children[3]
list.Items = { "Item 1", "Item 2", "Item 4" }
Due to the fact that "list" is actually a reference, the Items will be changed.
You can, of course, also write the following if you only want to access it once:
Graffiti.Windows.Children["TestWindow"].Children[1].Items = { "Item 1", "Item 2", "Item 4" }
API (1.6+)
Spoiler
Most of it stayed the same so I'll just write the new stuff here and you can go to the "1.3 - 1.5" help if you need more information.1) You can refresh the window by calling "drawWindow()" without any parameters.
2) New function: Graffiti.pullEvent([optional] requestedEvent)
It's pretty much the same as "os.pullEvent()" with the only difference that it will only return custom events that get triggered by the user when interacting with any object. It returns the event type, the object ID and, depending on the event type, some extras.
I recommend you to use this function instead of "getInput()".
Here's the list of custom event types:
- "quit": The user clicked the quit-button. Returns no extra parameters. (Important note: You might not like it but this event can always get returned, even if you specify another event type)
- "button_clicked": The user clicked a button. Returns "true" (because the state won't change).
- "button_toggled": The user toggled the state of a button. Returns whether the button got toggled on or off.
- "text_changed": The user entered something into an Input-object. Returns the text that the user entered.
- "selection_changed": The user clicked an element of a list. Returns the text of the selected element.
os.loadAPI("Graffiti")
Graffiti.setVariableValue("newVariable", "couldBeAnything")
Graffiti.setSliderValue("testSlider", 87) -- percent.
local quit = false
while not quit do
local eventType, objectID, toggleState = Graffiti.pullEvent("button_toggled")
if (eventType == "quit") then
quit = true
else
print("Button clicked: " .. objectID)
print("Toggle state: " .. toggleState)
end
end
API (1.3 - 1.5)
Spoiler
Using the API is extremely easy. Create a GUI using the normal editor but you only need to follow the "Design" and "Attributes" part because the "Coding" part will be done in your own program.Here's an example of how you could use the code:
os.loadAPI("Graffiti")
Graffiti.setVariableValue("testVariable", "variable-value")
Graffiti.setSliderValue("testSlider", 87)
local monitorSide = "right"
local input = ""
local currentScreen = "mainScreen"
while input ~= "quit" do
input, screen = Graffiti.getInput(monitorSide)
currentScreen = (screen == nil) and currentScreen or screen
print("Selected list-item: " .. GraffitiAPI.selectedItems["testList"])
print("User input: " .. GraffitiAPI.userInputs["testInput"])
print("Pressed button: " .. input) -- "param"-value of the pressed button.
end
Basically, you've got these 4 functions:
- setVariableValue(varID, value): Determines which variable should display a certain value.
- setSliderValue(sliderID, value): Determines how much a certain slider is filled.
- showScreen(screen): Shows the window with the given name.
- getInput(monitorSide): Lets the user interact with everything and returns two variables: param and currentScreen; param is the param-attribute of the button that has been pressed and currentScreen is the window that you have to show if you make changes to the screen.
- userInputs
- selectedItems
AddOns (1.2+)
Spoiler
To use the AddOn, all you have to do is place it in the same directory as the program. You can start the editor and the AddOn-objects get displayed in the object-list.If you want to create a new AddOn then you can use this template: http://pastebin.com/94gAtudt
Here's an example.
How to make an AddOn
1) Define your objects
If you looked at the example then you should have seen the object-definition right at the beginning of the file. Basically you can put it wherever you want but I still recommend putting it at the top of your file.
Add an object by writing <object>. On the next lines you should add the parameters objectID and objectType (everything needs to be on a separate line!) by entering something like this:
objects = [[
<objects>
<object>
objectID=testID
objectType=Button
</object>
</objects>
]]
Depending on the objectType you will need some extra-attributes:
Button
- defaultWidth (optional, default: 15)
- defaultHeight (optional, default: 3)
- text
- none, just don't forget to set the objectID
- message
- elements (separate each element with a ";". The example "Hi;there!;How are you?;I'm fine." should be valid.
- isMultiselect
- canScale
- canClick
- defaultWidth (only if canScale or canClick is set to true)
- defaultHeight (only if canScale or canClick is set to true)
Before I start, you need to know that there is a useful table called systemInfo which the AddOn will always get when it gets called.
The systemInfo contains the following fields by default (some of them can be empty depending on the reason why the AddOn gets called):
- x: The x-coordinate of the object.
- y: The y-coordinate of the object.
- width: The width of the object (Empty if neither clickable nor scaleable).
- height: The height of the object (Empty if neither clickable nor scaleable).
- maxX: The horizontal size of the monitor.
- maxY: The vertical size of the monitor.
- selectedItems: The selected elements of all lists (more information in the editor-tutorial).
- userInputs: The values of all input-objects.
If you use my template then one of the two methods should be called automatically.
Show will be called when you need to display a variable or a custom object. All neccessary informations (like x and y coords) will be in the systemInfo.
Click will be called when a button or a custom object with enabled canClick-attribute gets clicked.
3) Save your new AddOn
The file-name has to end with ".add"
Yep, thats it.
Editor (1.1+)
Spoiler
There are 3 steps: Design, Attributes, Coding (not the best step-names but meh…)Note: You don't necessarily need to finish one step to continue with the next one. I'll just explain it like that to make it easier to understand.
Note 2: The "Design" and "Attribute" part are very easy to understand. However, I recommend you to look at the "Coding"-part since you need to follow some rules to make your program work.
Step 0: Start the program
Start it using the parameter "edit". If the name of your program is "Graffiti" then you should enter "Graffiti edit".
You should now see something like this:
– image removed to make space for the ones in the newer instructions –
Step 1: Design
This is probably the easiest part. Add objects to your main-screen, make a new one, add objects to the new one and so on.
First you need to select the "Design"-mode (just touch the text). It should now be orange and you can touch the "edit screens"-button.
Now you will see a list of all your screens as well as the Buttons "New", "Edit" and "Delete". I guess each function is self-explanatory.
The mainScreen-screen will be shown as soon as you start the program normally, therefore i would recommend you to select it and press the "edit"-button (if you haven't changed it yet).
To create an object you have to right-click the position where it should be. A list should pop up:
– image removed to make space for the ones in the newer instructions –
Touch the object that you want to add and it should be displayed. You can do that as often as you want as long as there is still space on your screen. Simple, right?
Delete: If you created an object which you don't like then you can press the "options"-button in the lower right corner, select "Delete" and press the "last screen" button. This will change your mode to delete-mode and any object that you touch will be removed.
Editing your objects is almost as easy as creating or deleting them. When you touch one in "Design" mode then you will see one or two pixels with another color (depends on the object). The one on the left is for moving your object. The one on the right is for scaling (works only with buttons and sliders). Simply click one of them (it should turn white) and press the pixel for the new position/scale.
1.3 feature: horizontalAlignment and verticalAlignment
Each attribute can either be left/top, right/bottom or stretch. Press the green/red pixel at the end of the gray lines of an object to change one of the attributes.
Left and top won't change the design when the screen size changes. Right and bottom will move the objects and "stretch" won't move the objects but it changes its scale.
Example for the horizontalAlignment attribute: Left pixel red and right pixel green: horizontalAlignment is "right". Both are green: "stretch". Otherwise it's "left".
Step 2: Attributes
You don't know what I'm talking about? Well, every object that you've added to your window has certain attributes (look at my v1.0 instructions to see them all). The button has the default-attributes "objType", "x" and "y" as well as some special ones: "width", "height", "text", "funcType" and "param". To edit an attribute which can't be changed in the "Design"-mode, you have to press the "options"-button, select "Attributes" and press the "last screen" button.
– image removed to make space for the ones in the newer instructions –
If you want to change an attribute all you have to do is click it. Depending of the attribute it will either automatically change to another value (e.g. "direction"-attribute of the slider from "right" to "up") or a yellow pixel will appear. When this is the case then you have to go to the computer and enter a value for the attribute.
Example:
If the attribute is "isMultiselect" (a list-attribute) then you can touch it and it will switch between true and false.
If the attribute is "param" (a button-attribute) then you have to enter the name of your function or screen (depending on the "funcType"-attribute)
Important: Don't change the "elements"-attribute of the list-object until you have read the instructions. It's a bit more complicated than I want it to be but I can't change it for now.
Step 3: Coding
If you've done everything right in the previous 2 steps then this shouldn't be hard for you.
Basically you can do these 4 things:
- Add values for your variables.
- Add values for your sliders.
- Create arrays for your lists.
- Create functions for your buttons.
When the screen finished loading then two functions get called: "getVariableValue(varID)" and "getSliderValue(sliderID)".
You can return a certain value depending on the object ID. This value will then get displayed on the screen. Note that the variable-value can be anything while the slider-value has to be a value between 0 (empty) and 100 (full).
Creating the function for a button is almost as easy as creating a normal one. Instead of "function myFunction() CODE end" you have to write "function userFunctions.myFunction() CODE end"
It's almost similar with the list. Instead of "myList = { }" you have to write "userLists.myList = { string1, string2 }".
Example:
function getVariableValue(variable)
if (variable == nil or variable.objType ~= "Variable") then
return
end
variableID = variable.varID
if (variableID == "testVariable") then
return "Variable";
elseif (variableID == "Time") then
return textutils.formatTime(os.time(), true)
end
return ""
end
local randomValue = 50
function getSliderValue(slider)
if (slider == nil or slider.objType ~= "Slider") then
return
end
sliderID = slider.sliderID
if (sliderID == "testSlider") then
return 87;
elseif (sliderID == "randomSlider") then
return randomValue
end
end
function userFunctions.setRandomValue()
randomValue = math.random(100)
end
userLists.testList = { "hi", "there" }
No editor (1.0)
(Not as simple as using the editor but if you insist on using the older version…)
Spoiler
1) Create your screen(s)Note: I don't know why I used the name "screen" instead of "window" in the code. I just did.
There is an array called "screens" inside the program (should be at line 91).
There are already two example-screens which should show you how to use it.
Spoiler
The screen array:
screens = {
mainScreen = {
};
}
And here's the example:
screens = {
mainScreen = {
};
subScreen = {
parentScreen="mainScreen";
};
superSubScreen = {
parentScreen="subScreen"
}
}
You just need to write "arrayName = { };" inside the screens-array. All your objects are going to go into this array.
If you are going to nest your screens (in this case: link from one screen to another) you can write "parentScreen=screenName;" inside your array. That way you will get redirected to the parentScreen when the "back"-button gets pressed. Otherwise you will always get back to the mainScreen-screen.
You always need the mainScreen! It's the one that gets shown when the program starts!
2) Add objects to your screen(s)
It get's a bit more complicated here but it's not hard to understand.
Each object has at least those 3 attributes:
Object type (objType)
x-coordinate (x)
y-coordinate (y)
There are the following 5 object types and their extra attributes:
Button
- width
- height
- text
- funcType
- param
"switch" changes the screen to the one that you've selected with the param-attribute.
The param-attribute has to be a string.
"function" calls the function that you've specified in the param-attribute.
The param has to be the actual function, not a string.
Here's an example:
Spoiler
screens = {
mainScreen = {
{ objType="Button", x=2, y=4, width=25, height=3, text="call function!", funcType="function", param=doSomething };
{ objType="Button", x=2, y=9, width=25, height=3, text="switch to subScreen", funcType="switch", param="subScreen" };
};
subScreen = {
parentScreen="mainScreen";
};
}
function doSomething()
randomValue = 5
end
Text
- text: The text that you want to show. Nothing else.
- varID
Example:
Spoiler
function getVariableValue(variable)
if (variable == nil or variable.objType ~= "Variable") then
return
end
variableID = variable.varID
if (variableID == "testVariable") then
return 42;
elseif (variableID == "Time") then
return textutils.formatTime(os.time(), true)
end
return ""
end
Slider
- length
- direction: either "left", "up", "right" or "down"
- sliderID
sliderID is like varID of the variable-object. The only difference is the name and the fact that the function "getSliderValue" gets called.
In the getSliderValue-function you have to return how much the slider is full in percent (a value between 0 and 100).
Example:
Spoiler
function getSliderValue(slider)
if (slider == nil or slider.objType ~= "Slider") then
return
end
sliderID = slider.sliderID
if (sliderID == "testSlider") then
return 87;
elseif (sliderID == "randomSlider") then
return randomValue
end
end
Input
- inputID
- message
- isPassword
Why is it the best?
It actually works. That makes me happy
Why is it the worst?
It's not user friendly.
It only supports lower case letters and numbers. That makes me sad.
I know, I know… you will probably say something like "Encreedem! Why can I only use lower-case letters and numbers!? If i use a password with 4 digits the number of possible combinations would get reduced from 2,1267647932558653966460912964486e+37 to only 4722366482869645213696!!!!1! How can you do this!?"
What? You didn't calculate the number of possible combinations before? Well, now you know it.
The reason is that it's currently not possible to determine if the user holds down the Shift-key. I could probably find a way around it but it will need some time.
Whatever… let's get back to the attribute-explanations:
As soon as the user touches the input-object, it gets yellow and he can go to the computer and enter something. The result will be saved in the "userInputs"-array with the inputID as the key.
"message" is the text that will be displayed before the user can enter something.
I don't know if this attribute is optional. I'ts up to you to find it out. (To make it simpler: if "print(nil)" throws an error then not including "message" will do the same).
isPassword is the attribute that makes the object non-user friendly. If it's set to true it will display a "*" instead of the actual letter or number (on the computer as well as on the monitor). The value inside the userInputs-array will still be the actual input of the user.
This attribute is optional and will be "false" by default.
Example:
Spoiler
screens = {
mainScreen = {
{ objType="Input", x=2, y=2, inputID="randomInput", message="Please enter something.", isPassword=false }
};
}
function doSomething
userInput = userInputs[randomInput]
end