If you're still bound and determined to use one of the core Widget types as a base class, or simply have no other choice, it is crucial that you select the correct one. Here are the list of core base Widget classes, and some brief descriptions about them. Hopefully this will make your decision a little easier.
00001 #include "OpenGUI.h" // we obviously need this 00002 00003 using namespace OpenGUI; // bring OpenGUI into this scope 00004 00005 class MyWidget : public Control { 00006 public: 00007 MyWidget(); 00008 virtual ~MyWidget(); 00009 00010 // Property: MyBool 00011 void setMyBool( bool value ); // set mMyBool 00012 bool getMyBool(); // get mMyBool 00013 00014 // Event: MyEvent 00015 void eventMyEvent(); // event processor for MyEvent 00016 virtual void onMyEvent(Object* sender, EventArgs& args); // event handler for MyEvent 00017 00018 virtual void onDraw(Object* sender, Draw_EventArgs& args); // override onDraw event 00019 private: 00020 bool mMyBool; 00021 };
include on line 1. The "using namespace OpenGUI;" (line 3) is just so that we don't need to constantly prepend "OpenGUI::" to every OpenGUI specific type. We could just as easily do the prepending, or wrap this entire class in a namespace scope such as: #include "OpenGUI.h" namespace OpenGUI { /* Class definition in here */ }
#include "custom_widget.h" // using namespace OpenGUI is performed within header // local ObjectProperty to handle MyBool class MyWidget_MyBool_ObjectProperty : public ObjectProperty { public: virtual const char* getAccessorName() { return "MyBool"; } virtual void get( Object& objectRef, Value& valueOut ) { try { MyWidget& w = dynamic_cast<MyWidget&>( objectRef ); valueOut.setValue( w.getMyBool() ); } catch ( std::bad_cast e ) { OG_THROW( Exception::ERR_INVALIDPARAMS, "Bad Object Pointer", __FUNCTION__ ); } } virtual void set( Object& objectRef, Value& valueIn ) { try { MyWidget& w = dynamic_cast<MyWidget&>( objectRef ); w.setMyBool( valueIn.getValueAsBool() ); } catch ( std::bad_cast e ) { OG_THROW( Exception::ERR_INVALIDPARAMS, "Bad Object Pointer", __FUNCTION__ ); } } virtual Value::ValueType getPropertyType() { return Value::T_BOOL; } } gMyWidget_MyBool_ObjectProperty; // local ObjectAccessorList class MyWidget_ObjectAccessorList : public ObjectAccessorList { public: MyWidget_ObjectAccessorList() { addAccessor( &gMyWidget_MyBool_ObjectProperty ); } ~MyWidget_ObjectAccessorList() {} } gMyWidget_ObjectAccessorList; //#################################################### // MyWidget class implementation begins here // constructor MyWidget::MyWidget(){ // ensure our static property handler is ready to go if( gMyWidget_ObjectAccessorList.getParent() == 0 ) gMyWidget_ObjectAccessorList.setParent( Control::getAccessors() ); // create the event getEvents().createEvent( "MyEvent" ); // and then bind the default handler getEvents()["MyEvent"].add( new EventDelegate( this, &MyWidget::onMyEvent ) ); } // destructor MyWidget::~MyWidget() { /* nothing special necessary here */ } // native access functions for MyBool property void MyWidget::setMyBool( bool value ){ mMyBool = value; } bool MyWidget::getMyBool(){ return mMyBool; } // event processor void MyWidget::eventMyEvent(){ EventArgs myArgs; triggerEvent( "MyEvent", myArgs ); } // default event handler void MyWidget::onMyEvent(Object* sender, EventArgs& args){ /* do something important */ } // new onDraw implementation void MyWidget::onDraw(Object* sender, Draw_EventArgs& args){ //call base first so we draw over top of it Control::onDraw(sender, args); Brush& b = args.brush; // get a new reference to brush so it's easier to use b.pushColor( Color::PresetRed() ); // set the brush color to red b.Primitive.drawRect( getRect() ); // draw a rectangle using our position and size b.pop(); // pop off our previous color push, because pop'ing what you push is neighborly }
Its funny how the most initially obscure looking code is always first. ;) The following code exposes access to the "MyBool" property. This is done by creating a new class that derives from OpenGUI::ObjectProperty and performing the necessary virtual function implementations to achieve the desired effect. You should notice that within get() and set() we end up simply calling our native property accessor functions.
class MyWidget_MyBool_ObjectProperty : public ObjectProperty { public: virtual const char* getAccessorName() { return "MyBool"; } virtual void get( Object& objectRef, Value& valueOut ) { try { MyWidget& w = dynamic_cast<MyWidget&>( objectRef ); valueOut.setValue( w.getMyBool() ); } catch ( std::bad_cast e ) { OG_THROW( Exception::ERR_INVALIDPARAMS, "Bad Object Pointer", __FUNCTION__ ); } } virtual void set( Object& objectRef, Value& valueIn ) { try { MyWidget& w = dynamic_cast<MyWidget&>( objectRef ); w.setMyBool( valueIn.getValueAsBool() ); } catch ( std::bad_cast e ) { OG_THROW( Exception::ERR_INVALIDPARAMS, "Bad Object Pointer", __FUNCTION__ ); } } virtual Value::ValueType getPropertyType() { return Value::T_BOOL; } } gMyWidget_MyBool_ObjectProperty;
The next chunk of code is, once again, an in place definition and static creation rolled into one. This time it is for the OpenGUI::ObjectAccessorList ObjectAccessorList, which is a sort of property lookup hub. It groups all of the individual properties into a single object for lookup.
class MyWidget_ObjectAccessorList : public ObjectAccessorList { public: MyWidget_ObjectAccessorList() { addAccessor( &gMyWidget_MyBool_ObjectProperty ); } ~MyWidget_ObjectAccessorList() {} } gMyWidget_ObjectAccessorList;
And finally we get to the actual meat of our widget class. We're just not quite out of the "freaky" woods just yet though. In the first few lines of the class constructor, we need to make sure that our static ObjectAccessorList is property initialized by giving it the parent of our base class if it doesn't already have one. This allows failed lookups of property names to fall back to the next parent class. If we don't do this, we won't inherit the property accessors of our base class, and will either have to live with not having them, or implement brand new accessors ourselves. Since the code soup involved in making properties work is probably less than appealing at this point, we'll happily point our ObjectAccessorList to out base classes ObjectAccessorList.
MyWidget::MyWidget(){
// ensure our static property handler is ready to go
if( gMyWidget_ObjectAccessorList.getParent() == 0 )
gMyWidget_ObjectAccessorList.setParent( Control::getAccessors() );
// create the event
getEvents().createEvent( "MyEvent" );
// and then bind the default handler
getEvents()["MyEvent"].add( new EventDelegate( this, &MyWidget::onMyEvent ) );
}
On to the destructor. If your class requires special clean up actions, you can obviously perform them here. Our destructor does nothing special, so we'll move on.
// destructor MyWidget::~MyWidget() { /* nothing special necessary here */ }
The following 2 methods are the native property accessors. They were used in the property binding to access the internal state variable. You obviously don't need to use a state variable, and can happily generate the results purely pragmatically if you so desire. But since this is just an example, we took the easy way out.
// native access functions for MyBool property void MyWidget::setMyBool( bool value ){ mMyBool = value; } bool MyWidget::getMyBool(){ return mMyBool; }
And here's the event injector. Remember how I said it was mostly a convenience function? I wasn't kidding. OpenGUI uses event processors similar to this quite extensively to ensure we properly build event structs and don't typo the event name.
// event processor void MyWidget::eventMyEvent(){ EventArgs myArgs; triggerEvent( "MyEvent", myArgs ); }
And finally we reach the default event handler. As you can see, we're supposed to do something important here, but again, this is only an example so there's nothing to do.
// default event handler void MyWidget::onMyEvent(Object* sender, EventArgs& args){ /* do something important */ }
Last but not least, we have a new onDraw implementation. The onDraw method is the default event handler for draw operations and is initially exposed by the OpenGUI::Control Control class.
// new onDraw implementation void MyWidget::onDraw(Object* sender, Draw_EventArgs& args){ //call base first so we draw over top of it Control::onDraw(sender, args); Brush& b = args.brush; // get a new reference to brush so it's easier to use b.pushColor( Color::PresetRed() ); // set the brush color to red b.Primitive.drawRect( getRect() ); // draw a rectangle using our position and size b.pop(); // pop off our previous color push, because pop'ing what you push is neighborly }
The source for the base widget classes can be found in the following files:
Name, Library, and a pointer to a valid C callback style factory that creates your Widget via the new operator and returns the pointer. The Name of your widget can be just about anything, but you should obviously use a name that makes sense as this will be the name that is used to create your widget. The Library is a sort of group identifier. It exists so that widget libraries can register widgets that use fairly common names (like "Button") without much fear for name collisions (as those cause fatal exceptions). As a side effect, it also often provides a mechanism for grouping widgets together that came from the same widget library, but that convention isn't enforced so your mileage may vary. What you use for the Library identifier is up to you, but it is highly recommended that you use the same identifier for all widgets in the same library, and that it be something fairly original to prevent name collisions.