Adding a dialog
Windows and dialogs in Second Life are implemented as LLFloater objects. The class hierarchy is:
LLFloaterFoo - a "foo" dialog LLFloater - generic window or dialog with close box, minimize box, etc. LLPanel - rectangular area with dark background, has functions like childSetText(), childGetValue() LLUICtrl - any widget that can take keyboard focus LLView - base class, container for UI widget children
Create a new C++ header and source file to hold your new class. By convention these are called llfloaterfoo.h and .cpp. Place them in the newview directory.
Make the build system aware of your new files:
- Windows - add to Visual Studio "newview" project file
- Mac - add to macview.xcodeproj file
- Linux - add a line referencing the only the cpp file to newview/files.lst
You'll also need a new XML description file. These are found in the indra/newview/skins/default/xui/en-us directory. It's easiest to copy an existing file and modify it. Name the new file floater_foo.xml.
Important - Second Life UI uses OpenGL coordinates, so 0,0 is at the bottom left corner.
Let's make a floater that has a text label "bar", a text line input field "baz", and a button "hippo".
A basic floater header looks like this:
#include "llfloater.h" class LLFloaterFoo : public LLFloater { public: LLFloaterFoo(); virtual ~LLFloaterFoo(); // by convention, this shows the floater and does instance management static void show(void*); private: // when a line editor loses keyboard focus, it is committed. // commit callbacks are named onCommitWidgetName by convention. static void onCommitBaz(LLUICtrl* ctrl, void *userdata); // by convention, button callbacks are named onClickButtonLabel static void onClickHippo(void* userdata); // no pointers to widgets here - they are referenced by name //assuming we just need one, which is typical static LLFloaterFoo* sInstance; };
The XML data definition should look like this:
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <!-- All our XML is utf-8 encoded. --> <!-- Floaters can optionally have their titlebar drag handle on the left. If so, the title is not visible. When a floater is resizable, a min_width and min_height must be specified. --> <floater name="foo_floater" title="Foo" can_resize="true" can_minimize="true" can_close="true" can_drag_on_left="false" width="275" height="250" min_width="80" min_height="220" > <!-- By convention text fields are suffixed with _text. Common fonts include SansSerif and SansSerifSmall. See LLFontGL::fontFromName for more. Positions may be in absolute coordinates or as deltas. bottom_delta = -40 indicates 40 pixels from the top edge. follows indicates how the widget should move when the container is resized. --> <text name="bar_text" font="SansSerifSmall" left="10" bottom_delta="-40" width="260" height="16" follows="left|top" > bar is a generic programming identifier </text> <!-- Line editor names end in _edit by convention. mouse_opaque means it blocks mouse clicks, rather than passing on to underlying view max_length is in bytes, not Unicode characters --> <line_editor name="baz_edit" width="120" height="20" left="10" bottom_delta="-30" follows="left|bottom" hidden="false" mouse_opaque="true" max_length="31" font="SansSerif" bevel_style="in" border_style="line" border_thickness="1" select_all_on_focus_received="true" /> <!-- Buttons are suffixed with _btn by convention. Note there is no association with a callback function here - it must be connected in the C++ code. bottom_delta here means from the TOP of the last widget, not the bottom. --> <button name="hippo_btn" label="Hippo" font="SansSerifSmall" left="10" bottom_delta="-25" width="100" height="20" follows="bottom|left" /> </floater>
The C++ code for this floater looks like:
#include "llviewerprecompiledheaders.h" // must be first include #include "llfloaterfoo.h" #include "lluictrlfactory.h" // builds floaters from XML // Statics LLFloaterFoo* LLFloaterFoo::sInstance = NULL; LLFloaterFoo::LLFloaterFoo() : LLFloater(std::string("floater_foo")) { LLUICtrlFactory::getInstance()->buildFloater(this, "floater_foo.xml"); childSetCommitCallback("baz_edit", onCommitBaz, this); childSetAction("hippo_btn", onClickHippo, this); setDefaultBtn("hippo_btn"); } // static void LLFloaterFoo::show(void*) { if (!sInstance) sInstance = new LLFloaterFoo(); sInstance->open(); } LLFloaterFoo::~LLFloaterFoo() { sInstance=NULL; } // static void LLFloaterFoo::onCommitBaz(LLUICtrl* ctrl, void* userdata) { LLFloaterFoo* self = (LLFloaterFoo*)userdata; std::string text = self->childGetText("baz_edit"); llinfos << "baz contains: " << text << llendl; } // static void LLFloaterFoo::onClickHippo(void* userdata) { LLFloaterFoo* self = (LLFloaterFoo*)userdata; llinfos << "Hippo! from " << self->getName() << llendl; }
Finally, to test out this floater you'll need to add a menu item that calls the show() method. See Adding a menu item.
Hints:
You will need to add
#include "llfloaterfoo.h"
to the file llviewermenu.cpp. The show method is called with the following command:
LLFloaterFoo::show(NULL);
Put that command in the class LLToolsFoo you made in the previous example, Adding a menu item.