Adding a dialog

From Second Life Wiki
Revision as of 22:47, 16 February 2007 by Adz Childs (talk | contribs) (added hints to help with this example)
Jump to navigation Jump to 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.

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.