Object / Element / Area / Border / Widget

This class implements event handling, and introduces alignment and weight. It's the superclass of almost all the other gadget classes at one point or another : by setting an object Mode, you can make it behave like a button or like a toggle gadget. That's why there is no extra button class. A button is simply a Text object with a raised frame and a release input mode.

Since especially the Group class is a subclass of the Widget class, you can create rather complex buttons consisting of many other display elements.

States

Although the Area class defines four graphical states for objects, it doesn't manage them. The Widget class implements this managing through 3 attributes : Disabled, Pressed and Active, which correspond to the ghost, touch and focus states. The fourth state, neutral, is used when the object is neither disabled, pressed or active.

When these attributes are modified, the Widget class select which state to use, the attribute priority being Disabled, Pressed and Active. If the widget is already rendered, the SetState method is invoked, allowing classes to set their properties for the state. The Layout method is then invoked to rethink the widget's box. If the widget is drawable, and the widget was damaged during the SetState or the Layout methods it's redrawn.

These attributes don't only modify the graphical aspect of widgets, but also change their behaviour. For example a disabled widget cannot be activated and doesn't receive events.

Types

Attributes

Overrides

Implements

Macros

FWidgetPublic -- (01.00)

typedef struct FeelinWidgetPublic
{
    bits32                           Flags;
    uint16                           Weight;
    uint8                            Align;
}
FWidgetPublic;

Because the Object class uses an opaque structure to keep as much independence as possible from its subclasses, it's a terrible idea to make any assumption about the offset from the start of the object to this public data. In order to get a pointer to this structure you MUST use the PublicData attribute.

Members

Flags (bits32)

Weight (uint16)

The weight of an object influence the free space distribution during the layout. The more weight, the more space an object can get. By default, the weight of objects is set to 100, which can be modified at initialisation time using the Weight attribute. You can use the _widget_weight macro to get the weight of your object.

Align (uint8)

The alignment of the object relative to its container. By default, the alignment is set to FV_Widget_Align_Center. You can modify it at initialisation time using the Align attribute. You can use the _widget_align macro to get the alignement of your object.

Active -- (01.00) [ISG], bool32

Use this attribute to get and set the active state of an object.

This attribute is generaly modified when an object is part of a window cycle chain : it is set to TRUE when the object becomes the active one, and is set to FALSE when the object is no longer the active one.

Align -- (01.00) [I.G], keyword

Special values

FV_Widget_Align_Start
FV_Widget_Align_Center
FV_Widget_Align_End

Use this attribute to align objects that don't expand completely within their parent's box.

By default objects are centered.

Example

widget-align
<?xml version="1.0" ?>

<feelin:application>
    <Application Id="app">
        <Window Id="win" Title="Widget : Align" Open="true" Width="200px">
            <Group Orientation="vertical">
                <Widget style="background: red; min-height: 5px;" />
<Button Align="start" style="width: auto;">start</Button> <Button Align="center" style="width: auto;">center</Button> <Button Align="end" style="width: auto;">end</Button>
<Widget style="background: red; min-height: 5px;" /> </Group> </Window> </Application> <notify> <win attribute="CloseRequest" value="true" target="app" method="Shutdown" /> </notify> </feelin:application>

Example

Obviously this also work with vertical layouts.

widget-align
<?xml version="1.0" ?>

<feelin:application>
    <Application Id="app">
        <Window Id="win" Title="Widget : Align" Open="true" Width="200px" Height="200px">
            <Group>
                <Widget style="background: red; min-height: 5px;" />
<Button Align="start" style="width: auto;">start</Button> <Button Align="center" style="width: auto;">center</Button> <Button Align="end" style="width: auto;">end</Button>
<Widget style="background: red; min-height: 5px;" /> </Group> </Window> </Application> <notify> <win attribute="CloseRequest" value="true" target="app" method="Shutdown" /> </notify> </feelin:application>

Chainable -- (01.00) [I.G], bool32

Feelin UI can be fully controled with the keyboard.

By default all objects are chained to the window cycle chain. Setting the Chainable attribute to FALSE disable the automation.

See also

Draggable -- (01.00) [ISG], bool32

Set this attribute to TRUE if you want a widget to be draggable.

Dropable -- (01.00) [ISG], bool32

Set this attribute to TRUE is you want a widget to be a possible drop place for draggable widgets.

Mode -- (01.00) [I.G], keyword

This attribute sets the input mode of a widget.

By default, all widgets are inert : they have no event handler and thus they don't react on events. You can change to (non)behavior by using the Mode attribute. Beside creating an event handler, the widget's mode really modify how the Selected and Pressed attributes are modified.

Special values

FV_Widget_Mode_Inert

This is the default mode. The widget doesn't react on events.

FV_Widget_Mode_Touch

Use this mode with widgets that should report immediately that they have been clicked e.g. radio buttons. The Selected attribute is set to TRUE when the widget is clicked or activated by a shortcut or a key pressed.

FV_Widget_Mode_Toggle

Use this mode with widgets that represent two states e.g. a checkbox. The Selected attribute is alternatively set to TRUE and FALSE when the widget is clicked or activated by a shortcut or a key pressed.

FV_Widget_Mode_Button

Use this mode with widgets that should trigger an action when they have actually been released. e.g. a button.

  • The Selected attribute is set to TRUE when the widget is pressed and the mouse pointer is inside the widget box, or the shortcut or key pressed has not yet been released.

  • The Selected attribute is set to FALSE when mouse pointer is outside the widget box or when the widget is released by the mouse button or key.

  • The Pressed attribute is set to TRUE when the widget is first pressed, and is set to FALSE only when the widget is release and the mouse pointer is inside the widget (if a mouse button was pressing the widget). Listening to this attribute getting FALSE is a good way to know if the user really wanted to press the widget without a second thought.

PublicData -- (01.00) [..G], FWidgetPublic *

One usualy uses attributes to get informations from objects, but sometimes you need faster ways to get these informations, more importantly if it's your own object. Unfortunately, since the Object class is opaque, you cannot peek and poke blindly. Fortunately, every widget embends an informative public structure that you can get using this attribute.

It's recommanded to save the pointer to the FWidgetPublic structure during the New method, using the F_SAVE_WIDGET_PUBLIC macro. Don't forget to add a WidgetPublic member to your struct LocalObjectData using the F_MEMBER_WIDGET_PUBLIC macro.

Example

widget-publicdata
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/feelin.h>
#include <libraries/feelin.h>
struct FeelinBase *FeelinBase;
struct LocalObjectData { F_MEMBER_WIDGET_PUBLIC; };
F_METHOD(uint32,mNew) { struct LocalObjectData *LOD = F_LOD(Class,Obj);
F_SAVE_WIDGET_PUBLIC;
return F_SUPERDO(); }
F_METHOD(uint32,mDraw) { struct LocalObjectData *LOD = F_LOD(Class,Obj);
IFEELIN F_Log ( FV_LOG_USER,
"widget is: %s, %s, %s, %s"n
_widget_is_selected ? "selected" : "not selected", _widget_is_pressed ? "pressed" : "not pressed", _widget_is_active ? "active" : "not active", _widget_is_disabled ? "disabled" : "not disabled" );
return F_SUPERDO(); } int32 main(void) { STATIC F_METHODS_ARRAY = { F_METHODS_ADD_STATIC(mNew, FM_New), F_METHODS_ADD_STATIC(mDraw, FM_Area_Draw),
F_ARRAY_END };
STATIC F_TAGS_ARRAY = { F_TAGS_ADD_SUPER(Text), F_TAGS_ADD_LOD, F_TAGS_ADD_METHODS,
F_ARRAY_END };
if (F_FEELIN_OPEN) { FClass *cc = IFEELIN F_CreateClassA(NULL, F_TAGS_PTR);
if (cc != NULL) { FObject app, win, play_1, play_2, disable_1, disable_2; app = AppObject, Child, win = WindowObject, FA_Window_Title, "FWidgetPublic", FA_Window_Open, TRUE,
Child, VGroup, Child, play_1 = IFEELIN F_NewObj(cc->Name,
FA_Element_Class, "button", FA_Element_Style, "height: auto", FA_Widget_Mode, FV_Widget_Mode_Release, FA_Text_Contents, "let's play !", FA_Text_PreParse, "<align=center>",
End,
Child, play_1 = IFEELIN F_NewObj(cc->Name,
FA_Element_Class, "button", FA_Element_Style, "height: auto", FA_Widget_Mode, FV_Widget_Mode_Release, FA_Text_Contents, "let's play !", FA_Text_PreParse, "<align=center>",
End,
Child, RowGroup(2), FA_Element_Class, "box",
Child, HLabel("enabled first:"), Child, disable_1 = Checkbox(TRUE), Child, HLabel("enabled second:"), Child, disable_2 = Checkbox(TRUE),
End, End, End, End;
if (app != NULL) { IFEELIN F_Do(win, FM_Notify, FA_Window_CloseRequest, TRUE, app, FM_Application_Shutdown, 0); IFEELIN F_Do(disable_1, FM_Notify, FA_Widget_Selected, FV_Notify_Always, play_1, FM_Set, 2, FA_Widget_Disabled, FV_Notify_Toggle); IFEELIN F_Do(disable_2, FM_Notify, FA_Widget_Selected, FV_Notify_Always, play_2, FM_Set, 2, FA_Widget_Disabled, FV_Notify_Toggle);
IFEELIN F_Do(app, FM_Application_Run);
IFEELIN F_DisposeObj(app); }
IFEELIN F_DeleteClass(cc); } else { IDOS_ Printf("Could not create custom class.\n"); }
F_FEELIN_CLOSE; } else { IDOS_ Printf("Failed to open feelin.library\n"); }
return 0; }

Example

If you need to get the public structure of others objects, you can use the F_GET_WIDGET_PUBLIC() macro as following :

F_HOOKM(APTR, code_group_create_node, FS_Family_CreateNode)
{
    FAreaPublic *apd = F_GET_AREA_PUBLIC(Msg->Object);
    FWidgetPublic *wpd = F_GET_WIDGET_PUBLIC(Msg->Object);
if ((apd != NULL) && (wpd != NULL)) { FWidgetNode *node = IFEELIN F_NewP(CUD->Pool, sizeof (FWidgetNode));
if (node != NULL) { node->AreaPublic = apd; node->WidgetPublic = wpd;
return node; } } else if (Msg->Object != NULL) { IFEELIN F_Log(FV_LOG_DEV, "%s{%08lx) is not a subclass of the Widget class", _object_classname(Msg->Object), Msg->Object); }
return NULL; }

Weight -- (01.00) [I.G], uint16

The weight of an object determines how much space it'll get during the layout process. Imagine you have a 100 pixels wide horizontal group with two string objects. Usually, each object will get half of the room and be 50 pixels wide. If you feel the left gadget is more important and should be bigger, you can give it a weight of 200 (and 100 for the right gadget). Because the left gadget is twice as heavy as the right gadget, it will become twice as big (about 66 pixel) as the right one (34 pixel).

Of course giving weights only makes sense if the object is resizable. An object with a weight of 0 will always stay at its minimum size.

By default, all objects have a weight of 100.

Hide

[method override]

If an event handler was created during the Show method, it's deleted here. Also, if the widget to hide is currently the active, the FA_Window_ActiveObject attribute of its parent window is set to NULL.

Show

[method override]

If you have requested events with the ModifyEvents methods, or if the Mode attribute is different than FV_Widget_Mode_Inert, an event handler is created. Thus, once the Show method is done, your widget is able to receive events.

ModifyEvents -- (01.00)

IFEELIN F_DoA(obj, FM_Widget_ModifyEvents, FS_Widget_ModifyEvents);

Function

Event handlers allow custom classes to receive events. This method is an interface to the FM_Window_CreateEventHandle and FM_Window_DeleteEventHandle methods. It creates and disposes handlers on the fly, depending on the modifications applied to the events requested.

Inputs

Add (bits32)

Events to add.

Rem (bits32)

Events to remove.

Result

The method returns NULL if it was unable to create the handler.

Example

F_METHOD(bool32, mSetup)
{
    if (F_SUPERDO() == FALSE)
    {
        return FALSE;
    }
F_Do(Obj, FM_Widget_ModifyEvents, FF_EVENT_KEY | FF_EVENT_BUTTON, 0);
return TRUE; }
F_METHOD(uint32, mCleanup) { F_Do(Obj, FM_Widget_ModifyEvents, 0, FF_EVENT_KEY | FF_EVENT_BUTTON);
return F_SUPERDO(); }
F_METHODM(bits32, mHandleEvent, FS_Widget_HandleEvent) { struct LocalObjectData *LOD = F_LOD(Class, Obj);
FEvent *fev = Msg->Event;
if (fev->Key) { switch (fev->Key) { case FV_KEY_LEFT: LOD->sx = -1; break; case FV_KEY_RIGHT: LOD->sx = 1; break; case FV_KEY_TOP: LOD->sy = -1; break; case FV_KEY_BOTTOM: LOD->sy = 1; break;
default: return 0; }
F_Draw(Obj, FF_Draw_Update);
return FF_HandleEvent_Eat; } else { switch (fev->Class) { case FF_EVENT_BUTTON: { if ((fev->Code == FF_EVENT_BUTTON) && (FF_EVENT_BUTTON_DOWN & fev->Flags)) { if (between(fev->MouseX, _area_cx, _area_cx2) && (between(fev->MouseY, _area_cy, _area_cy2)) { LOD->x = fev->MouseX; LOD->y = fev->MouseY;
F_Draw(Obj, FF_Draw_Update);
/* Only request FF_EVENT_MOTION if we really need it * /
F_DoA(Obj, FM_Widget_ModifyEvents, FF_EVENT_MOTION, 0);
return FF_HandleEvent_Eat; } } else { /* reject FF_EVENT_MOTION because lmb is no longer pressed * /
F_DoA(Obj, FM_Widget_ModifyEvents, 0, FF_EVENT_MOTION); } } break;
case FF_EVENT_MOTION: { if (between(fev->MouseX, _area_cx, _area_cx2) && (between(fev->MouseY, _area_cy, _area_cy2)) { LOD->x = fev->MouseX; LOD->y = fev->MouseY;
F_Draw(Obj, FF_Draw_Update);
return FF_HandleEvent_Eat; } } break; } }
return 0; }

Passing the method to your the superclass is only necessary if you rely on Widget input handling, through the Mode attribute.

Development