Skip to content

Instantly share code, notes, and snippets.

@Katharine
Created August 11, 2016 10:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Katharine/3b722cbb789dc7e9d037b1a5dcb57ef5 to your computer and use it in GitHub Desktop.
Save Katharine/3b722cbb789dc7e9d037b1a5dcb57ef5 to your computer and use it in GitHub Desktop.
Demo of old LSL UI proof of concept
// LSL UI test script with a draft of some helpers.
// All client->script communication occurs on this channel. Any channel is valid except DEBUG_CHANNEL and 0.
// Immediate response: ready [major] [minor]
// Delayed response: permission [granted]
// No further messages may be sent until permission is received with granted = TRUE.
// Requesting permission implicitly revokes any previous permissions, which will also silently close any
// open floaters.
UIRequestPermission(integer channel)
{
llOwnerSay("!suinit " + (string)channel + " Test post; please ignore.");
}
// Names for all elements and floaters must be unique; any non-unique name will be rejected.
// Any element may be a child of any element or floater; however, the behaviour of most such
// combinations is undefined. Floaters and panels can contain anything.
// All commands may return the following error if badly formed:
// badsyntax [command] [human readable message]
// All creation commands can return the following to indicate duplication:
// duplicate [command] [name]
// All commands referring to an existing element can return the following error if not found:
// notfound [command] [name]
// By convention, the title should be in ALL CAPS. This is not a requirement.
// If the floater is resizable, the given height and width are taken as minima.
UICreateFloater(string name, integer width, integer height, integer resizable, string title)
{
llOwnerSay("!floater " + name + " " + (string)width + " " + (string)height + " " + (string)resizable + " :" + title);
}
// The list layout in all methods is of the same format, consisting of alternating keys and values:
// [key, value, key, value, ...]
// The following keys are accepted (all are optional):
// width: int - width in pixels
// height: int - height in pixels
// top: int - distance from top of parent view
// bottom: int - distance from bottom of parent view (broken)
// right: int - distance from right of parent view (broken)
// follows: int - mask of the UI_FOLLOWS constants indicating which edges to track on resize (default: top|left)
// bottom_delta: int - distance of bottom from top of adjacent view (broken)
// top_delta: int - distance of top from bottom of adjacent view
// left_pad: int - left padding
// right_pad: int - right padding
// left_delta: int - distance of left from right of adjacent view
// font: string - name of font to use. Valid options are SansSerif, Monospace, DejaVu and Helvetica.
// font_size: string - font size. Valid options are Small, Medium, Large and Huge.
// font_style: string - font style. Valid options are BOLD, ITALIC and UNDERLINE. Styles may be combined using |, e.g. BOLD|ITALIC.
//
// All of these settings are optional and sane defaults are usually available.
// Combining foo and foo_delta is undefined.
// Only the top and left related positioning keys currently work as expected.
// Creates a button at with the given label
// Buttons will send the following message on click:
// commit [button_name]
UICreateButton(string name, string parent, string label, list layout)
{
llOwnerSay("!button " + name + " " + parent + " " + llDumpList2String(layout, " ") + " :" + label);
}
// Creates a text label with the specified content. If word_wrap is true it will wrap, if false
// it will truncate with an ellipsis.
UICreateText(string name, string parent, integer word_wrap, string content, list layout)
{
llOwnerSay("!text " + name + " " + parent + " " + (string)word_wrap + " " + llDumpList2String(layout, " ") + " :" + content);
}
// Creates a single-line input field.
// value - initial_value
// max_length - maximum length in bytes
// revert_on_sec - if TRUE, the content will revert to the last committed value on hitting sec
// spellcheck - enables spellcheck
// require_enter - if FALSE, commits when the field loses focus. If TRUE, only on pressing return/enter.
UICreateInput(string name, string parent, string value, integer max_length, integer revert_on_esc, integer spellcheck, integer require_enter, list layout)
{
llOwnerSay("!input " + name + " " + parent + " " + (string)max_length + " " + (string)revert_on_esc + " " + (string)spellcheck + " " + (string)(!require_enter) + " " + llDumpList2String(layout, " ") + " :" + value);
}
// Creates a panel for holding other elements.
// bevel_style - one of in, out, bright and none.
// border_width - the number of single-pixel lines drawn, between zero and two.
UICreatePanel(string name, string parent, integer border_width, string bevel_style, list layout)
{
llOwnerSay("!panel " + name + " " + parent + " " + (string)border_width + " " + bevel_style + " " + llDumpList2String(layout, " "));
}
UICreateSlider(string name, string parent, string label, string orientation, integer label_width, integer text_width, integer show_text, integer can_edit_text, integer decimal_digits, float default_value, float min_value, float max_value, float increment, list layout)
{
llOwnerSay("!slider " + llDumpList2String([name, parent, orientation, label_width, text_width, show_text, can_edit_text, decimal_digits, default_value, min_value, max_value, increment], " ") + " " + llDumpList2String(layout, " ") + " :" + label);
}
UICreateSwatch(string name, string parent, string label, integer immediate, vector default_colour, list layout)
{
llOwnerSay("!swatch " + name + " " + parent + " " + (string)immediate + " " + (string)default_colour + " " + llDumpList2String(layout, " ") + " :" + label);
}
// Sets the text for the element given, if the operation is sane.
// If the operation is not defined for the element given, the following is returned:
// invalid settext [name] [found_type]
// Do not rely on the content of found_type being useful. It is intended for debugging.
UISetText(string element, string text)
{
llOwnerSay("!settext " + element + " :" + text);
}
// These constants are for use with the `follows` layout key.
integer UI_FOLLOWS_NONE = 0x00;
integer UI_FOLLOWS_LEFT = 0x01;
integer UI_FOLLOWS_RIGHT = 0x02;
integer UI_FOLLOWS_TOP = 0x10;
integer UI_FOLLOWS_BOTTOM = 0x20;
integer UI_FOLLOWS_ALL = 0x33;
// Given a message from the client, returns a list with the command followed by each argument.
list UIParseMessage(string message)
{
if(llGetSubString(message, 0, 35) != llGetKey()) return [];
message = llDeleteSubString(message, 0, 36);
if(llGetSubString(message, 0, 0) != "!") return [];
message = llDeleteSubString(message, 0, 0);
integer colon = llSubStringIndex(message, ":");
string last_arg; // = ""
integer has_last_arg = ~colon;
if(has_last_arg)
{
if(colon != llStringLength(message) - 1)
{
last_arg = llGetSubString(message, colon+1, -1);
}
message = llDeleteSubString(message, colon, -1);
}
list args = llParseString2List(message, [" "], []);
if(has_last_arg)
{
args += last_arg;
}
return args;
}
// --------------------
// A test script.
FallbackMainMenu()
{
llDialog(llGetOwner(), "Choose an option", ["Test 1", "Test 2", "Test 3", "Text", "Colour", "Opacity"], FALLBACK_CHANNEL);
}
FallbackColourMenu()
{
llDialog(llGetOwner(), "Adjust the colour", ["R-", "G-", "B-", "R+", "G+", "B+", "Done"], FALLBACK_CHANNEL);
}
FallbackOpacityMenu()
{
llDialog(llGetOwner(), "Adjust the opacity", ["More", "Less", "Done"], FALLBACK_CHANNEL);
}
AdjustColour(vector delta)
{
vector c = llGetColor(0);
c += delta;
llSetColor(c, ALL_SIDES);
}
integer COMM_CHANNEL = -234532;
integer FALLBACK_CHANNEL = -265346;
default
{
state_entry()
{
llListen(COMM_CHANNEL, "", llGetOwner(), "");
}
touch_start(integer num)
{
if(llDetectedKey(0) == llGetOwner())
{
UIRequestPermission(COMM_CHANNEL);
llSetTimerEvent(1.0);
}
}
listen(integer channel, string name, key id, string message)
{
list args = UIParseMessage(message);
string command = llList2String(args, 0);
if(command == "ready") // Immediate response from requesting permissions.
{
llSetTimerEvent(0);
llSay(0, "Ready! Client version " + llList2String(args, 1) + "." + llList2String(args, 2));
}
else if(command == "permission") // Delayed response from requesting permissions
{
if(llList2Integer(args, 1)) // permission granted
{
state sui;
}
else
{
state dialogs;
}
}
}
timer()
{
state dialogs;
}
on_rez(integer n)
{
llResetScript();
}
}
state sui
{
state_entry()
{
llListen(COMM_CHANNEL, "", llGetOwner(), "");
llSay(0, "Permission granted!");
UICreateFloater("test", 200, 250, TRUE, "LSL TEST FLOATER");
UICreateButton("testbutton", "test", "Testing!", [
"top", 5,
"left", 5,
"width", 100
]);
UICreateButton("test2", "test", "Another test", [
"width", 100,
"top_delta", 0
]);
UICreateButton("test3", "test", "Stretching button", [
"width", 190,
"top_delta", 0,
"follows", UI_FOLLOWS_TOP | UI_FOLLOWS_LEFT | UI_FOLLOWS_RIGHT
]);
UICreateText("summary", "test", TRUE, "This floater was generated using LSL. Hooray!", [
"height", 35,
"top_delta", 10,
"left", 5,
"follows", UI_FOLLOWS_TOP | UI_FOLLOWS_LEFT | UI_FOLLOWS_RIGHT
]);
UICreateText("owner", "test", FALSE, "A link: secondlife:///app/agent/" + (string)llGetOwner() + "/about", [
"height", 10,
"top_delta", 5,
"left", 5,
"follows", UI_FOLLOWS_TOP | UI_FOLLOWS_LEFT | UI_FOLLOWS_RIGHT
]);
UICreateSlider("opacity", "test", "Opacity", "horizontal", FALSE, FALSE, TRUE, FALSE, 2, llGetAlpha(0), 0, 1, 0.05, [
"top_delta", 5,
"left", 5,
"follows", UI_FOLLOWS_TOP | UI_FOLLOWS_LEFT | UI_FOLLOWS_RIGHT
]);
UICreateSwatch("swatch", "test", "", TRUE, llGetColor(0), [
"top_delta", 5,
"left", 5,
"height", 50,
"width", 40
]);
UICreateInput("input", "test", "", 255, TRUE, TRUE, TRUE, [
"width", 190,
"height", 23,
"top", 225,
"follows", UI_FOLLOWS_BOTTOM | UI_FOLLOWS_LEFT | UI_FOLLOWS_RIGHT
]);
UICreatePanel("panel", "test", 1, "none", [
"width", 80,
"height", 30,
"top", 5,
"left", 110,
"follows", UI_FOLLOWS_TOP | UI_FOLLOWS_RIGHT
]);
UICreateText("paneltext", "panel", FALSE, "Panel!", [
"width", 80,
"height", 12,
"top", 0,
"left", 2,
"follows", UI_FOLLOWS_TOP | UI_FOLLOWS_LEFT,
"font", "SansSerif",
"font_size", "Huge",
"font_style", "BOLD"
]);
}
touch_start(integer n)
{
// Race condition because I'm lazy.
UIRequestPermission(COMM_CHANNEL);
state default;
}
listen(integer channel, string name, key id, string message)
{
list args = UIParseMessage(message);
string command = llList2String(args, 0);
if(command == "commit") // When some control is "committed"
{
string element = llList2String(args, 1);
if(element == "input")
{
llSetText(llList2String(args, 2), <1.0, 1.0, 1.0>, 1.0);
}
else if(element == "slider")
{
UISetText("summary", "Slider value: " + llList2String(args, 2));
}
else if(element == "opacity")
{
llSetAlpha(llList2Float(args, 2), ALL_SIDES);
}
else if(element == "swatch")
{
llSetColor((vector)llList2String(args, 2), ALL_SIDES);
}
else
{
UISetText(element, "Clicked!");
UISetText("summary", "Clicked button " + element);
}
}
// Error commands
else if(command == "badsyntax")
{
llSay(0, "Syntax error in '" + llList2String(args, 1) + "' command.");
llSay(0, llList2String(args, 2));
}
else if(command == "invalid")
{
llSay(0, "Attempt to perform invalid " + llList2String(args, 1) + " operation on " + llList2String(args, 2) + " (" + llList2String(args, 3) + ")");
}
else if(command == "notfound")
{
llSay(0, "Attempt in " + llList2String(args, 1) + " command to reference non-existent element " + llList2String(args, 2));
}
else
{
llSay(0, "Garbage message: " + llList2CSV(args));
}
}
on_rez(integer n)
{
llResetScript();
}
}
state dialogs
{
state_entry()
{
llListen(FALLBACK_CHANNEL, "", llGetOwner(), "");
FallbackMainMenu();
}
listen(integer channel, string name, key id, string message)
{
if(message == "Test 1")
{
llOwnerSay("You clicked Test 1!");
FallbackMainMenu();
}
else if(message == "Test 2")
{
llOwnerSay("You clicked Test 2!");
FallbackMainMenu();
}
else if(message == "Test 3")
{
llOwnerSay("You clicked Test 3!");
FallbackMainMenu();
}
else if(message == "Text")
{
state textbox;
}
else if(message == "Colour")
{
FallbackColourMenu();
}
else if(message == "Opacity")
{
FallbackOpacityMenu();
}
else if(message == "R+")
{
AdjustColour(<0.05,0,0>);
FallbackColourMenu();
}
else if(message == "R-")
{
AdjustColour(<-0.05,0,0>);
FallbackColourMenu();
}
else if(message == "G+")
{
AdjustColour(<0,0.05,0>);
FallbackColourMenu();
}
else if(message == "G-")
{
AdjustColour(<0,-0.05,0>);
FallbackColourMenu();
}
else if(message == "B+")
{
AdjustColour(<0,0,0.05>);
FallbackColourMenu();
}
else if(message == "B-")
{
AdjustColour(<0,0,-0.05>);
FallbackColourMenu();
}
else if(message == "More")
{
llSetAlpha(llGetAlpha(0) + 0.05, ALL_SIDES);
FallbackOpacityMenu();
}
else if(message == "Less")
{
llSetAlpha(llGetAlpha(0) - 0.05, ALL_SIDES);
FallbackOpacityMenu();
}
else if(message == "Done")
{
FallbackMainMenu();
}
}
touch_start(integer num)
{
FallbackMainMenu();
}
on_rez(integer n)
{
llResetScript();
}
}
state textbox
{
state_entry()
{
llListen(FALLBACK_CHANNEL, "", llGetOwner(), "");
llTextBox(llGetOwner(), "Enter some text: ", FALLBACK_CHANNEL);
}
listen(integer channel, string name, key id, string message)
{
llSetText(message, <1.0, 1.0, 1.0>, 1.0);
state dialogs;
}
touch_start(integer num)
{
state dialogs;
}
on_rez(integer n)
{
llResetScript();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment