Difference between revisions of "Adding a dialog"

From Second Life Wiki
Jump to navigation Jump to search
(deal with memory leak as inspired by llKelly)
(Fixed up the example code to work again after removal of the gUICtrlFactory global.)
 
(7 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{OSWikiLearnBox|parent=Viewer architecture}}
{{OSWikiLearnBox|parent=Viewer architecture}}
[[Category:Viewer Architecture]]
Windows and dialogs in Second Life are implemented as LLFloater objects.  The class hierarchy is:
Windows and dialogs in Second Life are implemented as LLFloater objects.  The class hierarchy is:


Line 10: Line 12:
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.
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.
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.
'''Important''' - Second Life UI uses OpenGL coordinates, so 0,0 is at the '''bottom''' left corner.
Line 34: Line 41:
     // when a line editor loses keyboard focus, it is committed.
     // when a line editor loses keyboard focus, it is committed.
     // commit callbacks are named onCommitWidgetName by convention.
     // commit callbacks are named onCommitWidgetName by convention.
     void onCommitBaz(LLUICtrl* ctrl, void *userdata);
     static void onCommitBaz(LLUICtrl* ctrl, void *userdata);
   
   
     // by convention, button callbacks are named onClickButtonLabel
     // by convention, button callbacks are named onClickButtonLabel
     void onClickHippo(void* userdata);
     static void onClickHippo(void* userdata);
   
   
     // no pointers to widgets here - they are referenced by name
     // no pointers to widgets here - they are referenced by name
Line 45: Line 52:


};
};
</pre>


The XML data definition should look like this:
The XML data definition should look like this:
Line 137: Line 146:
#include "llfloaterfoo.h"
#include "llfloaterfoo.h"


#include "llvieweruictrlfactory.h" // builds floaters from XML
#include "lluictrlfactory.h" // builds floaters from XML
 
// Statics
LLFloaterFoo* LLFloaterFoo::sInstance = NULL;


LLFloaterFoo::LLFloaterFoo()
LLFloaterFoo::LLFloaterFoo()
:  LLFloater("floater_foo")
:  LLFloater(std::string("floater_foo"))
{
{
     gUICtrlFactory->buildFloater(this, "floater_foo.xml");
     LLUICtrlFactory::getInstance()->buildFloater(this, "floater_foo.xml");


     childSetCommitCallback("baz_edit", onCommitBaz, this);
     childSetCommitCallback("baz_edit", onCommitBaz, this);
Line 159: Line 171:
}
}


LLFloaterFoo::LLFloaterFoo()
LLFloaterFoo::~LLFloaterFoo()
{
{
     sInstance=NULL;
     sInstance=NULL;
Line 168: Line 180:
{
{
     LLFloaterFoo* self = (LLFloaterFoo*)userdata;
     LLFloaterFoo* self = (LLFloaterFoo*)userdata;
     LLString text = self->childGetText("baz_edit");
     std::string text = self->childGetText("baz_edit");
     llinfos << "baz contains: " << text << llendl;
     llinfos << "baz contains: " << text << llendl;
}
}
Line 182: Line 194:


Finally, to test out this floater you'll need to add a menu item that calls the show() method.  See [[Adding a menu item]].
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 <pre>#include "llfloaterfoo.h"</pre> to the file llviewermenu.cpp.
The show method is called with the following command: <pre>LLFloaterFoo::show(NULL);</pre>
Put that command in the class LLToolsFoo you made in the previous example, [[Adding a menu item]].

Latest revision as of 06:41, 16 August 2008

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.