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.
You'll also need a new XML description file. These are found in the indra/newview/skins/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 "llvieweruictrlfactory.h" // builds floaters from XML // Statics LLFloaterFoo* LLFloaterFoo::sInstance = NULL; LLFloaterFoo::LLFloaterFoo() : LLFloater("floater_foo") { gUICtrlFactory->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; LLString 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.