^ Introduction |
Introduction | Installation | Included Actions | Tutorial Introduction |
^ Introduction |
First steps in ReaGirl |
^ Best Practices |
Best Practices On Accessibility | Best Practices on Writing guis as Blind Scripter |
^ Final Words |
Final Words |
Welcome to ReaGirl, an accessible Gui-library for Reaper.
Up until now, there are numerous gui-libraries available for Reaper who do a great job at allowing you to code your own guis.
The problem is: none of them allow making guis for blind people for various (mostly technical) reasons, so only people who can see without a problem can use them.
ReaGirl's intention is to solve this with a gui-library that makes it easy for you to code your own guis that are also useable by blind users.
And it does not only feature accessibility for blind users, but it has some additional benefits:
So if you want to make easy guis that are also useable by blind users of your scripts, ReaGirl is there for you and tries to manage everthing you need.
There's also an official support thread for ReaGirl in the Reaper-forum at:
https://forum.cockos.com/showthread.php?t=291220
So if you find bugs or need help in general, head over there and ask your questions.
But there's one question left now:
"How do I start?" you may ask?
Well, let's dive into ReaGirl in the next chapters.
Since ReaGirl is part of Ultraschall-API, the installation-instructions from Ultraschall-API are applicable here as well.
First make sure, you use the right versions of Reaper, SWS and JS-extension:
Reaper 7.03 and higher, SWS 2.10.0.1 and higher and Julian Sader's plugin 1.215 and higher.
You can download them at reaper.fm and sws-extension.org respectively.
Julian Sader's plugin can be installed using ReaPack or gotten from his own github-site
The screen reader compatbility needs at least Windows 10 or Mac OS.
To install the Ultraschall-API with ReaGirl, just follow the following steps:
Installation via ReaPack:
The easiest way is to install the Ultraschall-API and ReaGirl using ReaPack. ReaPack is a package-manager for extensions and helper-stuff for Reaper, which allows you to easily install and update content done by the Reaper community.
Many Scripts, Themes, JSFX-FX, etc are available through that.
Install ReaPack. Get it from https://reapack.com/. There's also a real good User guide for it available, that explains, how to do it.
Copy the downloaded dll(Windows), dylib(Mac) or so(Linux)-file into the UserPlugins-folder in the Resources-folder of Reaper.
You can find the correct Resources-path in the Reaper-menu "Options -> Show REAPER resource path in explorer/finder...".
(Re-)Start Reaper
Go into the Menu Extensions -> ReaPack -> Manage Repositories
Click on Import/export...-button and choose Import repositories
Paste into the dialog the following link
https://github.com/Ultraschall/ultraschall-lua-api-for-reaper/raw/master/ultraschall_api_index.xml
and hit OK.
Doubleclick on the Ultraschall-API-entry. A dialog will pop up with a description of the Ultraschall-API and ReaGirl
Hit the Install/update Ultraschall-API-button and select Install all packages in this repository
ReaPack will ask you, if you want to install new packages/updates the next time you synchronize ReaPack.
Hit Yes.
ReaPack installs the Ultraschall-API with ReaGirl. If no error appears, it will tell you to restart Reaper, as a new extension has been installed
Restart Reaper.
To update the Ultraschall-API with ReaGirl in the future, choose in the menu Extensions -> ReaPack -> Synchronize packages. If an update is available, it will install it automatically.
For more information on ReaPack and it's usage, refer User guide.
Manual Installation:
If you can't or don't want to install ReaPack, you can also install it manually.
ReaGirl provides a variety of actions that go along with it. Some are meant for development, some for the general users, some both.
Development:
Settings:
Scaling:
General user settings:
Developer settings:
Experimental Edit Mode:
You'll probably not need many of them, especially when developing new guis with ReaGirl and if you are not a screen reader-user, but it's probably good to know that these exists, if you need to provide support for users of your ReaGirl-gui-script.
In the next few chapters, I will show you a lot of the available concepts and features of ReaGirl through various tutorials.
I added ready to use code-snippets and I tried to add screenshots wherever possible.
If for some reason or another you might not be able to copy the code-snippets from the documentation, you can also
find them as lua-script-examples. Just run the action "ReaGirl_OpenFolder_ExampleScripts.lua" which will open the
examples folder of ReaGirl. In the folders named tutorial, you find the codes used in the tutorials as ready to use lua-files.
The last lua-file in each tutorial-folder is the finished tutorial-example, if you want to jump ahead.
The documentation is available with the actions "ReaGirl_Help_Introduction_Concepts_Tutorials_Documentation.lua" for the explanation chapters and "ReaGirl_Help_Functions_Documentation.lua" for the function's reference.
At the end of this document, you'll also find two chapters on best practices for accessible guis with ReaGirl.
I recomment you to read these to improve accessibility on your guis with a handful of rules of thumb.
Best Practices on Accessibility and Best Practices on ReaGirl-Guis for blind scripters.
I will update them from time to time so check them out occasionally for more practices from feedback by users of ReaGirl.
And now, without much further ado, let's head over to the tutorials.
When making a gui in ReaGirl, you basically have to work through 5 steps.
0) Initialize ReaGirl
1) Add some run-functions
2) Start a new gui using reagirl.Gui_New()
3) Add some ui-elements using the various _Add-functions
4) Open a gui using reagirl.Gui_Open()
5) Write and run a defer-function, in which you call reagirl.Gui_Manage()
Let's get into these steps in more details.
First add the line
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
to your script. It will load all functions and features of ReaGirl. After that, you can start writing your guis with it.
It would also be a good idea to check here, if the installed ReaGirl-version is the one your script is requiring.
For this you can use reagirl.GetVersion() and warn the user, if the installed version is too old.
Run-Functions are functions that are run, when the user interacts with a user-interface-element(ui-element). For instance, clicking a checkbox or a button.
Or typing/hitting enter into an inputbox. Or if the user moves the slider or selects a menu in a drop-down-menu.
Or if the user selects a context-menu-entry or drag n drops files onto a ui-element.
So in a run function you tell Reaper what to do, after the user interacted with a ui element.
A rule of thumb is: every ui-element and interaction-style gets it's own run-function.
So one for when the user hits the button. One for when the user toggles a checkbox. One for when the user types text into an inputbox and one for when the user hits enter into the inputbox, etc, etc.
These run-functions will get parameters passed from the gui, like the identifier of a ui-element(more on this later) or additional information like a selected menu-entry.
So run-functions are powerful and will make the magic happen, once the user interacted with a ui-element.
When writing a new gui, you can just write some "empty" functions that do nothing. We'll fill them in later.
More on run-functions in our tutorials.
This will tell ReaGirl, that you want to start a new gui. And it's simple, just add reagirl.Gui_New() to your script and you're done.
You can also use this later in more complex scripts to throw away an already existing gui and start a new one, but in most cases, you'll only do this once at the beginning of your gui-script.
Now the magic starts, we add our first ui-elements. You do it using the various _Add-functions like Button_Add, InputBox_Add, DropDownMenu_Add, Slider_Add, Checkbox_Add, Label_Add, Image_Add, etc.
All these add-functions will ask you for the position of the ui-element, a caption(which is usually shown next to it), an accessibility-hint which is shown as tooltip and also helps blind users to navigate and understand your gui, and a run-function.
Some may ask you for more things like width and height(Images) or initial values(Checkboxes, Sliders, DropDownMenu) or even additional run-functions(InputBox uses one run-function when the user types text and one for when the user hits enter in the inputbox).
All _Add-functions return a string called element_id. This element_id is like a unique name for your ui-element.
You can use this to do additional things with the ui-element. For instance, you can make the edges of a button more round or more edgier using the reagirl-Button_SetRadius()-function.
The function needs this element_id to know, of which button you want to edit the edges and alter it accordingly.
If you remember what I told you about run-functions, then you'll know already that element_ids are also passed to a run-function by ReaGirl.
So you can use one run-function for all buttons. You just check, whether the element_id passed to the run-function is the same as the element_id of button1, button2 or button3.
So keep these element_ids in variables or tables, as you can do really nice things with it(we'll dive deeper into element_ids in the tutorials).
You can add up to several thousand ui-elements in your gui(though usually, you will do much less).
This will open the gui. You can pass to it the position, the dimensions, the window-title, an accessibility-hint which tells the blind user what to expect from your gui and so on.
It's pretty straightforward, so just follow the documentation for what the parameters shall be.
This one's also very easy. Add the following lines at the end of your script:
function main()
-- let ReaGirl manage the gui
reagirl.Gui_Manage()
-- check, if the gui-window is still open and if not, stop the script
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
This will run the Gui_Manage-function, which basically does all the management of the gui, like drawing, click-management, runnning the run-functions when the user interacts with a ui-element, scrolling and other cool stuff, so you don't have to do it yourself.
And that's it, 5 steps to make your own gui.
Some general things to know about ReaGirl:
Don't bother with scrolling your ui-elements. This is done by ReaGirl. So if a ui-element is outside of the window, scrollbars appear and you can scroll around with them or by swiping or by keyboard(basically like you do in web-browsers).
Also don't bother about scaling like Retina/HiDPI or something. This is also done by ReaGirl automatically. So if the user chooses to run your script on Retina/HiDPI-displays or even with an altered scaling set in the preferences, your gui will change automatically.
Since one of the key-features in ReaGirl is accessibility for blind people, most of the stuff needed for screen reader-management is done by ReaGirl itself. You just need to set the parameters meaningOfUI_Element properly by giving a short and precise explanation, what the ui-element is doing and why. This will be shown as tooltip and also communicated to blind users via screen reader. This happens automatically, so you don't need to do much more.
There's a chapter dedicated on best practices for accessible guis later, if you want to improve on things further.
In the next chapters, I will take you through some basic tutorials on how making a gui is done in practice.
And we'll start with a basic one.
In this tutorial, I will show you how to make a basic gui with some buttons, inputboxes and checkboxes. I will also explain to you the basic concepts in practice that you need to know to make guis with ReaGirl.
Stick with me, it's easier than you think.
Let's start with a basic gui, which will show one single button.
-- Step 0: initialize ReaGirl
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- Step 1: write the run-functions
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
reaper.MB("Button has been pressed", "Pressed", 0)
end
-- Step 2: create new gui
reagirl.Gui_New()
-- Step 3: add the ui-elements to the gui
-- add an ok-button to the gui at x-position 30 and y-position 200 with the run-function Button_RunFunction
-- The meaningOfUI_Element-parameter "Apply changes and close dialog" will be shown as tooltip
-- and sent to a screen reader for blind people.
button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
-- Step 4: open the new gui with size 640x250 pixels, titled "The dialog".
-- In addition to the title, blind people will also get "This is a demo dialog with settings for tool xyz."
-- sent to their screen readers.
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 250)
-- Step 5: Write and run a defer-function, in which you call reagirl.Gui_Manage()
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
You see the 5 steps I mentioned in the first steps-chapter detailed out in this script.
This script adds a button and the run-function Button_RunFunction which is run when the user hits the button.
Also note the parameter meaningOfUI_Element, which is set to "Apply changes and close dialog". This parameter will be shown as a tooltip. It will also tell blind users, what the ui-element is supposed to do and why via screen reader. So if you add your own ui-elements, add a short and descriptive explanation into this parameter which says more than just "ok". Blind users will thank you for that.
And that's it, a simple gui with one button. Try it and experiment with it. Write a different text into the button. Alter the numbers in the parameters of reagirl.Button_Add to see what happens.
Don't worry about making mistakes. When you pass wrong parameters to ReaGirl-functions, they will tell you with an error message, what went wrong and where.
One thing you'll note in the code is that reagirl.Button_Add() returns the element_id as a returnvalue that I put into the variable button_ok_id. We will use this later on.
As a sidenote: ReaGirl stores in the background the position, size and dockstate of the window, so you can reopen it the next time with the old position, size and dockstate.
This will be done by the first two parameters of reagirl.Gui_New(). The first parameter is a name for your window that should be a unique one like "Mespotine_My gui Window", which will be used by ReaGirl as a settings name to store position, size and dockstate in the background.
The second parameter sets, if the window shall be reopened with the old position, size and dockstate. Set it to false to open always with the position you give in reagirl.Gui_Open() or set it to true to reopen it with the old position, size and dockstate.
Try it. Set the second parameter in reagirl.Gui_Open() to true and run the script. Move it, resize it and then close it. When you run the script again, the window reopens at the position where you closed it.
In the tutorials, we will set the second parameter to false so the examples always behave the same. But in your scripts, you can do what works best for you.
Ok, now that you fiddled around with the example, how about adding a second button?
For this, we add a second Button_Add-function for a Cancel-button. We'll also give it the same run-function like the first button. The returned element_id of the cancel-button we put into the variable button_cancel_id.
It's looks like the following.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add an ok-button and a cancel button to the gui
button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 250)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
You'll also notice, that I altered the run-function Button_RunFunction. I mentioned in the first steps chapter, that the run-function gets passed over several parameters, with the first one being the element_id of the button that got pressed.
In our example, the function Button_RunFunction has a parameter pressed_button_id into which ReaGirl will tell you, which button that uses this run-function got pressed.
You remember, that both added buttons share the same run-function.
And that's the reason, why the variables button_ok_id and button_cancel_id come in handy, because you can use them now to check, if pressed_button_id==button_ok_id(the ok-button got pressed) or elseif pressed_button_id==button_cancel_id(the cancel button got pressed).
In this code, we will show a message-box that tells you, which one has been pressed.
This way, all buttons can share the same run-function, as you can compare the element_ids of each button with the parameter pressed_button_id.
As a little excercise, add another button called "Help". Put its return-value into the variable button_help_id and add to the run-function a help-message shown, when the help button is pressed.
It'll help you internalising how the mechanics work and will help you further down the road with more complex guis.
Now that we have two buttons, we could add another ui-element. This time it's checkboxes.
You know the drill: Checkbox_Add adds checkboxes into the next script. The Checkbox_Add-functions return the element_ids which will be put into the variables checkbox_remember and checkbox_mysetting.
We also add a run-function for it called Checkbox_RunFunction, which will pop up a dialog when one of the checkboxes is checked. It'll tell you which one it will be with the help of the element_ids, just like we did with the buttons.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add two checkboxes to the gui
checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 250)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
One thing that's different from before is, that the run-function for the checkboxes Checkbox_RunFunction gets now two parameters, whereas the one for the buttons only got one.
The parameters are:
So a run-function for checkboxes gets the element_id and the state of the checkbox after it was clicked. That way you know, how the checkbox-state currently is.
You can then use it for things like storing settings in the background: when the checkbox is checked you store "true" and when the checkbox is unchecked you store "false".
And since you can check against the individual element_ids of the checkboxes, you know, which one got clicked and therefore must be stored.
The different parameters of the run-functions is also the reason, why you cannot use the same run-function for all types of ui-elements, as different ui-elements pass over different parameters to the run-function.
This could confuse your code. It's possible to code around this but I highly recommend to only use one run-function for all buttons and one for all checkboxes and one for all sliders, etc.
Although you can do one run-function for each ui-element, which is perfectly fine.
Back to our example.
We have now two checkboxes and two buttons. Let's add another type of ui-elements: InputBoxes.
InputBoxes allow to type in text into your gui. They are added basically like all other ui-elements: InputBox_Add and two run-functions.
Two run-functions?
Yes, InputBoxes allow to have two run-functions. One is called everytime the user enters a character into the inputbox while the other one is called when the user hits enter into the inputbox.
Let's add two inputboxes with the two run-functions(InputBox_RunFunction_Type for when the user types text, InputBox_RunFunction_Enter when the user hits enter into the inputbox).
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"My Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add inputboxes to type in text
inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 250)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
If you run this script, you can click into the inputbox and enter text. The entered text will be shown in the debugging-window "ReaScript Console" of Reaper.
This outputting is done by the run-function InputBox_RunFunction_Type. When you hit enter, a message-box appears which shows you the entered text. This is done by the run-function InputBox_RunFunction_Enter.
Both run-functions get as parameters the element_id(inputbox_id) as well as the entered text(entered_text).
Ok, now we have two buttons, two checkboxes and two inputboxes, all doing stuff when clicking/typing.
I would like to introduce you to another ui-element, the label element. This allows you to show some text.
It's working the same: Label_Add adds a label. This could potentially be made clickable which would mean, we need a run-function as well, but right now we just make it a regular label that just shows text.
Therefore the last parameter for the run-function is nil(which means, no run-function available for this ui-element).
I would also like to show you something else that you can do with the element_id: setting additional attributes for a ui-element.
In this case, we want to make the font-size of the label bigger and set it's style to underlined.
For this, we use the functions Label_SetFontSize and Label_SetStyle.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add a textlabel to the top of the gui
label_header=reagirl.Label_Add(30, 50, "This is a settings dialog", "Set the settings, as you wish.", false, nil)
reagirl.Label_SetFontSize(label_header, 40) -- set the font-size of the label to 40
reagirl.Label_SetStyle(label_header, 6, 0, 0) -- set the label to underlined
-- add inputboxes to type in text
inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 250)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Running this code will add the text "This is a settings dialog" to the gui in font-size 40 and underlined.
You'll notice that Label_SetFontSize and Label_SetStyle are using the element_id stored in the variable label_header to adress, which label shall be altered.
Another use-case for element_ids.
This is how you can actually change other ui-elements too. For buttons for instance, just look up all functionnames starting with reagirl.Button_ in the functions list to find lots of functions to alter buttons. They'll expect the element_id of the button that you want to alter.
The same goes for reagirl.InputBox_, reagirl.Slider_, reagirl.DropDownMenu_\, reagirl.Label_, reagirl.Checkbox_, reagirl.Tabs_, etc.
Speaking of altering buttons: let's make the OK-button more round and the Cancel-button more edgier.
For this, we use the function reagirl.Button_SetRadius in the following code, which allows us to set the radius of the edges.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add a textlabel to the top of the gui
label_header=reagirl.Label_Add(30, 50, "This is a settings dialog", "Set the settings, as you wish.", false, nil)
reagirl.Label_SetFontSize(label_header, 40) -- set the font-size of the label to 40
reagirl.Label_SetStyle(label_header, 6, 0, 0) -- set the label to underlined
-- add inputboxes to type in text
inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
reagirl.Button_SetRadius(button_ok_id, 20) -- make the ok-button rounder
reagirl.Button_SetRadius(button_cancel_id, 0) -- make the cancel button square
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 250)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
You see, it's working the same: you give the reagirl.Button_SetRadius-function the element_id of the button that you want to alter.
What's now bothering me, is the black background, which looks a little bleak in my eyes, so let's change it using reagirl.Background_GetSetColor.
This function accepts four parameters, with the first one setting if you want to set the background or get its colors and the other three the red, green and blue-color values from 0-255.
I'll choose to set it(true) to a grey tone(55).
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add a textlabel to the top of the gui
label_header=reagirl.Label_Add(30, 50, "This is a settings dialog", "Set the settings, as you wish.", false, nil)
reagirl.Label_SetFontSize(label_header, 40) -- set the font-size of the label to 40
reagirl.Label_SetStyle(label_header, 6, 0, 0) -- set the label to underlined
-- add inputboxes to type in text
inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
reagirl.Button_SetRadius(button_ok_id, 20) -- make the ok-button rounder
reagirl.Button_SetRadius(button_cancel_id, 0) -- make the cancel button square
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 250)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Ahh, that's better. Of course, you can choose a different color. Just experiment with it.
And while we are at making things prettier, let's add one last ui-element, an image.
Reaper deploys several images for track-icons so let's choose one of them to display here, one of a bass-guitar.
This time, though, I would like to anchor the image to the right and bottom side of the window. ReaGirl supports anchoring ui-elements to the right-side/bottom of the window.
To achieve this, just make the x/y-coordinate negative. In the case of our image, I choose to make the image 150 pixels from the right side of the window(-150) and 120 pixels from the bottom(-120).
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add a textlabel to the top of the gui
label_header=reagirl.Label_Add(30, 50, "This is a settings dialog", "Set the settings, as you wish.", false, nil)
reagirl.Label_SetFontSize(label_header, 40) -- set the font-size of the label to 40
reagirl.Label_SetStyle(label_header, 6, 0, 0) -- set the label to underlined
-- add inputboxes to type in text
inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
reagirl.Button_SetRadius(button_ok_id, 20) -- make the ok-button rounder
reagirl.Button_SetRadius(button_cancel_id, 0) -- make the cancel button square
-- add an image of a bass guitar
reagirl.Image_Add(-150, -120, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "A Bass guitar", "An image of a bass guitar.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 250)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Hmmm, nice bass guitar, Davie. And when you resize the window, you'll notice, that it'll stay linked to the right and bottom side of the window.
You can also link other ui-elements that way or even just link width and height of a ui-element to it(if a ui-element has width and height). Works the same: make the coordinate negative.
Let's link the width of the first inputbox to the right-side of the window(-50) to see how that looks.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add a textlabel to the top of the gui
label_header=reagirl.Label_Add(30, 50, "This is a settings dialog", "Set the settings, as you wish.", false, nil)
reagirl.Label_SetFontSize(label_header, 40) -- set the font-size of the label to 40
reagirl.Label_SetStyle(label_header, 6, 0, 0) -- set the label to underlined
-- add inputboxes to type in text
inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, -50, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
reagirl.Button_SetRadius(button_ok_id, 20) -- make the ok-button rounder
reagirl.Button_SetRadius(button_cancel_id, 0) -- make the cancel button square
-- add an image of a bass guitar
reagirl.Image_Add(-150, -120, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "A Bass guitar", "An image of a bass guitar.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 250)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Looking good. This way, the user can choose, how long the visible inputbox shall be.
Images support this as well, so you can experiment by making x and y linked to the left/top of the window(positive value) and link width and height to the right/bottom of the window(negative value).
That way, the size of the image changes the way the window is resized. You can also use reagirl.Image_KeepAspectRatio() to keep the image from looking stretched(more on this in tutorial #3: "a basic image viewer with a drag'n'drop-zone and a context menu").
When resizing the window to make it smaller, you probably noticed already, that the image is moving behind the inputboxes at some point. It would be nice to have a way to prevent that.
There's a way. The functions reagirl.Window_ForceSize_Minimum() and reagirl.Window_ForceSize_Maximum() set a minimum and maximum window-size. So the user cannot resize it bigger or smaller than that.
Let's add them into our gui and set some reasonable boundaries.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add a textlabel to the top of the gui
label_header=reagirl.Label_Add(30, 50, "This is a settings dialog", "Set the settings, as you wish.", false, nil)
reagirl.Label_SetFontSize(label_header, 40) -- set the font-size of the label to 40
reagirl.Label_SetStyle(label_header, 6, 0, 0) -- set the label to underlined
-- add inputboxes to type in text
inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, -50, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
reagirl.Button_SetRadius(button_ok_id, 20) -- make the ok-button rounder
reagirl.Button_SetRadius(button_cancel_id, 0) -- make the cancel button square
-- add an image of a bass guitar
reagirl.Image_Add(-150, -120, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "A Bass guitar", "An image of a bass guitar.", nil)
-- let's force window-sizes
reagirl.Window_ForceSize_Minimum(550, 250) -- set the minimum size of the window
reagirl.Window_ForceSize_Maximum(800, 400) -- set the maximum size of the window
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 250)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When you try to resize the window now, it will remain within the chosen constraints, not bigger and not smaller, only inbetweens.
And so we're done. Our first basic gui with a lot of ui-elements and features applied.
Toy around with it to get a deeper grip on how the basic concepts work.
And when you are ready, you can move on to tutorial #2, which shows you, how to add tabs to this gui.
In the first tutorial, we made a basic gui with some ui-elements. In this tutorial we want to add tabs and put the ui-elements into the individual tabs.
The first tab gets all inputboxes, the second tab all checkboxes and the third tab all buttons.
I removed the label and the image from the gui in this tutorial to make it a little simpler.
Our initial gui looks this way, now:
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add inputboxes to type in text
inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
-- let's force window-sizes
reagirl.Window_ForceSize_Minimum(550, 200) -- set the minimum size of the window
reagirl.Window_ForceSize_Maximum(1150, 400) -- set the maximum size of the window
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with some options.", 640, 250)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
The first thing we will do is to create three tables, in which we will store the element_ids of the gui-elements.
Each table represents a tab, means: tab1 contains all element_ids of the ui-elements of tab 1, tab2 all element_ids of the ui-elements of tab 2 and tab3 all of tab 3.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add tables that will contain the element-ids of the ui-element
tab1={} -- for the ui-elements in tab 1
tab2={} -- for the ui-elements in tab 2
tab3={} -- for the ui-elements in tab 3
-- add inputboxes to type in text
tab1.inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
tab1.inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
tab2.checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
tab2.checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
tab3.button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
tab3.button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
-- let's force window-sizes
reagirl.Window_ForceSize_Minimum(550, 200) -- set the minimum size of the window
reagirl.Window_ForceSize_Maximum(1150, 400) -- set the maximum size of the window
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with some options.", 640, 250)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
You'll notice that we added three tables, tab1, tab2 and tab3. You'll also notice, that we put the element_ids returned by the _Add-functions into these tabs, for instance :
inputbox_name_of_setting = reagirl.InputBox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
became
tab1.inputbox_name_of_setting = reagirl.InputBox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
means, we added tab1. in front of the variable-name, so it's now associated with the table that stores the gui-elements of the first tab.
All Inputbox_Add add to tab1, all Checkbox_Add add to tab2 and all Button_Add add to tab3. That way we have one table for each tab with all ui-elements that shall be in that tab.
Now, let's add the tabs-ui-element to the gui using the function reagirl.Tabs_Add(). We also add a run-function for tabs called Tab_RunFunction.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
function Tab_RunFunction(tab_id, tab_selected, tab_name_selected)
-- this function is run, when tabs are switched
reaper.MB("Tab #"..tab_selected.." with name "..tab_name_selected.." is selected.", "Tabs", 0)
end
-- create new gui
reagirl.Gui_New()
-- add tables that will contain the element-ids of the ui-element
tab1={} -- for the ui-elements in tab 1
tab2={} -- for the ui-elements in tab 2
tab3={} -- for the ui-elements in tab 3
-- let's add tabs
reagirl.Tabs_Add(10, 10, 620, 187, "Tabs", "Different options in this dialog.", {"Inputboxes", "Checkboxes", "Buttons"}, 1, Tab_RunFunction)
-- add inputboxes to type in text
tab1.inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
tab1.inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
tab2.checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
tab2.checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
tab3.button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
tab3.button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
-- let's force window-sizes
reagirl.Window_ForceSize_Minimum(550, 200) -- set the minimum size of the window
reagirl.Window_ForceSize_Maximum(1150, 400) -- set the maximum size of the window
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with some options.", 640, 250)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
If you run this example, you'll have tabs. Click on them and the run-function will show a dialog with the selected tab number and its name.
Tabs can have a background drawn, which I set here to 620x187 pixels. You can alter them or make them linked to the right/bottom of the window by making them negative.
You can also pass nil instead of numbers. This will make the background enclose all ui-elements automatically. If you don't want a background, pass 0 as background-width and height.
Toy around with it to get an idea.
This dialog still shows all ui-elements in all tabs, so let's alter the tabs-run-function to show only all ui-elements associated with a certain tab.
To achieve this, we use the function reagirl.Tabs_SetUIElementsForTab(), which allows us to link element_ids stored in a table with a certain tab.
Now, tab1, tab2 and tab3 come in handy. We associated the ui-elements of tab1 with the first table(1), the one of tab2 with the second(2) and the one from tab3 with the third(3).
Here's how it looks like with reagirl.Tabs_SetUIElementsForTab()
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
function Tab_RunFunction(tab_id, tab_selected, tab_name_selected)
-- this function is run, when tabs are switched
end
-- create new gui
reagirl.Gui_New()
-- add tables that will contain the element-ids of the ui-element
tab1={} -- for the ui-elements in tab 1
tab2={} -- for the ui-elements in tab 2
tab3={} -- for the ui-elements in tab 3
-- let's add tabs
tabs_id = reagirl.Tabs_Add(10, 10, 620, 187, "Tabs", "Different options in this dialog.", {"Inputboxes", "Checkboxes", "Buttons"}, 1, Tab_RunFunction)
-- add inputboxes to type in text
tab1.inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
tab1.inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
tab2.checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
tab2.checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
tab3.button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
tab3.button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
-- let's force window-sizes
reagirl.Window_ForceSize_Minimum(550, 200) -- set the minimum size of the window
reagirl.Window_ForceSize_Maximum(1150, 400) -- set the maximum size of the window
-- set ui-elements to the tabs.
-- Give tab 1 the ui-elements stored in tab1, give tab 2 the ui-elements stored in tab2
-- and give tab 3 the ones stored in tab3.
reagirl.Tabs_SetUIElementsForTab(tabs_id, 1, tab1)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 2, tab2)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 3, tab3)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with some options.", 640, 250)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Now we're talking! If we switch the tabs, now, it will show all correct ui-elements: tab 1 all inputboxes, tab 2 all checkboxes and tab 3 all buttons.
We're almost done.
You remember from the first tutorial, that interacting with the user elements was showing dialogs. For instance, clicking the OK-button shows a dialog saying "OK button is pressed".
But when you click it now, it does nothing.
This is because our run-functions still use the old variables that used to store the element_ids but they are stored in the tables tab1, tab2 and tab3, now.
So let's alter the run-functions to check not for i.e. pressed_button_id==button_ok_id but pressed_button_id==tab3.button_ok_id and so on.
Here's how the run-functions look like, when they check against the table-entries of the ui-elements:
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==tab3.button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==tab3.button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==tab2.checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==tab2.checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==tab1.inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==tab1.inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==tab1.inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==tab1.inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
function Tab_RunFunction(tab_id, tab_selected, tab_name_selected)
-- this function is run, when tabs are switched
end
-- create new gui
reagirl.Gui_New()
-- add tables that will contain the element-ids of the ui-element
tab1={} -- for the ui-elements in tab 1
tab2={} -- for the ui-elements in tab 2
tab3={} -- for the ui-elements in tab 3
-- let's add tabs
tabs_id = reagirl.Tabs_Add(10, 10, 620, 187, "Tabs", "Different options in this dialog.", {"Inputboxes", "Checkboxes", "Buttons"}, 1, Tab_RunFunction)
-- add inputboxes to type in text
tab1.inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
tab1.inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
tab2.checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
tab2.checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
tab3.button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
tab3.button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
-- let's force window-sizes
reagirl.Window_ForceSize_Minimum(550, 200) -- set the minimum size of the window
reagirl.Window_ForceSize_Maximum(1150, 400) -- set the maximum size of the window
-- set ui-elements to the tabs.
-- Give tab 1 the ui-elements stored in tab1, give tab 2 the ui-elements stored in tab2
-- and give tab 3 the ones stored in tab3.
reagirl.Tabs_SetUIElementsForTab(tabs_id, 1, tab1)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 2, tab2)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 3, tab3)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with some options.", 640, 250)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Now the code works exactly as intended. Tab 1 shows all ui-elements of tab1, tab 2 shows all ui-elements of tab2, tab 3 shows all ui-elements of tab3.
Plus: interacting with all ui-elements works properly now.
There's one final thing: what if I want to have buttons shown all the time, like apply and close-buttons or additional checkboxes?
It's simple: add them BUT don't add their ids to tab1, tab2 or tab3:
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction(pressed_button_id)
-- this function is run, when a button is pressed
if pressed_button_id==tab3.button_ok_id then
reaper.MB("OK Button is pressed", "OK Button", 0)
elseif pressed_button_id==tab3.button_cancel_id then
reaper.MB("Cancel Button is pressed", "Cancel Button", 0)
elseif pressed_button_id==apply_button then
reaper.MB("Apply Button is pressed", "Apply Button", 0)
elseif pressed_button_id==close_button then
reaper.MB("Close Button is pressed", "Close Button", 0)
end
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==tab2.checkbox_remember then
reaper.MB("Checkbox \"Remember\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==tab2.checkbox_mysetting then
reaper.MB("Checkbox \"my Setting\" is "..tostring(checkstate), "Checkbox-State changed", 0)
elseif checked_checkbox_id==global_checkbox then
reaper.MB("Checkbox \"Apply only to project\" is "..tostring(checkstate), "Checkbox-State changed", 0)
end
end
function InputBox_RunFunction_Type(inputbox_id, entered_text)
-- this function is run, when the user types in text into an inputbox
reaper.ClearConsole()
if inputbox_id==tab1.inputbox_name_of_setting then
reaper.ShowConsoleMsg("NAME: "..entered_text)
elseif inputbox_id==tab1.inputbox_description_of_setting then
reaper.ShowConsoleMsg("DESCRIPTION: "..entered_text)
end
end
function InputBox_RunFunction_Enter(inputbox_id, entered_text)
-- this function is run, when the user hits enter into an inputbox
if inputbox_id==tab1.inputbox_name_of_setting then
reaper.MB(entered_text, "The typed text into NAME was", 0)
elseif inputbox_id==tab1.inputbox_description_of_setting then
reaper.MB(entered_text, "The typed text into DESCRIPTION was", 0)
end
end
function Tab_RunFunction(tab_id, tab_selected, tab_name_selected)
-- this function is run, when tabs are switched
end
-- create new gui
reagirl.Gui_New()
-- add tables that will contain the element-ids of the ui-element
tab1={} -- for the ui-elements in tab 1
tab2={} -- for the ui-elements in tab 2
tab3={} -- for the ui-elements in tab 3
-- let's add tabs
tabs_id = reagirl.Tabs_Add(10, 10, 620, 187, "Tabs", "Different options in this dialog.", {"Inputboxes", "Checkboxes", "Buttons"}, 1, Tab_RunFunction)
-- add inputboxes to type in text
tab1.inputbox_name_of_setting = reagirl.Inputbox_Add(30, 105, 300, "Name of the setting:", 150, "Type in here the name of the setting.", "No title", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
tab1.inputbox_description_of_setting = reagirl.Inputbox_Add(30, 130, 300, "Description of the setting:", 150, "Type in here a description of the setting.", "No Description", InputBox_RunFunction_Enter, InputBox_RunFunction_Type)
-- add two checkboxes to the gui
tab2.checkbox_mysetting = reagirl.Checkbox_Add(30, 150, "My setting", "How shall my setting be set?", true, Checkbox_RunFunction)
tab2.checkbox_remember = reagirl.Checkbox_Add(30, 170, "Remember chosen setting", "Shall this setting be used as future default?", true, Checkbox_RunFunction)
-- add an ok-button and a cancel button to the gui
tab3.button_ok_id = reagirl.Button_Add(30, 200, 0, 0, "OK", "Apply changes and close dialog.", Button_RunFunction)
tab3.button_cancel_id = reagirl.Button_Add(70, 200, 0, 0, "Cancel", "Discard changes and close dialog.", Button_RunFunction)
-- add a "global" checkbox, as well as an apply-button and close-button to the gui
global_checkbox = reagirl.Checkbox_Add(10, 225, "Apply only to project", "Shall these settings be applied only to a project?", true, Checkbox_RunFunction)
apply_button = reagirl.Button_Add(532, 225, 0, 0, "Apply", "Apply changes.", Button_RunFunction)
close_button = reagirl.Button_Add(582, 225, 0, 0, "Close", "Close dialog.", Button_RunFunction)
-- let's force window-sizes
reagirl.Window_ForceSize_Minimum(550, 200) -- set the minimum size of the window
reagirl.Window_ForceSize_Maximum(1150, 400) -- set the maximum size of the window
-- set ui-elements to the tabs.
-- Give tab 1 the ui-elements stored in tab1, give tab 2 the ui-elements stored in tab2
-- and give tab 3 the ones stored in tab3.
reagirl.Tabs_SetUIElementsForTab(tabs_id, 1, tab1)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 2, tab2)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 3, tab3)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with some options.", 640, 250)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When you run this script, a checkbox and two buttons are added to the bottom of the window. And you can switch tabs, they will always be visible.
And all we did was adding a checkbox and two more buttons, whose ids are stored into apply_button, close_button and global_checkbox and not in tab1 or tab2 or tab3.
We also altered the run-functions Button_RunFunction and Checkbox_RunFunction so clicking these ui-elements will also show a dialog.
If you want, you can place the ui-elements properly into the upper part of the each tab as an excercise.
Now you know everything you need to make guis with tabs in ReaGirl.
In this tutorial, I will show you a basic image viewer. I will also show you, how to add a drag'n'drop-zone to this image as well as a context menu.
And as a cherry on the top, we will make clicking the image setting the loaded image as track icon for the first track in the current project.
So let's get it on.
Let's start with basic code, which will show one of Reaper's images, a bass guitar, in a window.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_RunFunction(clicked_image_id)
-- this function is run, when the image is clicked
end
-- create new gui
reagirl.Gui_New()
-- add the image of a bass guitar to this gui
image_id = reagirl.Image_Add(10, 10, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "An image", "A user selectable image.", Image_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "Image Viewer", "This is a demo image viewer.", 120, 120)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When you run it, it will show the image of a bass guitar.
Now, we will try to adapt the code, so you can drag n drop an image onto it to change it. To achieve this, we need a drag n drop area for this image.
In ReaGirl, you can add to each ui-element a dropzone. This will have the dimensions and position of the ui-element.
This dropzone gets it's own run-function that is run every time a file is dropped onto the dropzone.
So, let's add a dropzone for our image, using the function reagirl.UI_Element_GetSet_DropZoneFunction(). We also add a run-function for this drop-zone, Image_DropZone_RunFunction.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_RunFunction(clicked_image_id)
-- this function is run, when the image is clicked
end
function Image_DropZone_RunFunction(element_id, dropped_filenames_table)
-- this function will be called everytime a file is dropped onto the image
reaper.MB(dropped_filenames_table[1], "First dropped file", 0)
end
-- create new gui
reagirl.Gui_New()
-- add the image of a bass guitar to this gui
image_id = reagirl.Image_Add(10, 10, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "An image", "A user selectable image.", Image_RunFunction)
-- add a dropzone for dropped files for this image
reagirl.UI_Element_GetSet_DropZoneFunction(image_id, true, Image_DropZone_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "Image Viewer", "This is a demo image viewer.", 120, 120)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
You'll notice, it's only one functioncall to add the dropzone plus a run-function. That's all you need.
Now you can drop files onto the image. A dialog will pop up that tells you the filename of the first file dropped. It's only the first one in this code, but you could drop multiple files at the same time into it.
What we would like to do next, is to change the shown image. For this, we alter the function Image_DropZone_RunFunction accordingly.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_RunFunction(clicked_image_id)
-- this function is run, when the image is clicked
end
function Image_DropZone_RunFunction(element_id, dropped_filenames_table)
-- this function will be called everytime a file is dropped onto the image
-- load the first file dropped as new image and show it
reagirl.Image_Load(element_id, dropped_filenames_table[1])
end
-- create new gui
reagirl.Gui_New()
-- add the image of a bass guitar to this gui
image_id = reagirl.Image_Add(10, 10, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "An image", "A user selectable image.", Image_RunFunction)
-- add a dropzone for dropped files for this image
reagirl.UI_Element_GetSet_DropZoneFunction(image_id, true, Image_DropZone_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "Image Viewer", "This is a demo image viewer.", 120, 120)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
You see, it's simply one functioncall of reagirl.Image_Load() to load the dropped file, the first file dropped in this case (dropped_filenames_table[1]).
Now that we have an image-viewer that accepts dropping of image-files, we might want to have a context-menu that allows us to load a file-requester and to clear the image.
Contextmenus work the same as dropzones. You can add a context-menu to a specific ui-element and you add a run-function.
When the user right-clicks the ui-element, the context-menu is opened. When the user selects a menu-entry, the run-function for this context-menu is run, which gives you the chance to react to the user's choice.
Let's add a context-menu using reagirl.UI_Element_GetSet_ContextMenu() including its run-function.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_RunFunction(clicked_image_id)
-- this function is run, when the image is clicked
end
function Image_DropZone_RunFunction(element_id, dropped_filenames_table)
-- this function will be called everytime a file is dropped onto the image
-- load the first file dropped as new image and show it
reagirl.Image_Load(element_id, dropped_filenames_table[1])
end
function Image_ContextMenu_RunFunction(element_id, menu_entry_selection)
-- this function will be called when the user opens up
-- the context-menu of the image and makes a choice
reaper.MB("The user chose menuentry "..menu_entry_selection, "Menu chosen", 0)
end
-- create new gui
reagirl.Gui_New()
-- add the image of a bass guitar to this gui
image_id = reagirl.Image_Add(10, 10, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "An image", "A user selectable image.", Image_RunFunction)
-- add a dropzone for dropped files for this image
reagirl.UI_Element_GetSet_DropZoneFunction(image_id, true, Image_DropZone_RunFunction)
-- add a context-menu to this image
reagirl.UI_Element_GetSet_ContextMenu(image_id, true, "Clear Image|Select a file", Image_ContextMenu_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "Image Viewer", "This is a demo image viewer.", 120, 120)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Now you can right-click on the image and a menu will appear. You can select either of the entries and the run-function Image_ContextMenu_RunFunction() will show a dialog with the selected menu-entry-number.
The parameter menu of the function reagirl.UI_Element_GetSet_ContextMenu works like gfx.showmenu from Reaper's own gfx-functions.
So check out the docs for it for more details or read the chapter about context-menus, where I'll explain it in more detail.
Ok, now that we have a menu, let's alter the run-function Image_ContextMenu_RunFunction() to react to the individual menu-entries.
The first menu-entry will clear the image to be fully black, while the other one loads a filerequester that allows you to choose an image.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_RunFunction(clicked_image_id)
-- this function is run, when the image is clicked
end
function Image_DropZone_RunFunction(element_id, dropped_filenames_table)
-- this function will be called everytime a file is dropped onto the image
-- load the first file dropped as new image and show it
reagirl.Image_Load(element_id, dropped_filenames_table[1])
end
function Image_ContextMenu_RunFunction(element_id, menu_entry_selection)
-- this function will be called when the user opens up
-- the context-menu of the image and makes a choice
if menu_entry_selection==1 then
-- if user chose the first menu-entry, clear the image to black
reagirl.Image_ClearToColor(element_id, 0, 0, 0)
elseif menu_entry_selection==2 then
-- if user chose the second menu-entry, allow to load an image using a file requester
local retval, filename =
reaper.GetUserFileNameForRead(reaper.GetResourcePath().."/Data/track_icons/",
"Choose an image to load",
"*.png;*.jpg")
if retval==true then
reagirl.Image_Load(element_id, filename)
end
end
end
-- create new gui
reagirl.Gui_New()
-- add the image of a bass guitar to this gui
image_id = reagirl.Image_Add(10, 10, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "An image", "A user selectable image.", Image_RunFunction)
-- add a dropzone for dropped files for this image
reagirl.UI_Element_GetSet_DropZoneFunction(image_id, true, Image_DropZone_RunFunction)
-- add a context-menu to this image
reagirl.UI_Element_GetSet_ContextMenu(image_id, true, "Clear Image|Select a file", Image_ContextMenu_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "Image Viewer", "This is a demo image viewer.", 120, 120)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Nice, it does the job. But there's still one thing, that might bother you: images that are not square will be shown stretched. So, why not keeping the aspect ratio of the image properly?
We can achieve this with the function reagirl.Image_KeepAspectRatio()
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_RunFunction(clicked_image_id)
-- this function is run, when the image is clicked
end
function Image_DropZone_RunFunction(element_id, dropped_filenames_table)
-- this function will be called everytime a file is dropped onto the image
-- load the first file dropped as new image and show it
reagirl.Image_Load(element_id, dropped_filenames_table[1])
end
function Image_ContextMenu_RunFunction(element_id, menu_entry_selection)
-- this function will be called when the user opens up
-- the context-menu of the image and makes a choice
if menu_entry_selection==1 then
-- if user chose the first menu-entry, clear the image to black
reagirl.Image_ClearToColor(element_id, 0, 0, 0)
elseif menu_entry_selection==2 then
-- if user chose the second menu-entry, allow to load an image using a file requester
local retval, filename =
reaper.GetUserFileNameForRead(reaper.GetResourcePath().."/Data/track_icons/",
"Choose an image to load",
"*.png;*.jpg")
if retval==true then
reagirl.Image_Load(element_id, filename)
end
end
end
-- create new gui
reagirl.Gui_New()
-- add the image of a bass guitar to this gui
image_id = reagirl.Image_Add(10, 10, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "An image", "A user selectable image.", Image_RunFunction)
-- add a dropzone for dropped files for this image
reagirl.UI_Element_GetSet_DropZoneFunction(image_id, true, Image_DropZone_RunFunction)
-- add a context-menu to this image
reagirl.UI_Element_GetSet_ContextMenu(image_id, true, "Clear Image|Select a file", Image_ContextMenu_RunFunction)
-- keep the aspect ratio of the image properly
reagirl.Image_KeepAspectRatio(image_id, true)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "Image Viewer", "This is a demo image viewer.", 120, 120)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
This is starting to look good.
Since we're good in programming flow, let's put the currently loaded image as track-icon of the first track in the project everytime the user clicks on the image.
For this, we need to adapt the run-function for the image(it's finally used!).
First, we get the filename of the currently used image-file for the image using reagirl.Image_GetImageFilename() and pass it over to reaper.GetSetMediaTrackInfo_String().
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_RunFunction(clicked_image_id)
-- this function is run, when the image is clicked
-- get the filename of the currently loaded image-file
filename = reagirl.Image_GetImageFilename(clicked_image_id)
-- set it as track-icon of the first track
reaper.GetSetMediaTrackInfo_String(reaper.GetTrack(0,0), "P_ICON", filename, true)
end
function Image_DropZone_RunFunction(element_id, dropped_filenames_table)
-- this function will be called everytime a file is dropped onto the image
-- load the first file dropped as new image and show it
reagirl.Image_Load(element_id, dropped_filenames_table[1])
end
function Image_ContextMenu_RunFunction(element_id, menu_entry_selection)
-- this function will be called when the user opens up
-- the context-menu of the image and makes a choice
if menu_entry_selection==1 then
-- if user chose the first menu-entry, clear the image to black
reagirl.Image_ClearToColor(element_id, 0, 0, 0)
elseif menu_entry_selection==2 then
-- if user chose the second menu-entry, allow to load an image using a file requester
local retval, filename =
reaper.GetUserFileNameForRead(reaper.GetResourcePath().."/Data/track_icons/",
"Choose an image to load",
"*.png;*.jpg")
if retval==true then
reagirl.Image_Load(element_id, filename)
end
end
end
-- create new gui
reagirl.Gui_New()
-- add the image of a bass guitar to this gui
image_id = reagirl.Image_Add(10, 10, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "An image", "A user selectable image.", Image_RunFunction)
-- add a dropzone for dropped files for this image
reagirl.UI_Element_GetSet_DropZoneFunction(image_id, true, Image_DropZone_RunFunction)
-- add a context-menu to this image
reagirl.UI_Element_GetSet_ContextMenu(image_id, true, "Clear Image|Select a file", Image_ContextMenu_RunFunction)
-- keep the aspect ratio of the image properly
reagirl.Image_KeepAspectRatio(image_id, true)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "Image Viewer", "This is a demo image viewer.", 120, 120)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When running this script, left-clicking the image will set the loaded image to be the track-icon of track 1 in the project.
And then we're done.
A gui-script which shows an image on which you can drag n drop image-files. You can also use a context-menu with it to clear it/load a new image.
And when clicking on it, the loaded imagefile will be set as track-icon.
In 64 lines of code(including comments ;) ).
You may have seen it in certain guis, that sometimes gui-elements are disabled. You can do this too in ReaGirl.
In this tutorial, I will show you, how.
First things first: not all ui-elements can be set to disabled, but those who can have a _SetDisabled-function.
Like reagirl.Button_SetDisabled() or reagirl.Checkbox_SetDisabled, etc.
So let's dive into it.
In this tutorial I want to introduce you to two new gui-elements not yet covered in the other ones: Sliders and Drop Down Menus.
Our final gui will have one slider and one drop down menu plus checkboxes who disable/enable them.
First, let's create our basic gui without any gui-element in it as a base, that just opens a window.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 120)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Now, let's add first ui-elements: a checkbox and a slider. I will also add two run-functions, one for the checkboxes and one for the slider.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Slider_RunFunction(used_slider_id, current_value)
-- this function is run, when the slider is moved
reaper.ClearConsole()
reaper.ShowConsoleMsg("The current value is: "..current_value)
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_disableSlider_id then
reaper.ClearConsole()
reaper.ShowConsoleMsg("Checkbox is "..tostring(checkstate), "", 0)
end
end
-- create new gui
reagirl.Gui_New()
-- add a checkbox and a slider to the gui
checkbox_disableSlider_id = reagirl.Checkbox_Add(30, 50, "Activated", "Check to activate slider.", true, Checkbox_RunFunction)
slider_id = reagirl.Slider_Add(200, 50, -20, "I am a slider", nil, "A slider to set a value.", "%", 20, 200, 1, 25, 100, Slider_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 120)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When you run this code, you see a checkbox and a slider. When you click on the checkbox or move the slider, you will see the current value in the ReaScript console window.
Now, let's make the checkbox disable the slider. For this, we use the function reagirl.Slider_SetDisabled() in the run-function Checkbox_RunFunction.
We will use the element_id of the slider called slider_id to adress it in reagirl.Slider_SetDisabled().
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Slider_RunFunction(used_slider_id, current_value)
-- this function is run, when the slider is moved
reaper.ClearConsole()
reaper.ShowConsoleMsg("The current value is: "..current_value)
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_disableSlider_id then
-- if the first checkbox's checkstate is changed to true
if reagirl.Checkbox_GetCheckState(checked_checkbox_id)==true then
reagirl.Slider_SetDisabled(slider_id, false) -- set the slider enabled
else -- otherwise
reagirl.Slider_SetDisabled(slider_id, true) -- set the slider disabled
end
end
end
-- create new gui
reagirl.Gui_New()
-- add a checkbox and a slider to the gui
checkbox_disableSlider_id = reagirl.Checkbox_Add(30, 50, "Activated", "Check to activate slider.", true, Checkbox_RunFunction)
slider_id = reagirl.Slider_Add(200, 50, -20, "I am a slider", nil, "A slider to set a value.", "%", 20, 200, 1, 25, 100, Slider_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 120)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When running this script, clicking the checkbox will change the slider to disabled/enabled, depending on the checkbox-state.
Now, let's do this for another ui-element. But this time with a drop down menu. And we want it to set disabled at script startup.
To do this, we add another checkbox and a drop down menu(using reagirl.DropDownMenu_Add()).
And as usual: we add a run-function called DropDownMenu_RunFunction() for the drop down menu.
For the second checkbox, we use again the Checkbox_RunFunction(), but we'll modify it in a later step.
Let's see, how the gui looks now.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Slider_RunFunction(used_slider_id, current_value)
-- this function is run, when the slider is moved
reaper.ClearConsole()
reaper.ShowConsoleMsg("The current value is: "..current_value)
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_disableSlider_id then
-- if the first checkbox's checkstate is changed to true
if reagirl.Checkbox_GetCheckState(checked_checkbox_id)==true then
reagirl.Slider_SetDisabled(slider_id, false) -- set the slider enabled
else -- otherwise
reagirl.Slider_SetDisabled(slider_id, true) -- set the slider disabled
end
end
end
function DropDownMenu_RunFunction(used_dropdownmenu_id, selected_menuitem, selected_name)
-- this function is run, when the user selects a menu-entry
reaper.MB("Dropdownmenu entry #"..selected_menuitem.." - "..selected_name, "", 0)
end
-- create new gui
reagirl.Gui_New()
-- add a checkbox and a slider to the gui
checkbox_disableSlider_id = reagirl.Checkbox_Add(30, 50, "Activated", "Check to activate slider.", true, Checkbox_RunFunction)
slider_id = reagirl.Slider_Add(200, 50, -20, "I am a slider", nil, "A slider to set a value.", "%", 20, 200, 1, 25, 100, Slider_RunFunction)
-- add a checkbox and a drop-down-menu to the gui
checkbox_disableDropDownMenu_id = reagirl.Checkbox_Add(30, 72, "Activated", "Check to activate drop-down-menu.", false, Checkbox_RunFunction)
dropdownmenu_id = reagirl.DropDownMenu_Add(200, 72, -20, "I am a dropdownmenu", nil, "A Drop Down Menu to choose from.", {"Entry 1 - The first entry", "Entry 2 - The second entry", "Entry 3 - The third entry"}, 2, DropDownMenu_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 120)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
This code shows now additionally a checkbox and a drop down menu. You can select menu-entries by clicking into it and when you do, the run-function of the drop down menu will show a messagebox with the selected menu-entry.
The checkbox isn't doing anything yet, so clicking has no effect. Let's change this, by altering the run-function Checkbox_RunFunction().
It's basically done the same way, as we did for the slider. However, we use now reagirl.DropDownMenu_SetDisabled() to disable the drop down menu.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Slider_RunFunction(used_slider_id, current_value)
-- this function is run, when the slider is moved
reaper.ClearConsole()
reaper.ShowConsoleMsg("The current value is: "..current_value)
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_disableSlider_id then
-- if the first checkbox's checkstate is changed to true
if reagirl.Checkbox_GetCheckState(checked_checkbox_id)==true then
reagirl.Slider_SetDisabled(slider_id, false) -- set the slider enabled
else -- otherwise
reagirl.Slider_SetDisabled(slider_id, true) -- set the slider disabled
end
-- if the second checkbox's checkstate is changed to true
elseif checked_checkbox_id==checkbox_disableDropDownMenu_id then
if reagirl.Checkbox_GetCheckState(checked_checkbox_id)==true then
reagirl.DropDownMenu_SetDisabled(dropdownmenu_id, false) -- set the drop down menu to enabled
else -- otherwise
reagirl.DropDownMenu_SetDisabled(dropdownmenu_id, true) -- set the drop down menu to disabled
end
end
end
function DropDownMenu_RunFunction(used_dropdownmenu_id, selected_menuitem, selected_name)
-- this function is run, when the user selects a menu-entry
reaper.MB("Dropdownmenu entry #"..selected_menuitem.." - "..selected_name, "", 0)
end
-- create new gui
reagirl.Gui_New()
-- add a checkbox and a slider to the gui
checkbox_disableSlider_id = reagirl.Checkbox_Add(30, 50, "Activated", "Check to activate slider.", true, Checkbox_RunFunction)
slider_id = reagirl.Slider_Add(200, 50, -20, "I am a slider", nil, "A slider to set a value.", "%", 20, 200, 1, 25, 100, Slider_RunFunction)
-- add a checkbox and a drop-down-menu to the gui
checkbox_disableDropDownMenu_id = reagirl.Checkbox_Add(30, 72, "Activated", "Check to activate drop-down-menu.", false, Checkbox_RunFunction)
dropdownmenu_id = reagirl.DropDownMenu_Add(200, 72, -20, "I am a dropdownmenu", nil, "A Drop Down Menu to choose from.", {"Entry 1 - The first entry", "Entry 2 - The second entry", "Entry 3 - The third entry"}, 2, DropDownMenu_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 120)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
This will set the drop down menu to disabled, when you set the checkbox to unchecked and enabled when you set it to checked.
However, you have probably noticed when starting up the script, the checkbox is unchecked but the drop down menu is still enabled. Only checking the checkbox on and off will make it disabled.
To solve this, we add one last reagirl.DropDownMenu_SetDisabled(), this time right after the reagirl.DropDownMenu_Add-functioncall, using the element_id of the drop down list.
And we set it to disable the drop down menu(true).
Here's how the code looks like now.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Slider_RunFunction(used_slider_id, current_value)
-- this function is run, when the slider is moved
reaper.ClearConsole()
reaper.ShowConsoleMsg("The current value is: "..current_value)
end
function Checkbox_RunFunction(checked_checkbox_id, checkstate)
-- this function is run, when the checkstate of a checkbox is changed
if checked_checkbox_id==checkbox_disableSlider_id then
-- if the first checkbox's checkstate is changed to true
if reagirl.Checkbox_GetCheckState(checked_checkbox_id)==true then
reagirl.Slider_SetDisabled(slider_id, false) -- set the slider enabled
else -- otherwise
reagirl.Slider_SetDisabled(slider_id, true) -- set the slider disabled
end
-- if the second checkbox's checkstate is changed to true
elseif checked_checkbox_id==checkbox_disableDropDownMenu_id then
if reagirl.Checkbox_GetCheckState(checked_checkbox_id)==true then
reagirl.DropDownMenu_SetDisabled(dropdownmenu_id, false) -- set the drop down menu to enabled
else -- otherwise
reagirl.DropDownMenu_SetDisabled(dropdownmenu_id, true) -- set the drop down menu to disabled
end
end
end
function DropDownMenu_RunFunction(used_dropdownmenu_id, selected_menuitem, selected_name)
-- this function is run, when the user selects a menu-entry
reaper.MB("Dropdownmenu entry #"..selected_menuitem.." - "..selected_name, "", 0)
end
-- create new gui
reagirl.Gui_New()
-- add a checkbox and a slider to the gui
checkbox_disableSlider_id = reagirl.Checkbox_Add(30, 50, "Activated", "Check to activate slider.", true, Checkbox_RunFunction)
slider_id = reagirl.Slider_Add(200, 50, -20, "I am a slider", nil, "A slider to set a value.", "%", 20, 200, 1, 25, 100, Slider_RunFunction)
-- add a checkbox and a drop-down-menu to the gui
checkbox_disableDropDownMenu_id = reagirl.Checkbox_Add(30, 72, "Activated", "Check to activate drop-down-menu.", false, Checkbox_RunFunction)
dropdownmenu_id = reagirl.DropDownMenu_Add(200, 72, -20, "I am a dropdownmenu", nil, "A Drop Down Menu to choose from.", {"Entry 1 - The first entry", "Entry 2 - The second entry", "Entry 3 - The third entry"}, 2, DropDownMenu_RunFunction)
reagirl.DropDownMenu_SetDisabled(dropdownmenu_id, true) -- set drop-down-menu to disabled
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 640, 120)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
And there we are: the finished gui that enables/disables the slider and the drop down menu according to the states of the checkboxes.
In this tutorial, I will show you, how to make a gui, where an image is draggable to multiple destinations.
It will have one image, that can be dragged and two other images and a label, which act as destinations for the dragged source-image.
Dragging the source-image to the destination-images will change the destination-image to the one of the source image, while dragging to the label will show the filename+path of the source-image.
Note: blind users can drag the image using Ctrl+Shift+PageUp or Ctrl+Shift+PageDown to select, to which ui-element the images shall be dragged to and Ctrl+Shift+Enter to drop it.
Let's start first with a basic-gui, that will show the source-image and have an empty run-function for the image.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_Runfunction(element_id, imagepath_plus_filename, drag_destination)
-- this function will be run when the image is clicked or dragged to a destination
end
-- start a new gui
reagirl.Gui_New()
-- add the source-image, which we will draggable
image_source_id = reagirl.Image_Add(20, 100, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "Bass-guitar", "An image of a bass guitar.", Image_Runfunction)
-- open gui
reagirl.Gui_Open("My Dialog Name", false, "ReaGirl Tutorial", "Tutorial for draggable images.", 665, 310, nil, nil, nil)
-- manage gui
function main()
reagirl.Gui_Manage()
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
This will show our image of a bass guitar. Note, how the meaningOfUI_Element-parameter explains, that the image shows a bass-guitar. This is important for blind users, since they can't see the image and need to know, what the image looks like by you giving a short description.
Clicking the image does nothing yet, as well as trying to drag.
Now, let's add our dragging-destinations, which will be a label and two images.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_Runfunction(element_id, imagepath_plus_filename, drag_destination)
-- this function will be run when the image is clicked or dragged to a destination
end
-- start a new gui
reagirl.Gui_New()
-- add the source-image, which we will draggable
image_source_id = reagirl.Image_Add(20, 100, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "Bass-guitar", "An image of a bass guitar.", Image_Runfunction)
-- add some additional images and a label, that are the destinations for the dragging
label_id = reagirl.Label_Add(240, 10, "Nothing has been dragged to this label, yet", "A destination for the source-image to drag to.", false, nil)
image_dest1_id = reagirl.Image_Add(220, 50, 100, 100, reaper.GetResourcePath().."/Data/track_icons/amp.png", "Amplifier", "An image of an amplifier.", nil)
image_dest2_id = reagirl.Image_Add(220, 160, 100, 100, reaper.GetResourcePath().."/Data/track_icons/congas.png", "Congas", "An image of congas.", nil)
-- open gui
reagirl.Gui_Open("My Dialog Name", false, "ReaGirl Tutorial", "Tutorial for draggable images.", 665, 310, nil, nil, nil)
-- manage gui
function main()
reagirl.Gui_Manage()
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
This shows now the source and destination-images as well as a label. The parameter meaningOfUI_Element describes now, what the images are showing(amp, congas).
In the next step, we will make the source-image draggable. For this, we use the function reagirl.Image_SetDraggable(), in which we can set, which ui-element shall be destination of the source-image-dragging.
It will look like this.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_Runfunction(element_id, imagepath_plus_filename, drag_destination)
-- this function will be run when the image is clicked or dragged to a destination
reaper.MB("Dragged to: "..reagirl.UI_Element_GetSetCaption(drag_destination, false, ""), "", 0)
end
-- start a new gui
reagirl.Gui_New()
-- add the source-image, which we will draggable
image_source_id = reagirl.Image_Add(20, 100, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "Bass-guitar", "An image of a bass guitar.", Image_Runfunction)
-- add some additional images and a label, that are the destinations for the dragging
label_id = reagirl.Label_Add(240, 10, "Nothing has been dragged to this label, yet", "A destination for the source-image to drag to.", false, nil)
image_dest1_id = reagirl.Image_Add(220, 50, 100, 100, reaper.GetResourcePath().."/Data/track_icons/amp.png", "Amplifier", "An image of an amplifier.", nil)
image_dest2_id = reagirl.Image_Add(220, 160, 100, 100, reaper.GetResourcePath().."/Data/track_icons/congas.png", "Congas", "An image of congas.", nil)
-- add the element_ids of image2-4_id as drag-destinations of image1_id
reagirl.Image_SetDraggable(image_source_id, true, {label_id, image_dest1_id, image_dest2_id})
-- open gui
reagirl.Gui_Open("My Dialog Name", false, "ReaGirl Tutorial", "Tutorial for draggable images.", 665, 310, nil, nil, nil)
-- manage gui
function main()
reagirl.Gui_Manage()
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
You can see, that we pass to it the element_ids of the destinations, like label_id, image_dest1_id and image_dest2_id.
When you run this, you can drag the source-image of a bass-guitar around. You will see a smaller version of the image is shown at the mouse-cursor during dragging.
When dropping the source-image to a destination, the run-function of the source-image Image_Runfunction() will be run and shows a messagebox that tells you, onto which ui-element you dragged the source-image.
It will use the function reagirl.UI_Element_GetSetCaption() to get the caption of the dragged ui-element.
The run-function uses the third parameter, drag_destination. This parameter is sent to run-functions once the image it is associated with is set to draggable, like image_source_id in our case.
Now, we want to make the label show the filename of the source-image when dropping the source-image onto it.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_Runfunction(element_id, imagepath_plus_filename, drag_destination)
-- this function will be run when the image is clicked or dragged to a destination
if drag_destination==label_id then
-- if source-image is dragged to the label, set labeltext to filename of the source-image
-- get the filename of the source-image
image_filename = reagirl.Image_GetImageFilename(element_id)
-- set label-text to filename of source-image
reagirl.Label_SetLabelText(label_id, "Filename of source-image is: "..image_filename)
end
end
-- start a new gui
reagirl.Gui_New()
-- add the source-image, which we will draggable
image_source_id = reagirl.Image_Add(20, 100, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "Bass-guitar", "An image of a bass guitar.", Image_Runfunction)
-- add some additional images and a label, that are the destinations for the dragging
label_id = reagirl.Label_Add(240, 10, "Nothing has been dragged to this label, yet", "A destination for the source-image to drag to.", false, nil)
image_dest1_id = reagirl.Image_Add(220, 50, 100, 100, reaper.GetResourcePath().."/Data/track_icons/amp.png", "Amplifier", "An image of an amplifier.", nil)
image_dest2_id = reagirl.Image_Add(220, 160, 100, 100, reaper.GetResourcePath().."/Data/track_icons/congas.png", "Congas", "An image of congas.", nil)
-- add the element_ids of image2-4_id as drag-destinations of image1_id
reagirl.Image_SetDraggable(image_source_id, true, {label_id, image_dest1_id, image_dest2_id})
-- open gui
reagirl.Gui_Open("My Dialog Name", false, "ReaGirl Tutorial", "Tutorial for draggable images.", 665, 310, nil, nil, nil)
-- manage gui
function main()
reagirl.Gui_Manage()
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When we drag the source-image now to the label, the run-function Image_Runfunction() is run, in which we check the parameter drag_destination against the element_id label_id.
If the source-image has been dropped at the label(drag_destination==label_id) then we get the filename of the source-image and set the caption of the label to it.
Nice.
Now, let's alter the run-function even further. We will now make it, that dragging the source-image onto the destination-images will change the destination-images once the image was dropped on them.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_Runfunction(element_id, imagepath_plus_filename, drag_destination)
-- this function will be run when the image is clicked or dragged to a destination
if drag_destination==label_id then
-- if source-image is dragged to the label, set labeltext to
-- filename of the source-image
-- get the filename of the source-image
image_filename = reagirl.Image_GetImageFilename(element_id)
-- set label-text to filename of source-image
reagirl.Label_SetLabelText(label_id, "Filename of source-image is: "..image_filename)
elseif drag_destination==image_dest1_id then
-- if source_image is dragged to image2, change it's image to the
-- one of the source-image
-- get the filename of the source-image
image_filename = reagirl.Image_GetImageFilename(element_id)
-- load the filename of the source-image in the destination-image
reagirl.Image_Load(image_dest1_id, image_filename)
elseif drag_destination==image_dest2_id then
-- if source_image is dragged to image2, change it's image to the
-- one of the source-image
-- get the filename of the source-image
image_filename = reagirl.Image_GetImageFilename(element_id)
-- load the filename of the source-image in the destination-image
reagirl.Image_Load(image_dest2_id, image_filename)
end
end
-- start a new gui
reagirl.Gui_New()
-- add the source-image, which we will draggable
image_source_id = reagirl.Image_Add(20, 100, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "Bass-guitar", "An image of a bass guitar.", Image_Runfunction)
-- add some additional images and a label, that are the destinations for the dragging
label_id = reagirl.Label_Add(240, 10, "Nothing has been dragged to this label, yet", "A destination for the source-image to drag to.", false, nil)
image_dest1_id = reagirl.Image_Add(220, 50, 100, 100, reaper.GetResourcePath().."/Data/track_icons/amp.png", "Amplifier", "An image of an amplifier.", nil)
image_dest2_id = reagirl.Image_Add(220, 160, 100, 100, reaper.GetResourcePath().."/Data/track_icons/congas.png", "Congas", "An image of congas.", nil)
-- add the element_ids of image2-4_id as drag-destinations of image1_id
reagirl.Image_SetDraggable(image_source_id, true, {label_id, image_dest1_id, image_dest2_id})
-- open gui
reagirl.Gui_Open("My Dialog Name", false, "ReaGirl Tutorial", "Tutorial for draggable images.", 665, 310, nil, nil, nil)
-- manage gui
function main()
reagirl.Gui_Manage()
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When we run this code, dropping the source-image onto one of the destination-images will now change the shown image.
To achieve this, we checked in the run-function Image_Runfunction() if the destination-ui-element(drag_destination-parameter) was either the first destination image(image_dest1_id) or the second(image_dest2_id).
If so, we get the filename of the source using reagirl.Image_GetImageFilename() and load it using reagirl.Image_Load() into the destination-image.
Awesome, it's already looking good. But: there's one thing we missed:
Remember that the description of the destination images was amp and conga? It still is, even though it's now showing a bass-guitar. That means, blind users didn't get, that the images was changed.
Let's change that.
For this, we need to alter the run-function Image_Runfunction and add the two functions, reagirl.UI_Element_GetSetMeaningOfUIElement() and reagirl.UI_Element_GetSetCaption().
The function reagirl.UI_Element_GetSetCaption() will change the caption, which is sent to blind users when hovering above a ui-element.
The function reagirl.UI_Element_GetSetMeaningOfUIElement() will change the screen reader-message, which is sent to blind users when they are tabbing through the ui-elements.
For the label we don't need to change the caption, since label-text and caption is the same.
We need to alter the run-function for the images as well as the label.
Note: in the ReaGirl-Settings you find an option called "Show screen reader messages in console", which will show the messages sent to blind users in the ReaScript-console window.
With that, you can check, whether the meaningOfUI_Element was correctly altered or is even reflecting the correct message.
Ok, now that we altered the script, it looks like this now.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Image_Runfunction(element_id, imagepath_plus_filename, drag_destination)
-- this function will be run when the image is clicked or dragged to a destination
if drag_destination==label_id then
-- if source-image is dragged to the label, set labeltext to
-- filename of the source-image
-- get the filename of the source-image
image_filename = reagirl.Image_GetImageFilename(element_id)
-- set label-text to filename of source-image
reagirl.Label_SetLabelText(label_id, "Filename of source-image is: "..image_filename)
-- change meaningOfUI_Element to reflect, what the label shows now for blind users
reagirl.UI_Element_GetSetMeaningOfUIElement(label_id, true, "An image of a bass-guitar")
elseif drag_destination==image_dest1_id then
-- if source_image is dragged to image2, change it's image to the
-- one of the source-image
-- get the filename of the source-image
image_filename = reagirl.Image_GetImageFilename(element_id)
-- load the filename of the source-image in the destination-image
reagirl.Image_Load(image_dest1_id, image_filename)
-- change meaningOfUI_Element to reflect, what the image shows now for blind users
reagirl.UI_Element_GetSetMeaningOfUIElement(image_dest1_id, true, "An image of a bass-guitar")
reagirl.UI_Element_GetSetCaption(image_dest1_id, true, "Image of a bass guitar")
elseif drag_destination==image_dest2_id then
-- if source_image is dragged to image2, change it's image to the
-- one of the source-image
-- get the filename of the source-image
image_filename = reagirl.Image_GetImageFilename(element_id)
-- load the filename of the source-image in the destination-image
reagirl.Image_Load(image_dest2_id, image_filename)
-- change meaningOfUI_Element to reflect, what the image shows now for blind users
reagirl.UI_Element_GetSetMeaningOfUIElement(image_dest2_id, true, "An image of a bass-guitar.")
reagirl.UI_Element_GetSetCaption(image_dest2_id, true, "Image of a bass guitar")
end
end
-- start a new gui
reagirl.Gui_New()
-- add the source-image, which we will draggable
image_source_id = reagirl.Image_Add(20, 100, 100, 100, reaper.GetResourcePath().."/Data/track_icons/bass.png", "Bass-guitar", "An image of a bass guitar.", Image_Runfunction)
-- add some additional images and a label, that are the destinations for the dragging
label_id = reagirl.Label_Add(240, 10, "Nothing has been dragged to this label, yet", "A destination for the source-image to drag to.", false, nil)
image_dest1_id = reagirl.Image_Add(220, 50, 100, 100, reaper.GetResourcePath().."/Data/track_icons/amp.png", "Amplifier", "An image of an amplifier.", nil)
image_dest2_id = reagirl.Image_Add(220, 160, 100, 100, reaper.GetResourcePath().."/Data/track_icons/congas.png", "Congas", "An image of congas.", nil)
-- add the element_ids of image2-4_id as drag-destinations of image1_id
reagirl.Image_SetDraggable(image_source_id, true, {label_id, image_dest1_id, image_dest2_id})
-- open gui
reagirl.Gui_Open("My Dialog Name", false, "ReaGirl Tutorial", "Tutorial for draggable images.", 665, 310, nil, nil, nil)
-- manage gui
function main()
reagirl.Gui_Manage()
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
You'll notice, how we alter the screen reader-message using reagirl.UI_Element_GetSetMeaningOfUIElement() as well as the caption using reagirl.UI_Element_GetSetCaption().
And when you run the script, tabbing through the ui-elements will now tell to blind users, which image has been used by image_dest1_id and image_dest2_id and label_id.
Great. Now we have a script that allows dragging a source-image to various destinations. It alters the images and labels when the source-image has been dropped.
We also altered the screen reader-messages accordingly to reflect the changes, so blind users know, what has happened with dragging as well.
In this tutorial, we made images draggable, but you can also make labels draggable. The only difference is, that for labels, the run-function is getting the destination-element_id as second parameter instead of the third.
The rest is the same.
In ReaGirl, you can set a run-function for when the gui-window is closed using esc and closing via the close-button.
You can also set a run-function to the enter-key, so you can have a "Apply settings and close window via Enter-key"-feature.
In this tutorial, we'l look at how to do them.
Let's start with a basic gui.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction()
-- this function will be run when the button is pressed
-- It stores the setting and keeps the dialog open
reaper.SetExtState("My_Setting", "My_Key", reagirl.Slider_GetValue(slider_percentage), true)
end
-- create new gui
reagirl.Gui_New()
-- Add a slider and a button to the gui.
-- First: get old stored value of the slider
old_slider_value=reaper.GetExtState("My_Setting", "My_Key")
-- Second: if there isn't a slider-value stored yet, use a default of 0
if old_slider_value=="" then old_slider_value=0 else old_slider_value=tonumber(old_slider_value) end
-- add the slider with the slider-value stored in old_slider_value
slider_percentage = reagirl.Slider_Add(4, 4, 250, "Percentage", 140, "Set the percentage.", nil, 0, 8, 1, old_slider_value, 0, nil)
-- add a store button
button = reagirl.Button_Add(260, 4, 0, 0, "Store", "Store percentage setting.", Button_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 325, 40)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When you run the script, you get a script with one slider and a button to store the value of the slider in an extstate.
You can close it via the close-button of the window and the esc-key.
In the next step, we want to show a dialog that tells the user that the slider-value wasn't stored when closing via esc-key or the close-button.
For this we add a new run-function AtExit_RunFunction and also add the function reagirl.Gui_AtExit() to tell ReaGirl, which run-function to use when the gui is closed.
This is how it looks like, now.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction()
-- this function will be run when the button is pressed
-- It stores the setting and keeps the dialog open
reaper.SetExtState("My_Setting", "My_Key", reagirl.Slider_GetValue(slider_percentage), true)
end
function AtExit_RunFunction()
-- this function is run when the window is closed by either esc-key or the x-button of the window
-- it will show an aborted dialog
reaper.MB("Aborted setting the percentage.\n\nPercentage is not stored.", "Abort", 0)
end
-- create new gui
reagirl.Gui_New()
-- Add a slider and a button to the gui.
-- First: get old stored value of the slider
old_slider_value=reaper.GetExtState("My_Setting", "My_Key")
-- Second: if there isn't a slider-value stored yet, use a default of 0
if old_slider_value=="" then old_slider_value=0 else old_slider_value=tonumber(old_slider_value) end
-- add the slider with the slider-value stored in old_slider_value
slider_percentage = reagirl.Slider_Add(4, 4, 250, "Percentage", 140, "Set the percentage.", nil, 0, 8, 1, old_slider_value, 0, nil)
-- add a store button
button = reagirl.Button_Add(260, 4, 0, 0, "Store", "Store percentage setting.", Button_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 325, 40)
reagirl.Gui_AtExit(AtExit_RunFunction)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When you run this script, you can close the window via esc-key and the close-button and if you do, a dialog opens up that tells the user that storing the setting was aborted.
We made this possible with the function AtExit_RunFunction(), which opens a dialog using reaper.MB(). And we used reagirl.Gui_AtExit(AtExit_RunFunction) to tell Reaper to run the run-function AtExit_RunFunction everytime the window is closes using the close-button or the esc-key.
Great.
In the next step, we add that hitting enter stores the slider-value and closes the window. For this, we add another run-function AtEnter_RunFunction() and the function reagirl.Gui_AtEnter(AtEnter_RunFunction) to tell ReaGirl to run the run-function everytime the user hits enter in the dialog.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
function Button_RunFunction()
-- this function will be run when the button is pressed
-- It stores the setting and keeps the dialog open
reaper.SetExtState("My_Setting", "My_Key", reagirl.Slider_GetValue(slider_percentage), true)
end
function AtEnter_RunFunction()
-- this function is run, when the enter-key is hit
-- it stores the setting, shows a success-message and closes the gui
reaper.SetExtState("My_Setting", "My_Key", reagirl.Slider_GetValue(slider_percentage), true)
reaper.MB("Entered new percentage", "Success", 0)
reagirl.Gui_Close()
end
function AtExit_RunFunction()
-- this function is run when the window is closed by either esc-key or the x-button of the window
-- it will show an aborted dialog
reaper.MB("Aborted setting the percentage.\n\nPercentage is not stored.", "Abort", 0)
end
-- create new gui
reagirl.Gui_New()
-- Add a slider and a button to the gui.
-- First: get old stored value of the slider
old_slider_value=reaper.GetExtState("My_Setting", "My_Key")
-- Second: if there isn't a slider-value stored yet, use a default of 0
if old_slider_value=="" then old_slider_value=0 else old_slider_value=tonumber(old_slider_value) end
-- add the slider with the slider-value stored in old_slider_value
slider_percentage = reagirl.Slider_Add(4, 4, 250, "Percentage", 140, "Set the percentage.", nil, 0, 8, 1, old_slider_value, 0, nil)
-- add a store button
button = reagirl.Button_Add(260, 4, 0, 0, "Store", "Store percentage setting.", Button_RunFunction)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 325, 40)
reagirl.Gui_AtExit(AtExit_RunFunction)
reagirl.Gui_AtEnter(AtEnter_RunFunction)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
You'll notice, that the run-function AtEnter_RunFunction() stores the slider-value into an extstate. It also closes the gui using Gui_Close() and shows a dialog using reaper.MB() that tells the user that the slider-value is stored.
And when you run this script, you can change the slider, hit enter and when you rerun the script, the stored slider-value is restored.
With that, you have a dialog that reacts to closing via close-button, the esc-key and the enter key.
One thing to notice: the run-function for enter isn't run when the an inputbox is currently selected, which uses a run-function for the enter-key! It's also not run when the inputbox is currently selected and the user is a screen reader-user.
So you might need to add a run-function for inputboxes that does the same as the enter-run-function used by reagirl.Gui_AtEnter()
When doing guis in ReaGirl, you can use its autopositioning-feature. This is very practical, as you don't really need to place them by hand.
Especially when you are blind and want to create a gui, you need a way to position the gui-elements without having to use your eyes to finetune things.
In this and the next tutorials, I will show you, how to use it.
And believe me: it's easy.
First we start with a basic gui with no ui-element in it.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 425, 240)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
reagirl.Gui_AtExit(AtExit_RunFunction)
reagirl.Gui_AtEnter(AtEnter_RunFunction)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
And then we add some ui-elements in it, let's say, three checkboxes. These shall be one after another in one line.
To do that with the autopositioning-feature, you simply set the x and y-parameters to nil.
Here's how it looks like.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- first line of checkboxes
reagirl.Checkbox_Add(nil, nil, "Checkbox 1", "This is the first checkbox.", true, nil)
reagirl.Checkbox_Add(nil, nil, "Checkbox 2", "This is the second checkbox.", true, nil)
reagirl.Checkbox_Add(nil, nil, "Checkbox 3", "This is the third checkbox.", true, nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 425, 240)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
reagirl.Gui_AtExit(AtExit_RunFunction)
reagirl.Gui_AtEnter(AtEnter_RunFunction)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
There you are. Three checkboxes, positioned after each other in one line.
Now, let's add another line into it, but how to do that?
Simple. Before we start another line, we add reagirl.NextLine() to it. Then we add another line of checkboxes with x and y set to nil.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- first line of checkboxes
reagirl.Checkbox_Add(nil, nil, "Checkbox 1", "This is the first checkbox.", true, nil)
reagirl.Checkbox_Add(nil, nil, "Checkbox 2", "This is the second checkbox.", true, nil)
reagirl.Checkbox_Add(nil, nil, "Checkbox 3", "This is the third checkbox.", true, nil)
-- second line of checkboxes
reagirl.NextLine() -- start a new line of ui-elements
reagirl.Checkbox_Add(nil, nil, "Checkbox 4", "This is the fourth checkbox.", true, nil)
reagirl.Checkbox_Add(nil, nil, "Checkbox 5", "This is the fifth checkbox.", true, nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 425, 240)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
reagirl.Gui_AtExit(AtExit_RunFunction)
reagirl.Gui_AtEnter(AtEnter_RunFunction)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When you run it, you see two lines of checkboxes, one with three and one with two checkboxes.
Let's add another line of ui-elements, this time a checkbox and a button, anchored to the right side of the window. And we position it a few pixels more than the space between the first two lines were.
To do that, we use reagirl.NextLine() but this time we add 10 pixels as parameter to it.
And then we add a checkbox and a button. For the checkbox we add as x-parameter 200 pixels from the right side of the window(-200) and y-parameter we leave at nil for autopositioning.
The button, we set x=nil and y=nil.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- first line of checkboxes
reagirl.Checkbox_Add(nil, nil, "Checkbox 1", "This is the first checkbox.", true, nil)
reagirl.Checkbox_Add(nil, nil, "Checkbox 2", "This is the second checkbox.", true, nil)
reagirl.Checkbox_Add(nil, nil, "Checkbox 3", "This is the third checkbox.", true, nil)
-- second line of checkboxes
reagirl.NextLine() -- start a new line of ui-elements
reagirl.Checkbox_Add(nil, nil, "Checkbox 4", "This is the fourth checkbox.", true, nil)
reagirl.Checkbox_Add(nil, nil, "Checkbox 5", "This is the fifth checkbox.", true, nil)
-- third line with one checkbox and one button anchored to right side of the window
-- this line is placed 10 pixels lower to gain some distance between the lines
reagirl.NextLine(10) -- start a new line of ui-elements, ten pixels lower than
reagirl.Checkbox_Add(-200, nil, "Checkbox 5", "This is the fifth checkbox.", true, nil)
button = reagirl.Button_Add(nil, nil, 0, 0, "Store", "Store 1.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 425, 240)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
reagirl.Gui_AtExit(AtExit_RunFunction)
reagirl.Gui_AtEnter(AtEnter_RunFunction)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When we run this script, the newly added checkbox and button are anchored to the right side of the window. So when you resize it, they more accordingly.
You also notice, that the button is positioned relative to the checkbox that we put at x=-200. So ui-elements coming after a ui-element that was positioned "manually" will be placed relative to the manually placed one.
Let's add another line with a checkbox and a button, this time anchored again to the left side of the window.
But how do we do that?
Simple:
We set the x-position(40) and this time the y-position as well(100) of the checkbox. After that, we autoposition another button with x=nil and y=nil.
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- first line of checkboxes
reagirl.Checkbox_Add(nil, nil, "Checkbox 1", "This is the first checkbox.", true, nil)
reagirl.Checkbox_Add(nil, nil, "Checkbox 2", "This is the second checkbox.", true, nil)
reagirl.Checkbox_Add(nil, nil, "Checkbox 3", "This is the third checkbox.", true, nil)
-- second line of checkboxes
reagirl.NextLine() -- start a new line of ui-elements
reagirl.Checkbox_Add(nil, nil, "Checkbox 4", "This is the fourth checkbox.", true, nil)
reagirl.Checkbox_Add(nil, nil, "Checkbox 5", "This is the fifth checkbox.", true, nil)
-- third line with one checkbox and one button anchored to right side of the window
-- this line is placed 10 pixels lower to gain some distance between the lines
reagirl.NextLine(10) -- start a new line of ui-elements, ten pixels lower than
reagirl.Checkbox_Add(-200, nil, "Checkbox 5", "This is the fifth checkbox.", true, nil)
button = reagirl.Button_Add(nil, nil, 0, 0, "Store", "Store 1.", nil)
-- fourth line with one checkbox and one button anchored to the left side of the window
reagirl.Checkbox_Add(40, 100, "Checkbox 6", "This is the fifth checkbox.", true, nil)
button = reagirl.Button_Add(nil, nil, 0, 0, "Store 2", "Store 2.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.", 425, 240)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
reagirl.Gui_AtExit(AtExit_RunFunction)
reagirl.Gui_AtEnter(AtEnter_RunFunction)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
And there you are. The new line added is anchored to the left side of the window again and the button has been placed relative to this checkbox.
So rule of thumb is:
1. to autoposition, set x and/or y-position to nil to place ui-elements after each other.
2. add reagirl.NextLine() to start another line of ui-elements
3. when you place a ui-element to specific coordinates, all following ui-elements will be autopositioned relative to the previous one when x=nil and/or y=nil
Toy around with it to get a grip on it.
More on using autopositioning as blind scripter in a later chapter.
In the next tutorial, I will explain to you, how to use auto-positioning with tabs.
In the last tutorial, I showed you how to use autopositioning in ReaGirl. In this one, we will enhance it by adding tabs, which differs slightly.
You probably remember from the last tutorial, that when using auto-positioning, the next ui-element is positioned relative to the previous ui-element.
Means: next to the right of the previous ui-element or underneath it(when using reagirl.NextLine()).
We could use the same thing using tabs, what would look like the following, which includes all the ui-elements from the
previous tutorial but sorted into tabs, using autopositioning:
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
tab1={}
tab2={}
tab3={}
tab4={}
-- let's add tabs
tabs_id = reagirl.Tabs_Add(10, 10, 620, 187, "Tabs", "Different options in this dialog.", {"Tab1", "Tab2", "Tab3", "Tab4"}, 1, Tab_RunFunction)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 1, tab1)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 2, tab2)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 3, tab3)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 4, tab4)
--]]
-- first line of checkboxes
tab1.checkbox1 = reagirl.Checkbox_Add(nil, nil, "Checkbox 1", "This is the first checkbox.", true, nil)
tab1.checkbox2 = reagirl.Checkbox_Add(nil, nil, "Checkbox 2", "This is the second checkbox.", true, nil)
tab1.checkbox3 = reagirl.Checkbox_Add(nil, nil, "Checkbox 3", "This is the third checkbox.", true, nil)
-- second line of checkboxes
reagirl.NextLine() -- start a new line of ui-elements
tab2.checkbox1 = reagirl.Checkbox_Add(nil, nil, "Checkbox 4", "This is the fourth checkbox.", true, nil)
tab2.checkbox2 = reagirl.Checkbox_Add(nil, nil, "Checkbox 5", "This is the fifth checkbox.", true, nil)
-- third line with one checkbox and one button anchored to right side of the window
-- this line is placed 10 pixels lower to gain some distance between the lines
reagirl.NextLine(10) -- start a new line of ui-elements, ten pixels lower than
tab3.checkbox = reagirl.Checkbox_Add(nil, nil, "Checkbox 5", "This is the fifth checkbox.", true, nil)
tab3.button = reagirl.Button_Add(nil, nil, 0, 0, "Store", "Store 1.", nil)
-- fourth line with one checkbox and one button anchored to the left side of the window
reagirl.NextLine()
tab4.checkbox = reagirl.Checkbox_Add(nil, nil, "Checkbox 6", "This is the fifth checkbox.", true, nil)
tab4.button = reagirl.Button_Add(nil, nil, 0, 0, "Store 2", "Store 2.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.")--, 425, 240)
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
reagirl.Gui_AtExit(AtExit_RunFunction)
reagirl.Gui_AtEnter(AtEnter_RunFunction)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
However, when you run it, you quickly realize, there's a problem. When clicking through the tabs, the ui-elements are positioned
under the ui-elements from the previous tabs. So when you click through tab1 to tab4, the ui-elements wander downwards.
This is due the fact, that they are positioned according to the last ui-element in the code and not by the tabs.
And it would be much better, if the ui-elements would start at the top of each tab.
For this, we add another new function, called reagirl.AutoPosition_SetNextYToUIElement(). With this function,
you can set, which ui-element shall be seen as the previous one, so autoposition uses this "newly set" ui-element as basis to position the next ui-element.
In our element, we want to set the first checkbox of each tab right after the tab. So all we do is include
reagirl.AutoPosition_SetNextUIElementRelativeTo(tabs_id)
into our script right before each checkbox, that shall be put at the beginning of the tab. You'll notice, that we
put the element_id of the tabs, "tabs_id", as parameter. This tells autoposition "The next ui-element shall be put after tabs.
Here's how it looks like:
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
tab1={}
tab2={}
tab3={}
tab4={}
-- let's add tabs
tabs_id = reagirl.Tabs_Add(10, 10, 620, 187, "Tabs", "Different options in this dialog.", {"Tab1", "Tab2", "Tab3", "Tab4"}, 1, Tab_RunFunction)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 1, tab1)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 2, tab2)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 3, tab3)
reagirl.Tabs_SetUIElementsForTab(tabs_id, 4, tab4)
-- first line of checkboxes
reagirl.AutoPosition_SetNextUIElementRelativeTo(tabs_id)
tab1.checkbox1 = reagirl.Checkbox_Add(nil, nil, "Checkbox 1", "This is the first checkbox.", true, nil)
tab1.checkbox2 = reagirl.Checkbox_Add(nil, nil, "Checkbox 2", "This is the second checkbox.", true, nil)
tab1.checkbox3 = reagirl.Checkbox_Add(nil, nil, "Checkbox 3", "This is the third checkbox.", true, nil)
-- second line of checkboxes
reagirl.AutoPosition_SetNextUIElementRelativeTo(tabs_id)
reagirl.NextLine() -- start a new line of ui-elements
tab2.checkbox1 = reagirl.Checkbox_Add(nil, nil, "Checkbox 4", "This is the fourth checkbox.", true, nil)
tab2.checkbox2 = reagirl.Checkbox_Add(nil, nil, "Checkbox 5", "This is the fifth checkbox.", true, nil)
-- third line with one checkbox and one button anchored to right side of the window
-- this line is placed 10 pixels lower to gain some distance between the lines
reagirl.AutoPosition_SetNextUIElementRelativeTo(tabs_id)
reagirl.NextLine(10) -- start a new line of ui-elements, ten pixels lower than
tab3.checkbox = reagirl.Checkbox_Add(nil, nil, "Checkbox 5", "This is the fifth checkbox.", true, nil)
tab3.button = reagirl.Button_Add(nil, nil, 0, 0, "Store", "Store 1.", nil)
-- fourth line with one checkbox and one button anchored to the left side of the window
reagirl.AutoPosition_SetNextUIElementRelativeTo(tabs_id)
reagirl.NextLine()
tab4.checkbox = reagirl.Checkbox_Add(nil, nil, "Checkbox 6", "This is the fifth checkbox.", true, nil)
tab4.button = reagirl.Button_Add(nil, nil, 0, 0, "Store 2", "Store 2.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.")
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
reagirl.Gui_AtExit(AtExit_RunFunction)
reagirl.Gui_AtEnter(AtEnter_RunFunction)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
You'll notice, that reagirl.AutoPosition_SetNextYToUIElement() is put right before the reagirl.Checkbox_Add()-functioncalls to tell ReaGirl to autoposition the next checkbox right after the next tab.
And when you run it and click through the tabs, the ui-elements are now positioned at the right position: right after the tabs.
One thing to notice: you could theoretically put the element_id of any ui-element into reagirl.AutoPosition_SetNextYToUIElement(), not just tabs.
That way, you can autoposition in relation to any ui-element.
Keep in mind: when using tabs, autoposition will assume that the next ui-element shall be put into the next line, so you don't need to use reagirl.NextLine() when using reagirl.AutoPosition_SetNextYToUIElement() with tabs.
With other ui-elements, like buttons, checkboxes, sliders, etc, the next ui-element will be placed next to the right of it. So you might need to add reagirl.NextLine() when needed.
You'll see it when it happens and if you want to put the next ui-element underneath the one you put in reagirl.AutoPosition_SetNextYToUIElement(), simply use reagirl.NextLine() and you're fine.
And there you are. If you followed all tutorials up til this one, you know most of the concepts and possibilities in ReaGirl.
This should give you plenty of possibilities to code guis as you need and wish.
In this tutorial, we will structure Guis using labels. For this, we will add backdrops around ui-elements to create sections in your gui. We will also have spaces between these sections, so each section is easy to separate from the other ones for the user.
Backdrops are additional elements for labels. They are a rectangle that starts at half the height of the label and goes around until it ends again on the other side of the label.
You can imagine this as a rectangle, that has a gap on the top at which the label-text is placed. The bottom ends under the last ui-elements of a section of ui-elements.
Backdrops enclose ui-elements to make it easier to see, which ui-elements belong to each other.
If you do only simple guis, you will usually not having problems with guis that are hard to read.
However, when you have a lot of ui-elements in your gui, you may quickly end up with a wall of ui-elements.
Consider the following example-gui.
-- load ReaGirl into this script
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- now let's add all ui-elements of the first tab to table tab1
-- Labels
label_header=reagirl.Label_Add(nil, nil, "Labels", "Possible labels with ReaGirl.", false, nil)
reagirl.NextLine()
label_regular=reagirl.Label_Add(nil, nil, "Example label", "A regular label.", false, nil)
label_clickable=reagirl.Label_Add(nil, nil, "Clickable label", "A clickable label.", true, nil)
label_styled=reagirl.Label_Add(nil, nil, "Styled label", "A styled label.", false, nil)
reagirl.Label_SetStyle(label_styled, 7, 2, 0) -- set to inverted and italic
reagirl.NextLine()
label_small=reagirl.Label_Add(nil, nil, "different", "Label with small font-size.", false, nil)
reagirl.Label_SetFontSize(label_small, 10) -- set to small font-size
label_medium=reagirl.Label_Add(nil, nil, "font", "Label with medium font-size.", false, nil)
reagirl.Label_SetFontSize(label_medium, 30) -- set to medium font-size
label_large=reagirl.Label_Add(nil, nil, "size", "Label with large font-size.", false, nil)
reagirl.Label_SetFontSize(label_large, 50) -- set to large font-size
label_medium_and_style=reagirl.Label_Add(nil, nil, "and style", "Label with medium font-size and some styles.", false, nil)
reagirl.Label_SetFontSize(label_medium_and_style, 30) -- set to medium font-size
reagirl.Label_SetStyle(label_medium_and_style, 2, 0, 0) -- set to italic
-- let it end underneath label_large - the largest label
-- Inputboxes
reagirl.NextLine()
label_header2=reagirl.Label_Add(nil, nil, "Inputboxes", "Some demo-inputboxes.", false, nil)
reagirl.NextLine()
inputbox_name_of_setting = reagirl.Inputbox_Add(nil, nil, 280, "Name:", 90, "Type in here the name of the setting.", "No title", nil, nil)
reagirl.NextLine()
button_choose_file_id = reagirl.Button_Add(223, nil, 0, 0, "Choose file", "Choose a file.", nil) -- a button
-- Checkboxes
reagirl.NextLine()
label_header3=reagirl.Label_Add(nil, nil, "And some checkboxes", "Some checkboxes in ReaGirl.", false, nil)
reagirl.NextLine() -- first line of checkboxes
checkbox_mysetting = reagirl.Checkbox_Add(nil, nil, "My setting", "The first checkbox.", true, nil)
checkbox_another_setting = reagirl.Checkbox_Add(nil, nil, "Another setting", "The second checkbox.", true, nil)
reagirl.NextLine() -- second line of checkboxes
checkbox_extra_setting = reagirl.Checkbox_Add(nil, nil, "Extra", "A third checkbox?", true, nil)
checkbox_remember = reagirl.Checkbox_Add(108, nil, "Remember chosen setting", "Shall this setting be used as future default?", true, nil)
-- Images
reagirl.NextLine()
label_header4=reagirl.Label_Add(nil, nil, "Or Images", "Set the settings, as you wish.", false, nil)
-- local some images from Reaper
reagirl.NextLine()
image1 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/idea.png", "An idea-cloud", "An image of an idea-thought-cloud.", nil)
image2 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/envelope.png", "An envelope", "An image of an envelope.", nil)
image3 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/ff.png", "ff notation symbol", "An image of a ff-notation-symbol.", nil)
image4 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/folder_up.png", "A folder up", "An image of a folder with an arrow pointing up.", nil)
image5 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/bass_clef.png", "A bass clef", "An image of a bass clef.", nil)
-- Buttons
reagirl.NextLine(10)
button_ok_id = reagirl.Button_Add(205, nil, 0, 0, "OK", "Apply changes and close dialog.", nil)
button_cancel_id = reagirl.Button_Add(nil, nil, 0, 0, "Cancel", "Discard changes and close dialog.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.")
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
When you run this gui, you see gazillions of ui-elements placed onto this gui. And you also notice that it's hard to read.
You don't know, where one context starts and where another context ends. It's using labels to somewhat structure the gui but the labels simply drown in this multiverse of ui-elements.
It would be awesome to have a way to separate each context from each other: "Labels", "Inputboxes", "And some checkboxes" and "Or Images".
Behold, there's a way to do it, an attribute-function for labels, the function reagirl.Label_SetBackdrop().
With this function, you can create a backdrop for ui-elements. This backdrop is a boundary box that you surround around your ui-elements, beginning at a certain label.
The function accepts as first parameter the element_id of the label, where the backdrop shall start, then the width and height of it.
Let's add this function to draw a backdrop from the label "Labels" underneath the huge label "size".
-- load ReaGirl into this script
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- now let's add all ui-elements of the first tab to table tab1
-- Labels
label_header=reagirl.Label_Add(nil, nil, "Labels", "Possible labels with ReaGirl.", false, nil)
reagirl.NextLine()
label_regular=reagirl.Label_Add(nil, nil, "Example label", "A regular label.", false, nil)
label_clickable=reagirl.Label_Add(nil, nil, "Clickable label", "A clickable label.", true, nil)
label_styled=reagirl.Label_Add(nil, nil, "Styled label", "A styled label.", false, nil)
reagirl.Label_SetStyle(label_styled, 7, 2, 0) -- set to inverted and italic
reagirl.NextLine()
label_small=reagirl.Label_Add(nil, nil, "different", "Label with small font-size.", false, nil)
reagirl.Label_SetFontSize(label_small, 10) -- set to small font-size
label_medium=reagirl.Label_Add(nil, nil, "font", "Label with medium font-size.", false, nil)
reagirl.Label_SetFontSize(label_medium, 30) -- set to medium font-size
label_large=reagirl.Label_Add(nil, nil, "size", "Label with large font-size.", false, nil)
reagirl.Label_SetFontSize(label_large, 50) -- set to large font-size
label_medium_and_style=reagirl.Label_Add(nil, nil, "and style", "Label with medium font-size and some styles.", false, nil)
reagirl.Label_SetFontSize(label_medium_and_style, 30) -- set to medium font-size
reagirl.Label_SetStyle(label_medium_and_style, 2, 0, 0) -- set to italic
-- let it end underneath label_large - the largest label
reagirl.Label_SetBackdrop(label_header, 290, 80)
-- Inputboxes
reagirl.NextLine()
label_header2=reagirl.Label_Add(nil, nil, "Inputboxes", "Some demo-inputboxes.", false, nil)
reagirl.NextLine()
inputbox_name_of_setting = reagirl.Inputbox_Add(nil, nil, 280, "Name:", 90, "Type in here the name of the setting.", "No title", nil, nil)
reagirl.NextLine()
button_choose_file_id = reagirl.Button_Add(223, nil, 0, 0, "Choose file", "Choose a file.", nil) -- a button
-- Checkboxes
reagirl.NextLine()
label_header3=reagirl.Label_Add(nil, nil, "And some checkboxes", "Some checkboxes in ReaGirl.", false, nil)
reagirl.NextLine() -- first line of checkboxes
checkbox_mysetting = reagirl.Checkbox_Add(nil, nil, "My setting", "The first checkbox.", true, nil)
checkbox_another_setting = reagirl.Checkbox_Add(nil, nil, "Another setting", "The second checkbox.", true, nil)
reagirl.NextLine() -- second line of checkboxes
checkbox_extra_setting = reagirl.Checkbox_Add(nil, nil, "Extra", "A third checkbox?", true, nil)
checkbox_remember = reagirl.Checkbox_Add(108, nil, "Remember chosen setting", "Shall this setting be used as future default?", true, nil)
-- Images
reagirl.NextLine()
label_header4=reagirl.Label_Add(nil, nil, "Or Images", "Set the settings, as you wish.", false, nil)
-- local some images from Reaper
reagirl.NextLine()
image1 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/idea.png", "An idea-cloud", "An image of an idea-thought-cloud.", nil)
image2 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/envelope.png", "An envelope", "An image of an envelope.", nil)
image3 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/ff.png", "ff notation symbol", "An image of a ff-notation-symbol.", nil)
image4 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/folder_up.png", "A folder up", "An image of a folder with an arrow pointing up.", nil)
image5 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/bass_clef.png", "A bass clef", "An image of a bass clef.", nil)
-- Buttons
reagirl.NextLine(10)
button_ok_id = reagirl.Button_Add(205, nil, 0, 0, "OK", "Apply changes and close dialog.", nil)
button_cancel_id = reagirl.Button_Add(nil, nil, 0, 0, "Cancel", "Discard changes and close dialog.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.")
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
That looks much better, the upper part for labels is much easier to distinguish from the other parts.
In the code, we added reagirl.Label_SetBackdrop(label_header, 290, 80) which started the label from the label with the element_\id label_header and a backdrop-size of 290 by 80 pixels.
Let's add it for the other parts as well to see, if things get more easier to read with that.
-- load ReaGirl into this script
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- now let's add all ui-elements of the first tab to table tab1
-- Labels
label_header=reagirl.Label_Add(nil, nil, "Labels", "Possible labels with ReaGirl.", false, nil)
reagirl.NextLine()
label_regular=reagirl.Label_Add(nil, nil, "Example label", "A regular label.", false, nil)
label_clickable=reagirl.Label_Add(nil, nil, "Clickable label", "A clickable label.", true, nil)
label_styled=reagirl.Label_Add(nil, nil, "Styled label", "A styled label.", false, nil)
reagirl.Label_SetStyle(label_styled, 7, 2, 0) -- set to inverted and italic
reagirl.NextLine()
label_small=reagirl.Label_Add(nil, nil, "different", "Label with small font-size.", false, nil)
reagirl.Label_SetFontSize(label_small, 10) -- set to small font-size
label_medium=reagirl.Label_Add(nil, nil, "font", "Label with medium font-size.", false, nil)
reagirl.Label_SetFontSize(label_medium, 30) -- set to medium font-size
label_large=reagirl.Label_Add(nil, nil, "size", "Label with large font-size.", false, nil)
reagirl.Label_SetFontSize(label_large, 50) -- set to large font-size
label_medium_and_style=reagirl.Label_Add(nil, nil, "and style", "Label with medium font-size and some styles.", false, nil)
reagirl.Label_SetFontSize(label_medium_and_style, 30) -- set to medium font-size
reagirl.Label_SetStyle(label_medium_and_style, 2, 0, 0) -- set to italic
-- let it end underneath label_large - the largest label
reagirl.Label_SetBackdrop(label_header, 290, 80)
-- Inputboxes
reagirl.NextLine()
label_header2=reagirl.Label_Add(nil, nil, "Inputboxes", "Some demo-inputboxes.", false, nil)
reagirl.NextLine()
inputbox_name_of_setting = reagirl.Inputbox_Add(nil, nil, 280, "Name:", 90, "Type in here the name of the setting.", "No title", nil, nil)
reagirl.NextLine()
button_choose_file_id = reagirl.Button_Add(223, nil, 0, 0, "Choose file", "Choose a file.", nil) -- a button
reagirl.Label_SetBackdrop(label_header2, 290, 53)
-- Checkboxes
reagirl.NextLine()
label_header3=reagirl.Label_Add(nil, nil, "And some checkboxes", "Some checkboxes in ReaGirl.", false, nil)
reagirl.NextLine() -- first line of checkboxes
checkbox_mysetting = reagirl.Checkbox_Add(nil, nil, "My setting", "The first checkbox.", true, nil)
checkbox_another_setting = reagirl.Checkbox_Add(nil, nil, "Another setting", "The second checkbox.", true, nil)
reagirl.NextLine() -- second line of checkboxes
checkbox_extra_setting = reagirl.Checkbox_Add(nil, nil, "Extra", "A third checkbox?", true, nil)
checkbox_remember = reagirl.Checkbox_Add(108, nil, "Remember chosen setting", "Shall this setting be used as future default?", true, nil)
reagirl.Label_SetBackdrop(label_header3, 290, 55)
-- Images
reagirl.NextLine()
label_header4=reagirl.Label_Add(nil, nil, "Or Images", "Set the settings, as you wish.", false, nil)
-- local some images from Reaper
reagirl.NextLine()
image1 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/idea.png", "An idea-cloud", "An image of an idea-thought-cloud.", nil)
image2 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/envelope.png", "An envelope", "An image of an envelope.", nil)
image3 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/ff.png", "ff notation symbol", "An image of a ff-notation-symbol.", nil)
image4 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/folder_up.png", "A folder up", "An image of a folder with an arrow pointing up.", nil)
image5 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/bass_clef.png", "A bass clef", "An image of a bass clef.", nil)
reagirl.Label_SetBackdrop(label_header4, 290, 65)
-- Buttons
reagirl.NextLine(10)
button_ok_id = reagirl.Button_Add(205, nil, 0, 0, "OK", "Apply changes and close dialog.", nil)
button_cancel_id = reagirl.Button_Add(nil, nil, 0, 0, "Cancel", "Discard changes and close dialog.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.")
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
This is even better. Each part is now separated from the other one. However, some are a little crammed to each other, so let's add some space by setting a margin into the function reagirl.NextLine() right above the labels with a backdrop "And some checkboxes" and "Or Images".
That way, the gui "can breathe" a little bit better and give the user even more clear indication, where one section starts and where one section ends.
-- load ReaGirl into this script
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- now let's add all ui-elements of the first tab to table tab1
-- Labels
label_header=reagirl.Label_Add(nil, nil, "Labels", "Possible labels with ReaGirl.", false, nil)
reagirl.NextLine()
label_regular=reagirl.Label_Add(nil, nil, "Example label", "A regular label.", false, nil)
label_clickable=reagirl.Label_Add(nil, nil, "Clickable label", "A clickable label.", true, nil)
label_styled=reagirl.Label_Add(nil, nil, "Styled label", "A styled label.", false, nil)
reagirl.Label_SetStyle(label_styled, 7, 2, 0) -- set to inverted and italic
reagirl.NextLine()
label_small=reagirl.Label_Add(nil, nil, "different", "Label with small font-size.", false, nil)
reagirl.Label_SetFontSize(label_small, 10) -- set to small font-size
label_medium=reagirl.Label_Add(nil, nil, "font", "Label with medium font-size.", false, nil)
reagirl.Label_SetFontSize(label_medium, 30) -- set to medium font-size
label_large=reagirl.Label_Add(nil, nil, "size", "Label with large font-size.", false, nil)
reagirl.Label_SetFontSize(label_large, 50) -- set to large font-size
label_medium_and_style=reagirl.Label_Add(nil, nil, "and style", "Label with medium font-size and some styles.", false, nil)
reagirl.Label_SetFontSize(label_medium_and_style, 30) -- set to medium font-size
reagirl.Label_SetStyle(label_medium_and_style, 2, 0, 0) -- set to italic
-- let it end underneath label_large - the largest label
reagirl.Label_SetBackdrop(label_header, 290, 80)
-- Inputboxes
reagirl.NextLine()
label_header2=reagirl.Label_Add(nil, nil, "Inputboxes", "Some demo-inputboxes.", false, nil)
reagirl.NextLine()
inputbox_name_of_setting = reagirl.Inputbox_Add(nil, nil, 280, "Name:", 90, "Type in here the name of the setting.", "No title", nil, nil)
reagirl.NextLine()
button_choose_file_id = reagirl.Button_Add(223, nil, 0, 0, "Choose file", "Choose a file.", nil) -- a button
reagirl.Label_SetBackdrop(label_header2, 290, 53)
-- Checkboxes
reagirl.NextLine(8)
label_header3=reagirl.Label_Add(nil, nil, "And some checkboxes", "Some checkboxes in ReaGirl.", false, nil)
reagirl.NextLine() -- first line of checkboxes
checkbox_mysetting = reagirl.Checkbox_Add(nil, nil, "My setting", "The first checkbox.", true, nil)
checkbox_another_setting = reagirl.Checkbox_Add(nil, nil, "Another setting", "The second checkbox.", true, nil)
reagirl.NextLine() -- second line of checkboxes
checkbox_extra_setting = reagirl.Checkbox_Add(nil, nil, "Extra", "A third checkbox?", true, nil)
checkbox_remember = reagirl.Checkbox_Add(108, nil, "Remember chosen setting", "Shall this setting be used as future default?", true, nil)
reagirl.Label_SetBackdrop(label_header3, 290, 55)
-- Images
reagirl.NextLine(9)
label_header4=reagirl.Label_Add(nil, nil, "Or Images", "Set the settings, as you wish.", false, nil)
-- local some images from Reaper
reagirl.NextLine()
image1 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/idea.png", "An idea-cloud", "An image of an idea-thought-cloud.", nil)
image2 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/envelope.png", "An envelope", "An image of an envelope.", nil)
image3 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/ff.png", "ff notation symbol", "An image of a ff-notation-symbol.", nil)
image4 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/folder_up.png", "A folder up", "An image of a folder with an arrow pointing up.", nil)
image5 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/bass_clef.png", "A bass clef", "An image of a bass clef.", nil)
reagirl.Label_SetBackdrop(label_header4, 290, 65)
-- Buttons
reagirl.NextLine(10)
button_ok_id = reagirl.Button_Add(205, nil, 0, 0, "OK", "Apply changes and close dialog.", nil)
button_cancel_id = reagirl.Button_Add(nil, nil, 0, 0, "Cancel", "Discard changes and close dialog.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.")
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Ah, now each part is separated from the other one properly and has some margin, so the gui "can breathe" a little.
So if you compare the first example in this tutorial with the final one, you see a huge improvement in readability.
In this tutorial, I used autopositioning for the most ui-elements, but if you position the ui-elements "manually" by giving exact positions, the same things are valid.
Use reagirl.Label_SetBackdrop() to create backdrops around ui-elements that you want to be seen together, usually the ones from the same context.
And give some space between each of these backdrops, so each section is obviously separated from each other. This will make it easier for users to quickly see each section, glancing at the label of each section to get, if the section is important right now and ignore everything that is unimportant.
Also keep in mind: don't add other ui-elements at the same y-position as the label that shall start a backdrop. Otherwise your ui-element will have a line right through it from the backdrop.
In the next tutorial, I will show you, how to do the same with autopositioning. So if you can't place the backdrops by eye, you still have an option to structure your gui better.
In this tutorial, I will show you, how to structure guis with autopositioning.
We will do the same as in the tutorial before: add backdrops around ui-elements to create sections in your gui and add spaces between these sections to make it easier to separate each section from each other by the user.
As a reminder: Backdrops are additional elements for labels. They are a rectangle that starts at half the height of the label and goes around until it ends again on the other side of the label.
You can imagine this as a rectangle, that has a gap on the top at which the label-text is placed. The bottom ends under the last ui-elements of a section of ui-elements.
Backdrops enclose ui-elements to make it easier to see, which ui-elements belong to each other.
Let's start with the same crowded gui from the previous tutorial.
-- load ReaGirl into this script
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- now let's add all ui-elements of the first tab to table tab1
-- Labels
label_header=reagirl.Label_Add(nil, nil, "Labels", "Possible labels with ReaGirl.", false, nil)
reagirl.NextLine()
label_regular=reagirl.Label_Add(nil, nil, "Example label", "A regular label.", false, nil)
label_clickable=reagirl.Label_Add(nil, nil, "Clickable label", "A clickable label.", true, nil)
label_styled=reagirl.Label_Add(nil, nil, "Styled label", "A styled label.", false, nil)
reagirl.Label_SetStyle(label_styled, 7, 2, 0) -- set to inverted and italic
reagirl.NextLine()
label_small=reagirl.Label_Add(nil, nil, "different", "Label with small font-size.", false, nil)
reagirl.Label_SetFontSize(label_small, 10) -- set to small font-size
label_medium=reagirl.Label_Add(nil, nil, "font", "Label with medium font-size.", false, nil)
reagirl.Label_SetFontSize(label_medium, 30) -- set to medium font-size
label_large=reagirl.Label_Add(nil, nil, "size", "Label with large font-size.", false, nil)
reagirl.Label_SetFontSize(label_large, 50) -- set to large font-size
label_medium_and_style=reagirl.Label_Add(nil, nil, "and style", "Label with medium font-size and some styles.", false, nil)
reagirl.Label_SetFontSize(label_medium_and_style, 30) -- set to medium font-size
reagirl.Label_SetStyle(label_medium_and_style, 2, 0, 0) -- set to italic
-- let it end underneath label_large - the largest label
-- Inputboxes
reagirl.NextLine()
label_header2=reagirl.Label_Add(nil, nil, "Inputboxes", "Some demo-inputboxes.", false, nil)
reagirl.NextLine()
inputbox_name_of_setting = reagirl.Inputbox_Add(nil, nil, 280, "Name:", 90, "Type in here the name of the setting.", "No title", nil, nil)
reagirl.NextLine()
button_choose_file_id = reagirl.Button_Add(223, nil, 0, 0, "Choose file", "Choose a file.", nil) -- a button
-- Checkboxes
reagirl.NextLine()
label_header3=reagirl.Label_Add(nil, nil, "And some checkboxes", "Some checkboxes in ReaGirl.", false, nil)
reagirl.NextLine() -- first line of checkboxes
checkbox_mysetting = reagirl.Checkbox_Add(nil, nil, "My setting", "The first checkbox.", true, nil)
checkbox_another_setting = reagirl.Checkbox_Add(nil, nil, "Another setting", "The second checkbox.", true, nil)
reagirl.NextLine() -- second line of checkboxes
checkbox_extra_setting = reagirl.Checkbox_Add(nil, nil, "Extra", "A third checkbox?", true, nil)
checkbox_remember = reagirl.Checkbox_Add(108, nil, "Remember chosen setting", "Shall this setting be used as future default?", true, nil)
-- Images
reagirl.NextLine()
label_header4=reagirl.Label_Add(nil, nil, "Or Images", "Set the settings, as you wish.", false, nil)
-- local some images from Reaper
reagirl.NextLine()
image1 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/idea.png", "An idea-cloud", "An image of an idea-thought-cloud.", nil)
image2 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/envelope.png", "An envelope", "An image of an envelope.", nil)
image3 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/ff.png", "ff notation symbol", "An image of a ff-notation-symbol.", nil)
image4 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/folder_up.png", "A folder up", "An image of a folder with an arrow pointing up.", nil)
image5 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/bass_clef.png", "A bass clef", "An image of a bass clef.", nil)
-- Buttons
reagirl.NextLine()
button_ok_id = reagirl.Button_Add(205, nil, 0, 0, "OK", "Apply changes and close dialog.", nil)
button_cancel_id = reagirl.Button_Add(nil, nil, 0, 0, "Cancel", "Discard changes and close dialog.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.")
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Ooof, this is really hard to read. Let's create some backdrops to make it better, shall we?
Let us start with the backdrop for the first section that starts with the label "Labels" with the element_id "label_header".
In the previous tutorial, we defined specific coordinates for the backdrop. In this tutorial, we will specify a backdrop from a label to underneath a specific ui-element.
For this, we use the function reagirl.Label_AutoBackdrop(). This accepts as first parameter the label at which the backdrop starts and the element_id of a second ui-element, under which the backdrop shall end.
Let's do this for the first section called "Labels". We add reagirl.Label_AutoBackdrop() and let it start with the label "Labels" with the element_id label_header and end it underneath the label "and style" with the element_id label_medium_and_style.
-- load ReaGirl into this script
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- now let's add all ui-elements of the first tab to table tab1
-- Labels
label_header=reagirl.Label_Add(nil, nil, "Labels", "Possible labels with ReaGirl.", false, nil)
reagirl.NextLine()
label_regular=reagirl.Label_Add(nil, nil, "Example label", "A regular label.", false, nil)
label_clickable=reagirl.Label_Add(nil, nil, "Clickable label", "A clickable label.", true, nil)
label_styled=reagirl.Label_Add(nil, nil, "Styled label", "A styled label.", false, nil)
reagirl.Label_SetStyle(label_styled, 7, 2, 0) -- set to inverted and italic
reagirl.NextLine()
label_small=reagirl.Label_Add(nil, nil, "different", "Label with small font-size.", false, nil)
reagirl.Label_SetFontSize(label_small, 10) -- set to small font-size
label_medium=reagirl.Label_Add(nil, nil, "font", "Label with medium font-size.", false, nil)
reagirl.Label_SetFontSize(label_medium, 30) -- set to medium font-size
label_large=reagirl.Label_Add(nil, nil, "size", "Label with large font-size.", false, nil)
reagirl.Label_SetFontSize(label_large, 50) -- set to large font-size
label_medium_and_style=reagirl.Label_Add(nil, nil, "and style", "Label with medium font-size and some styles.", false, nil)
reagirl.Label_SetFontSize(label_medium_and_style, 30) -- set to medium font-size
reagirl.Label_SetStyle(label_medium_and_style, 2, 0, 0) -- set to italic
-- let it end underneath label_large - the largest label
reagirl.Label_AutoBackdrop(label_header, label_medium_and_style)
-- Inputboxes
reagirl.NextLine()
label_header2=reagirl.Label_Add(nil, nil, "Inputboxes", "Some demo-inputboxes.", false, nil)
reagirl.NextLine()
inputbox_name_of_setting = reagirl.Inputbox_Add(nil, nil, 280, "Name:", 90, "Type in here the name of the setting.", "No title", nil, nil)
reagirl.NextLine()
button_choose_file_id = reagirl.Button_Add(223, nil, 0, 0, "Choose file", "Choose a file.", nil) -- a button
-- Checkboxes
reagirl.NextLine()
label_header3=reagirl.Label_Add(nil, nil, "And some checkboxes", "Some checkboxes in ReaGirl.", false, nil)
reagirl.NextLine() -- first line of checkboxes
checkbox_mysetting = reagirl.Checkbox_Add(nil, nil, "My setting", "The first checkbox.", true, nil)
checkbox_another_setting = reagirl.Checkbox_Add(nil, nil, "Another setting", "The second checkbox.", true, nil)
reagirl.NextLine() -- second line of checkboxes
checkbox_extra_setting = reagirl.Checkbox_Add(nil, nil, "Extra", "A third checkbox?", true, nil)
checkbox_remember = reagirl.Checkbox_Add(108, nil, "Remember chosen setting", "Shall this setting be used as future default?", true, nil)
-- Images
reagirl.NextLine()
label_header4=reagirl.Label_Add(nil, nil, "Or Images", "Set the settings, as you wish.", false, nil)
-- local some images from Reaper
reagirl.NextLine()
image1 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/idea.png", "An idea-cloud", "An image of an idea-thought-cloud.", nil)
image2 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/envelope.png", "An envelope", "An image of an envelope.", nil)
image3 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/ff.png", "ff notation symbol", "An image of a ff-notation-symbol.", nil)
image4 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/folder_up.png", "A folder up", "An image of a folder with an arrow pointing up.", nil)
image5 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/bass_clef.png", "A bass clef", "An image of a bass clef.", nil)
-- Buttons
reagirl.NextLine()
button_ok_id = reagirl.Button_Add(205, nil, 0, 0, "OK", "Apply changes and close dialog.", nil)
button_cancel_id = reagirl.Button_Add(nil, nil, 0, 0, "Cancel", "Discard changes and close dialog.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.")
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Hmm, this is close to what we want but not perfect. Most of the labels are within the backdrop but the label "size" is only halfway in it.
This is due to the fact, that the we set the backdrop to a label that isn't the highest ui-element in that line "and style". However, we gave the label "size" a much bigger height than "and style" so we need to choose "size" as the ui-element, underneath it the backdrop ends.
Let's do this by altering reagirl.Label_AutoBackdrop() to use "label_large"(the element_id of label "size") instead.
-- load ReaGirl into this script
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- now let's add all ui-elements of the first tab to table tab1
-- Labels
label_header=reagirl.Label_Add(nil, nil, "Labels", "Possible labels with ReaGirl.", false, nil)
reagirl.NextLine()
label_regular=reagirl.Label_Add(nil, nil, "Example label", "A regular label.", false, nil)
label_clickable=reagirl.Label_Add(nil, nil, "Clickable label", "A clickable label.", true, nil)
label_styled=reagirl.Label_Add(nil, nil, "Styled label", "A styled label.", false, nil)
reagirl.Label_SetStyle(label_styled, 7, 2, 0) -- set to inverted and italic
reagirl.NextLine()
label_small=reagirl.Label_Add(nil, nil, "different", "Label with small font-size.", false, nil)
reagirl.Label_SetFontSize(label_small, 10) -- set to small font-size
label_medium=reagirl.Label_Add(nil, nil, "font", "Label with medium font-size.", false, nil)
reagirl.Label_SetFontSize(label_medium, 30) -- set to medium font-size
label_large=reagirl.Label_Add(nil, nil, "size", "Label with large font-size.", false, nil)
reagirl.Label_SetFontSize(label_large, 50) -- set to large font-size
label_medium_and_style=reagirl.Label_Add(nil, nil, "and style", "Label with medium font-size and some styles.", false, nil)
reagirl.Label_SetFontSize(label_medium_and_style, 30) -- set to medium font-size
reagirl.Label_SetStyle(label_medium_and_style, 2, 0, 0) -- set to italic
-- let it end underneath label_large - the largest label
reagirl.Label_AutoBackdrop(label_header, label_large)
-- Inputboxes
reagirl.NextLine()
label_header2=reagirl.Label_Add(nil, nil, "Inputboxes", "Some demo-inputboxes.", false, nil)
reagirl.NextLine()
inputbox_name_of_setting = reagirl.Inputbox_Add(nil, nil, 280, "Name:", 90, "Type in here the name of the setting.", "No title", nil, nil)
reagirl.NextLine()
button_choose_file_id = reagirl.Button_Add(223, nil, 0, 0, "Choose file", "Choose a file.", nil) -- a button
-- Checkboxes
reagirl.NextLine()
label_header3=reagirl.Label_Add(nil, nil, "And some checkboxes", "Some checkboxes in ReaGirl.", false, nil)
reagirl.NextLine() -- first line of checkboxes
checkbox_mysetting = reagirl.Checkbox_Add(nil, nil, "My setting", "The first checkbox.", true, nil)
checkbox_another_setting = reagirl.Checkbox_Add(nil, nil, "Another setting", "The second checkbox.", true, nil)
reagirl.NextLine() -- second line of checkboxes
checkbox_extra_setting = reagirl.Checkbox_Add(nil, nil, "Extra", "A third checkbox?", true, nil)
checkbox_remember = reagirl.Checkbox_Add(108, nil, "Remember chosen setting", "Shall this setting be used as future default?", true, nil)
-- Images
reagirl.NextLine()
label_header4=reagirl.Label_Add(nil, nil, "Or Images", "Set the settings, as you wish.", false, nil)
-- local some images from Reaper
reagirl.NextLine()
image1 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/idea.png", "An idea-cloud", "An image of an idea-thought-cloud.", nil)
image2 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/envelope.png", "An envelope", "An image of an envelope.", nil)
image3 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/ff.png", "ff notation symbol", "An image of a ff-notation-symbol.", nil)
image4 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/folder_up.png", "A folder up", "An image of a folder with an arrow pointing up.", nil)
image5 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/bass_clef.png", "A bass clef", "An image of a bass clef.", nil)
-- Buttons
reagirl.NextLine()
button_ok_id = reagirl.Button_Add(205, nil, 0, 0, "OK", "Apply changes and close dialog.", nil)
button_cancel_id = reagirl.Button_Add(nil, nil, 0, 0, "Cancel", "Discard changes and close dialog.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.")
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Ah, that looks much better. Now all labels are within the backdrop, since we end it underneath the biggest ui-element "size".
This will usually not be a problem happening in your practice, since only labels and images can be higher than other ui-elements. But keep in mind that you need to keep track, if between two reagirl.NextLine()-functioncalls you have higher ui-elements than usual.
If you do and you want to end a backdrop underneath them, use the highest ui-element as ending-point for the bottom border, which isn't neccessarily the last ui-element in the line!
Ok, let's add backdrops for the other sections too, shall we?
-- load ReaGirl into this script
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- now let's add all ui-elements of the first tab to table tab1
-- Labels
label_header=reagirl.Label_Add(nil, nil, "Labels", "Possible labels with ReaGirl.", false, nil)
reagirl.NextLine()
label_regular=reagirl.Label_Add(nil, nil, "Example label", "A regular label.", false, nil)
label_clickable=reagirl.Label_Add(nil, nil, "Clickable label", "A clickable label.", true, nil)
label_styled=reagirl.Label_Add(nil, nil, "Styled label", "A styled label.", false, nil)
reagirl.Label_SetStyle(label_styled, 7, 2, 0) -- set to inverted and italic
reagirl.NextLine()
label_small=reagirl.Label_Add(nil, nil, "different", "Label with small font-size.", false, nil)
reagirl.Label_SetFontSize(label_small, 10) -- set to small font-size
label_medium=reagirl.Label_Add(nil, nil, "font", "Label with medium font-size.", false, nil)
reagirl.Label_SetFontSize(label_medium, 30) -- set to medium font-size
label_large=reagirl.Label_Add(nil, nil, "size", "Label with large font-size.", false, nil)
reagirl.Label_SetFontSize(label_large, 50) -- set to large font-size
label_medium_and_style=reagirl.Label_Add(nil, nil, "and style", "Label with medium font-size and some styles.", false, nil)
reagirl.Label_SetFontSize(label_medium_and_style, 30) -- set to medium font-size
reagirl.Label_SetStyle(label_medium_and_style, 2, 0, 0) -- set to italic
-- let it end underneath label_large - the largest label
reagirl.Label_AutoBackdrop(label_header, label_large)
-- Inputboxes
reagirl.NextLine()
label_header2=reagirl.Label_Add(nil, nil, "Inputboxes", "Some demo-inputboxes.", false, nil)
reagirl.NextLine()
inputbox_name_of_setting = reagirl.Inputbox_Add(nil, nil, 280, "Name:", 90, "Type in here the name of the setting.", "No title", nil, nil)
reagirl.NextLine()
button_choose_file_id = reagirl.Button_Add(223, nil, 0, 0, "Choose file", "Choose a file.", nil) -- a button
reagirl.Label_AutoBackdrop(label_header2, button_choose_file_id)
-- Checkboxes
reagirl.NextLine()
label_header3=reagirl.Label_Add(nil, nil, "And some checkboxes", "Some checkboxes in ReaGirl.", false, nil)
reagirl.NextLine() -- first line of checkboxes
checkbox_mysetting = reagirl.Checkbox_Add(nil, nil, "My setting", "The first checkbox.", true, nil)
checkbox_another_setting = reagirl.Checkbox_Add(nil, nil, "Another setting", "The second checkbox.", true, nil)
reagirl.NextLine() -- second line of checkboxes
checkbox_extra_setting = reagirl.Checkbox_Add(nil, nil, "Extra", "A third checkbox?", true, nil)
checkbox_remember = reagirl.Checkbox_Add(108, nil, "Remember chosen setting", "Shall this setting be used as future default?", true, nil)
reagirl.Label_AutoBackdrop(label_header3, checkbox_remember)
-- Images
reagirl.NextLine()
label_header4=reagirl.Label_Add(nil, nil, "Or Images", "Set the settings, as you wish.", false, nil)
-- local some images from Reaper
reagirl.NextLine()
image1 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/idea.png", "An idea-cloud", "An image of an idea-thought-cloud.", nil)
image2 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/envelope.png", "An envelope", "An image of an envelope.", nil)
image3 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/ff.png", "ff notation symbol", "An image of a ff-notation-symbol.", nil)
image4 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/folder_up.png", "A folder up", "An image of a folder with an arrow pointing up.", nil)
image5 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/bass_clef.png", "A bass clef", "An image of a bass clef.", nil)
reagirl.Label_AutoBackdrop(label_header4, image5)
-- Buttons
reagirl.NextLine()
button_ok_id = reagirl.Button_Add(205, nil, 0, 0, "OK", "Apply changes and close dialog.", nil)
button_cancel_id = reagirl.Button_Add(nil, nil, 0, 0, "Cancel", "Discard changes and close dialog.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.")
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
Ahh, much easier on the eyes. And all we added was four times a reagirl.Label_AutoBackdrop()-functioncall to the gui.
Now let's do one last step and add some space between each section, so the gui "can breathe" a little better.
For this we add some margin of 10 into the reagirl.NextLine()-functioncalls above the labels "Inputboxes", "And some checkboxes" and "And Images".
The first label "Labels" we don't move, since it's already at the top of the window.
-- load ReaGirl into this script
dofile(reaper.GetResourcePath().."/UserPlugins/reagirl.lua")
-- create new gui
reagirl.Gui_New()
-- now let's add all ui-elements of the first tab to table tab1
-- Labels
label_header=reagirl.Label_Add(nil, nil, "Labels", "Possible labels with ReaGirl.", false, nil)
reagirl.NextLine()
label_regular=reagirl.Label_Add(nil, nil, "Example label", "A regular label.", false, nil)
label_clickable=reagirl.Label_Add(nil, nil, "Clickable label", "A clickable label.", true, nil)
label_styled=reagirl.Label_Add(nil, nil, "Styled label", "A styled label.", false, nil)
reagirl.Label_SetStyle(label_styled, 7, 2, 0) -- set to inverted and italic
reagirl.NextLine()
label_small=reagirl.Label_Add(nil, nil, "different", "Label with small font-size.", false, nil)
reagirl.Label_SetFontSize(label_small, 10) -- set to small font-size
label_medium=reagirl.Label_Add(nil, nil, "font", "Label with medium font-size.", false, nil)
reagirl.Label_SetFontSize(label_medium, 30) -- set to medium font-size
label_large=reagirl.Label_Add(nil, nil, "size", "Label with large font-size.", false, nil)
reagirl.Label_SetFontSize(label_large, 50) -- set to large font-size
label_medium_and_style=reagirl.Label_Add(nil, nil, "and style", "Label with medium font-size and some styles.", false, nil)
reagirl.Label_SetFontSize(label_medium_and_style, 30) -- set to medium font-size
reagirl.Label_SetStyle(label_medium_and_style, 2, 0, 0) -- set to italic
-- let it end underneath label_large - the largest label
reagirl.Label_AutoBackdrop(label_header, label_large)
-- Inputboxes
reagirl.NextLine(10)
label_header2=reagirl.Label_Add(nil, nil, "Inputboxes", "Some demo-inputboxes.", false, nil)
reagirl.NextLine()
inputbox_name_of_setting = reagirl.Inputbox_Add(nil, nil, 280, "Name:", 90, "Type in here the name of the setting.", "No title", nil, nil)
reagirl.NextLine()
button_choose_file_id = reagirl.Button_Add(223, nil, 0, 0, "Choose file", "Choose a file.", nil) -- a button
reagirl.Label_AutoBackdrop(label_header2, button_choose_file_id)
-- Checkboxes
reagirl.NextLine(10)
label_header3=reagirl.Label_Add(nil, nil, "And some checkboxes", "Some checkboxes in ReaGirl.", false, nil)
reagirl.NextLine() -- first line of checkboxes
checkbox_mysetting = reagirl.Checkbox_Add(nil, nil, "My setting", "The first checkbox.", true, nil)
checkbox_another_setting = reagirl.Checkbox_Add(nil, nil, "Another setting", "The second checkbox.", true, nil)
reagirl.NextLine() -- second line of checkboxes
checkbox_extra_setting = reagirl.Checkbox_Add(nil, nil, "Extra", "A third checkbox?", true, nil)
checkbox_remember = reagirl.Checkbox_Add(108, nil, "Remember chosen setting", "Shall this setting be used as future default?", true, nil)
reagirl.Label_AutoBackdrop(label_header3, checkbox_remember)
-- Images
reagirl.NextLine(10)
label_header4=reagirl.Label_Add(nil, nil, "Or Images", "Set the settings, as you wish.", false, nil)
-- local some images from Reaper
reagirl.NextLine()
image1 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/idea.png", "An idea-cloud", "An image of an idea-thought-cloud.", nil)
image2 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/envelope.png", "An envelope", "An image of an envelope.", nil)
image3 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/ff.png", "ff notation symbol", "An image of a ff-notation-symbol.", nil)
image4 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/folder_up.png", "A folder up", "An image of a folder with an arrow pointing up.", nil)
image5 = reagirl.Image_Add(nil, nil, 50, 50, reaper.GetResourcePath().."/Data/track_icons/bass_clef.png", "A bass clef", "An image of a bass clef.", nil)
reagirl.Label_AutoBackdrop(label_header4, image5)
-- Buttons
reagirl.NextLine(10)
button_ok_id = reagirl.Button_Add(205, nil, 0, 0, "OK", "Apply changes and close dialog.", nil)
button_cancel_id = reagirl.Button_Add(nil, nil, 0, 0, "Cancel", "Discard changes and close dialog.", nil)
-- open the new gui
reagirl.Gui_Open("My Dialog Name", false, "The dialog", "This is a demo dialog with settings for tool xyz.")
-- make the background grey
reagirl.Background_GetSetColor(true, 55, 55, 55)
function main()
-- a function that runs the gui-manage function in the background, so the gui is updated correctly
reagirl.Gui_Manage()
-- if the gui-window hasn't been closed, keep the script alive.
if reagirl.Gui_IsOpen()==true then reaper.defer(main) end
end
main()
And that's it. We added backdrops for each section of ui-elements, so it's easier to separate them from each other, using autopositioning and reagirl.Label_AutoBackdrop() and added space between each section with reagirl.NextLine(10).
For blind scripters: this can be confusing in the beginning, so get some feedback from sighted users for your scripts that use label-backdrops.
That way you can improve on potential wrongfully positioned backdrops. Once you got a grip on how to achieve this, you'll be able to do it without feedback too.
ReaGirl features a set of ui-elements that you can add to your gui and add functionality to them,
as of now buttons, labels, checkboxes, inputboxes, images, sliders, dropdownmenus and tabs.
You can also add to each ui-element a context-menu. You can also add a file-drop-zone, which allows you to drop files onto a certain ui-element.
For both, context-menus and drop zones, you can add run-functions that will be run when a context-menu is opened or files have been dropped.
Some ui-elements(images and labels) can even be made draggable.
In addition, there's a subset of ReaGirl-functions whose name begin with reagirl.UI_Element_ that allow influencing ui-elements, like reagirl.UI_Element_GetSetPosition() which allows getting and setting the position of a ui-element.
In the next chapters, I will go through all of these ui-elements and additional concepts and explain how they are intended.
Buttons are calls to action. They are rectangular shaped ui-elements with round edges that feature a short text(called a caption).
They can be pressed to start an action, like opening a dialog, running actions from the action list, closing a window, etc.
What they do is defined by what you write into the run-function.
To add a button to your gui, you use reagirl.Button_Add(). It has seven parameters.
With this, you can add a button to your gui, but there's more that you can do with a button.
Check the first tutorial "A basic gui" to see examples of guis with a buttons.
Checkboxes are toggle-states. They can be set to checked(on) or unchecked(off).
They are square and have a small yellow square in the middle when checked with a descriptive text(caption) next to it.
They are usually used when the user has the option to turn something on or off.
To add a checkbox, you use reagirl.Checkbox_Add(). It has six parameters.
There's more that you can do with checkboxes:
Check the first tutorial "A basic gui" to see examples of guis with checkboxes.
Labels are a way to show text in your gui. They are meant to explain things or
give your gui some structure, like explaining the basic context of some ui-elements as a header.
They can be copied to the clipboard using Ctrl+C.
You can also make a label clickable, so it behaves like a link on a webpage.
Labels can have newlines.
There are also functions to set a label draggable.
You add labels to your gui with reagirl.Label_Add(), which has six parameters.
There's more that you can do with labels:
reagirl.Label_SetBackdrop - sets the backdrop of a label, which could enclose ui-elements underneath it
reagirl.Label_GetBackdrop - gets the backdrop of a label, which could enclose ui-elements underneath it
reagirl.Label_AutoBackdrop - sets a backdrop from label to underneath a specific ui-element
reagirl.Label_SetStyle - allows setting different styles for the label, like underlined or bold
reagirl.Label_GetStyle - gets the currently set styles for a label
reagirl.Label_SetFontSize - sets the font-size of the label
reagirl.Label_GetFontSize - gets the currently set font-size of the label
reagirl.Label_SetAlignment - sets the alignment of the label-text, like centered or right aligned
reagirl.Label_GetAlignment - gets the current alignment for this label
reagirl.Label_SetDraggable - sets the label as draggable, including possible drag-destinations
reagirl.Label_GetDraggable - gets the current drag-state and the current drag-destinations
reagirl.UI_Element_GetSetCaption - gets or sets the label text
Check the first tutorial "A basic gui" to see examples of guis with labels.
Images show an image in the gui. They can be clicked and do things using the run-function.
Transparency in png-images will be obeyed, so you can have images with transparent background drawn into your gui.
Very important: the caption and the meaningOfUI_Element-parameters must describe the content of the image, so blind users know, what the image contains, as they can't see it.
Images can automatically scale from 1x to 8x.
To make this happen, add scaled versions of the image to the same folder as the source image and name them the following way:
image-filename.png - 1x-scaling
image-filename-2x.png - 2x-scaling
image-filename-3x.png - 3x-scaling
image-filename-4x.png - 4x-scaling
image-filename-5x.png - 5x-scaling
image-filename-6x.png - 6x-scaling
image-filename-7x.png - 7x-scaling
image-filename-8x.png - 8x-scaling
Means: add -2x before the extension for the 2x scaled image, add -3x before the extension for the 3x scaled image and so on.
Give Image_Add the 1x-scaled image as filename and when the user changes the scaling, ReaGirl will load the accompanying scaled image automatically.
If there's no scaled image available, ReaGirl will automatically revert to the original filename for 1x-scaling. So if there's only one image available, ReaGirl will only load that one.
You can have up to 1000 images per gui.
Images can be made draggable.
You can add images using Image_Add, which has eight parameters.
You can do more things with images:
See the tutorial "A basic Image Viewer" and the tutorial "Draggable Images" to know how to work with images.
Inputboxes are fields, into which you can type text. They consist of a caption and a square next to it, into which you enter the text.
Unlike other ui-elements, inputboxes allow for two run-functions: one for when the user typed a character into the inputbox and one for when the user hits enter.
You can add inputboxes with InputBox_Add, which has 9 parameters.
You can do more with inputboxes.
Check the examples in the subfolder Additional_Examples for examples of guis with inputboxes. Run the action "ReaGirl_OpenFolder_ExampleScripts.lua" to open the example-folder.
Drop down menus are menus that give the user a selection of options and they can choose one of them.
They are a caption and a box right next to it which can be clicked to select the option.
You can cycle through the options with the arrow-keys, home/end-key and mouse-wheel.
You can add a drop down menu to the gui using DropDownMenu_Add, which has 9 parameters.
You can do more things with drop down menus:
Check the fourth tutorial "Disabled UI-Elements" to see examples of guis with sliders.
Sliders are ui-elements that give the user a way to choose a number-value between a start-value and an end-value.
It has a line on which you have a circle that can be moved around. Depending on where the circle is, a certain value has been selected.
For instance: you can have a slider with a value-range from 1 to 10 and the user can move the circle to choose any value between 1 and 10.
You can also give a stepsize. So if you want the user to just choose integers, give a step-size of 1.
The slider can be altered using mousewheel, arrow keys, PgUp and PgDn as well as home/end-key
You can add sliders with reagirl.Slider_Add() which has 13 parameters.
You can do more things with sliders:
Check the fourth tutorial "Disabled UI-Elements" to see examples of guis with sliders.
Tabs allow you to structure ui-elements into categories. For instance, you can have all settings for setting one in one category(tab) and the ones for setting two in another category(tab).
And when you switch between the tabs, only the ui-elements associated with a specific tab are shown while the others are hidden.
With them, you can organize your guis better so people are not overwhelmed by one page with all ui-elements at once.
You can also put ui-elements outside of tabs so they are shown all the time.
Only one tab-ui-element per gui is allowed!
You add tabs to your gui using reagirl.Tabs_Add(), which has nine parameters.
There are more things you can do with tabs:
Check the second tutorial "Adding Tabs to a Gui" as well as the eight tutorial "Autopositioning with tabs" to see, how to code tabs.
Context menus are menus that are opened, when the user right-clicks a ui-element.
Any ui-element can have its own context-menu.
To add one to a ui-element, you need the element_id of the ui-element that shall get the menu as well as a run-function.
Then use the function reagirl.UI_Element_GetSet_ContextMenu()
It has four parameters.
It is a list of fields separated by | characters. Each field represents a menu item.
Fields can start with special characters:
# : grayed out
! : checked
> : this menu item shows a submenu
< : last item in the current submenu
Example:
"first item, followed by separator||!second item, checked|>third item which spawns a submenu|#first item in submenu, grayed out|<second and last item in submenu|fourth item in top menu")
Experiment with it to get a grip on how it works.
And that's it. Add a run-function for the context-menu, add a ui-element, use its element_id as parameter for reagirl.UI_Element_GetSet_ContextMenu() and fill out the other parameters.
Run the script and the ui-element has a context menu when right clicking it.
Check the third tutorial "A Basic Image Viewer with a file-drop-zone and a context menu" to see, how it looks in practice.
File Drop Zones are zones, where you can drag and drop files onto.
You can give a ui-element a file drop zone and when you did, you can drop files onto the corresponding ui-element.
For instance, you can show an image and define a drop zone for this image. And then you can drop files onto the image itself.
Accessibility users can use ctrl+shift+f to select a file to drop.
Every ui-element can have a file drop zone. You just need the element_id and a run-function for it.
Then use the function reagirl.UI_Element_GetSet_DropZoneFunction() to add a file drop zone to a ui-element.
The run-function will get two parameters passed:
First, the element_id of the ui-element, onto which the files were dropped.
Second, a table with all filenames that were dropped.
It also supports dropping of fx from inside Reaper to it. In that case, the filename starts with @fx:
Check the third tutorial "A Basic Image Viewer with a file-drop-zone and a context menu" to see, how it looks in practice.
And that's all you need to add file drop zones to ui-elements.
ReaGirl includes a variety of basic functions to influence various attributes of UI-elements, Guis and the gui-window.
Here's a list of all of them. You've seen some of them already in the tutorials.
Functions for UI-elements:
Functions for the Gui:
Functions for the gui-window:
Function to access a ReaGirl-gui-window from other scripts:
Most of them are probably not needed by you but some you may need them from time to time.
ReaGirl is designed to make accessibility as easy as possible. For instance, screen reader messages are sent automatically. So blind users always know, what is happening on your gui.
Screen readers are usually a speech synthesis engine, that reads out, which ui-element is currently selected, which setting, etc.
This helps blind users to know, where in a gui they are, since the screen reader is reading it to them.
ReaGirl takes care of this as good as possible.
ReaGirl also takes care of keyboard-navigation, since blind users usually use the keyboard to use your gui.
However, there are some areas, where ReaGirl can't do the work for you and that need to be taken into consideration by you.
But don't worry, it's easier than you think. Once you digested the rules, most of your guis will be easy to use by blind users.
Blind users use guis generally using the keyboard. They select the ui-element using tab or shift+tab.
They "click" ui-elements using the space-bar or the enter-key.
And they choose the current selection of a drop down menu or a slider using cursor-keys, PgUp, PgDn, Home and End.
This has some implications on how to place ui-elements. For instance:
Put ui-elements of a certain context next to each other.
This is important! In your script, it means that the _Add-functions that belong to each other should follow each other.
Imagine an inputbox that shows a filename and a button that opens up a file-selection-dialog.
The inputbox and the button are probably placed next to each other in your gui. And they need to be placed
next to each other in your script, means: reagirl.InputBox_Add() first, then reagirl.Button_Add() as the next ui-element.
And when you use your gui and choose the ui-element using the tab-key, your inputbox will be selected first and the button as the next ui-element.
Make this logical. Blind users can't see, where an important ui-element is placed, so they usually expect, that the next logical ui-element they tab to follows the current one.
Or in our example: the inputbox first and when hitting the tab key again, the file-selection-button is selected next.
This might be a challenge at first, especially when you are not used to gui-coding yet. But you are going to get used to it quickly.
Oh, before I forget: when you rearrange ui-elements in your gui, don't only change the coordinates, but also think, if the order of the ui-elements visually is still the same as the order when going through them using the tab key.
So after the change, go through them using the tab key to see, if the order is still logical.
Rule of thumb is: when tabbing through the ui-elements, you go from left to right and from up to down, just like text in an editor.
If tabbing through the ui-elements doesn't follow this scheme, change the order of the _Add-functions in your script until they are correct.
This way, you make the gui easier to use by blind people.
Put the most important ui-elements first, then the less important ones, then the least important ones.
As I said, blind people usually use the tab-key and shift+tab to select the ui-elements in your gui.
When the gui-window is opened, the first ui-element is selected.
And here's why it's important to place the most important first:
When you need to enter your gui time and again to change settings, you want to access the most important ui-elements as quickly as possible.
If you need to hit tab thousands of times to get to the important ui-elements, it costs time and is very annoying.
So make a wise order of ui-elements so going through them using tab is as fast as possible.
If you have two sets of ui-elements that should be reached as quickly as possible, put the one set at the beginning and the other at the end of your gui.
That way you quickly reach the first ones using tab and the last ones using shift+tab.
Add text to meaningOfUI_Element-parameters, which is a short explanation and some context on what the ui-element is supposed to do.
This is very important! Setting meaningOfUI_Element-parameters fills up the tooltips. But it is also the text sent to users of screen readers.
You can imagine this the following:
If a blind user uses tab to select a checkbox, they get read out checkbox-caption, current checkstate and meaningOfUI_Element.
That way they know, how the checkbox is named(caption), if it's currently checked or not(checkstate) and what the checkbox is supposed to do and what happens if they change the checkstate(meaningOfUI_Element).
Make it as long as needed and as short as possible. Don't be afraid to rework it a few times to get it more precisely.
Rule of thumb: answer the questions What does the ui-element, how does it and why does it, as well as what happens when the ui-element is used. With these questions answered, you usually write a good meaningOfUI_Element.
If you need inspiration, open up the preferences of Reaper and hover above the ui-elements.
At the bottom of the window, there's a short explanation on what the ui-element is supposed to do(which is also sent to screen reader users).
This gives you an idea on how to write them.
If you need to write #Hashtags, write them camel case. Like #ThisIsBrilliant or #TheMightyBoosh. Screen readers will read out each word individually
that way, as they see each uppercase letter as start of a new word. If you write them all lowercase or uppercase, screen readers will try to read the hashtag as one word which could be a mumbling mess.
If you need to write acronyms, write them UPPERCASE. That way, screen readers will read each letter individually.
Sometimes, you do things in your script that you might want to communicate to blind users. For instance, if you store a setting everytime a checkbox is toggled, you should communicate this as blind users don't neccessarily know, what you did. In that case, use reagirl.ScreenReader_SendMessage() to send a message to screen reader users. So in our case, reagirl.ScreenReader_SendMessage("Setting stored") or something similar would be suitable to communicate this to blind users clearly. Don't use reaper.osara_outputMessage() since ReaGirl might interfere with this function and interrupt your sent message. Always use reagirl.ScreenReader_SendMessage() if you want to send screen reader messages, as ReaGirl will send them always this way. One thing to mention too: don't send messages permanently, only when needed. If you sent a message while the former one is still being read to blind users, the old message will be interrupted with the new one. So blind people might not get all information that you tried to convey.
Ask for feedback, especially by blind users. They know best, if your explanations are understandable or hard to get.
There's a mailing list called Reaper without Peepers, which is primarily a mailinglist by blind people for blind people.
Ask there for people who could test your script and give you feedback.
They have the most experience in what a good description is and what is a bad one, so pay attention and listen.
With these rules, most of your guis should be easy to use by blind people. The rest I intend to do automagically with ReaGirl.
One thing to note: in the ReaGirl-settings, there's an option under accessibility called "Show screen reader messages in console(debug)".
When you turn this option on, ReaGirl will put all screen reader-messages of ReaGirl-guis into the ReaScript-console-window.
That way you can peek, how other ReaGirl-guis write screen reader-messages or how things are communicated to blind people.
Looking at this for some time helps getting an idea on how blind users perceive guis.
You can also install the Osara-extension and NVDA(or JAWS) to get the full experience.
Osara sends screen reader messages to the operating system with the help of NVDA(or JAWS).
You install Osara and either NVDA(or JAWS). Then you start NVDA(or JAWS).You computer will read now every ui-element you are hovering above with the mouse and each ui-element that you are selecting using tab or shift+tab.
This can be overwhelming at first, so you can make it silent in the preferences and show a speech viewer instead, which gives you all screen reader messages as well, not just the ones from ReaGirl-guis.
Read about how to use NVDA(or JAWS), speech viewer and it's preferences and how to use it.
It's usually not needed to install Osara and NVDA(or JAWS) for ReaGirl scripts, but if you intend to make non-ReaGirl-scripts accessible, you'll need both to get these things right.
Especially, since Osara includes a function called reaper.osara_outputMessage(), that outputs messages to the screen reader. A function that I use in ReaGirl extensively(but use reagirl.ScreenReader_SendMessage() instead in your ReaGirl-guis!).
ReaGirl has some features, that you can use as a blind scripter to code your own guis.
The most important thing is auto-positioning, which allows you to position ui-elements automatically.
It's comparable with writing text: you can place each ui-element after each other or you place it in the next line.
To place ui-elements automatically, set the x and y-position-parameters of the _Add-functions to nil.
This will place the ui-element to the right side of the previous ui-element.
If you want to place the next ui-element into the next line, use reagirl.NextLine(), which is like the Enter-key in an editor, that starts a new line. So the next ui-element is positioned on the left side underneath the last one(like in a newly started line).
Read the tutorial about autopositioning and tutorial about autopositioning with tabs to learn how it works.
There are some things you should consider to also make the gui easy to use for sighted users:
1. Don't put too many ui-elements after each other, as this means you need to scroll horizontally to reach the ui-elements.
This is usually not a problem for you, as you are navigating with the tab-key, which automatically scrolls the tabbed ui-element into view.
However, sighted users usually work with mouse and they can become easily annoyed when having to scroll horizontally to see the last ui-element in a line.
So don't add more than five buttons, three checkboxes, one inputbox, one slider or one drop down menu after each other, to keep the gui conveniently usable.
When in doubt, better start a new line where you put the next ui-element. Scrolling up and down to get the next ui-element into view is usually more accepted than scrolling left and right.
When using labels, don't write a wall of text. From my experience, blind users use paragraphs in text much less often than sighted people, which can become difficult to read, especially for neurodivergent people.
So when you need to write a lot of text into a label, think about, when one point ends and when the next point starts.
Add two newlines between them. That way sighted people can digest your text much easier.
The reason for it is that sighted people tend to "scan" the text loosely for the most important things, like striving through the text for important words that their eyes will be hooked at, which determines if a text is important right now or not.
And they usually see each paragraph as one piece of knowledge to digest.
So adding newlines between each point you are making makes it faster for sighted people to scan your text for the most important information.
This point is basically the same advice I gave you about text in labels, but this time it's for ui-elements:
Put all ui-elements that are belonging to each other close to each other.
And when all ui-elements of one context are added, use two reagirl.NextLine()-functioncalls. This will add a, for lack of a better term, newline between ui-elements.
So you have one pack of ui-elements of one context, then some gap by a new line, then the next pack of ui-elements of the next context, etc.
That way you prevent crowded guis that can be hard to read.
Just as sighted people scan text, they also scan guis for the most important elements, so by adding newlines using two reagirl.NextLine()-functioncalls,
you make the gui easier to read, as it's faster to skip the unimportant parts in this moment.
Use label-backdrops to structure your gui. With them you can avoid even more walls of ui-elements.
Label-backdrops are rectangles that originate in a label and who enclose ui-elements. So sighted users easily see, which ui-element belongs to a certain context(like a label describing the context) and which one doesn't.
I wrote a tutorial on how to structure guis with label-backdrops.
This is probably a little difficult in the beginning, since it's hard to know, if you did it right, even though the functions used try to minimize potential errors as much as possible.
So when getting into label-backdrops, get feedback by sighted users to improve on potential issues.
When doing a gui that shall also be used by sighted people, don't hesitate to ask for feedback. That way you can avoid crowded walls of texts or walls of ui-elements.
Autopositioning should give you everything you need to make them work and label-backdrops to improve readability but there are still cases, where some feedback is still very valuable to get a gui right.
When you read every chapter of this documentation, you have a good idea of what ReaGirl can do for you and how.
But there's even more possible. So read about them in the functions-reference, where I explain in great detail every available function, their parameters and return values.
Great thanks to the development-team of Osara(especially James) without their extension the ReaGirl-project wouldn't be possible at all.
Also thanks to Scott and Chris, who helped me iron out accessibility-issues in the beta-phase.
Big thanks also to the various gui-lib-developers over the years like Lokasenna(Lokasennas Gui-Lib v2 and Scythe), Chris Fillion(ReaImGui) and Tack(Gui Toolkit),
whose gui-libraries were a huge inspiration for various concepts available in ReaGirl. Their influence made ReaGirl as easy to use as it is.
And also a big shoutout to the dozens of scripters in the Reaper-Community, whose help over the years made me a better scripter and therefore able to do this huge project.
I'm standing on the shoulders of giants.
And that's it for now. Enjoy coding guis with ReaGirl and give feedback and bugreports if you have any(see introduction for the Reaper-forum's thread.
Cheers and see you in the next version of ReaGirl
Meo-Ada Mespotine, 6th of May 2024
Automatically generated by Ultraschall-API 5 - 30 elements available |