Adding a dialog

From Second Life Wiki
Jump to: navigation, search

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.