Widgets and UIs

You will probably want to add some interaction to your EWS applications. The simplest way to do this is putting some widgets in your windows. Widgets are prepackaged user-interface elements. EWS provides you with the following kind of widgets:

EWS widgets are WINDOWs, each one having a specially defined appearance and behavior. Some widgets only define appearance (labels, fixed images) and others define only behavior (drag handles). You can define new widget kinds inheriting WINDOW or another widget class.

The following example is included with the EWS distribution, in the directory test/example1:

Source: button_test.e

class BUTTON_TEST

create
        make

feature {NONE} -- Creation

        d: DISPLAY
        c: MOUSE_POINTER

        new_img (fn: STRING): IMAGE is
                do
                        create {SDL_IMAGE}Result.make_from_file (fn)
                end

        make is
                local
                        b: BUTTON_IMAGE
                        w1: WINDOW
                do
                        -- Init video
                        create {SDL_DISPLAY}d.make (640,480, 16, True)

                        -- Background
                        create {WINDOW_IMAGE}w1.make (d.root, 100, 100, new_img ("logo.png"))

                        -- Buttons
                        create b.make (d.root, 200, 350, new_img("b1u.png"),
                                new_img("b1p.png"),
                                new_img("b1d.png"))
                        b.set_click_handler (agent on_click("YES!%N") )

                        create b.make (d.root, 350, 350, new_img("b2u.png"),
                                new_img("b2p.png"),
                                new_img("b2d.png"))
                        b.set_click_handler (agent on_click("OK!%N") )

                        -- Make pointer
                        create c.make (new_img ("cursor1.png"), 6, 4)

                        -- Set pointer
                        d.root.set_pointer (c)
                        w1.set_pointer (Void)

                        -- Main loop
                        d.do_event_loop
                        d.close
                rescue
                        if d /=Void then d.close end
                end

        on_click (message: STRING) is
                do
                        print (message)
                        d.add_event (create {EVENT_QUIT})
                end

end -- class BUTTON_TEST

This example shows a simple aplication with a logo and two buttons; clicking any button prints a message (which depends on the button clicked) and quits. Let's check how it works:

I will add an auxiliar function to make image loading easier to write. It's nothing new, just a wrapper for SDL_IMAGE.make_from_file:

        new_img (fn: STRING): IMAGE is
                do
                        create {SDL_IMAGE}Result.make_from_file (fn)
                end

After that, you will see the usual system initialization starting up the DISPLAY in full-screen mode (the True passed as last argument). A WINDOW_IMAGE image is added as a background/logo, loading from logo.png:

[logo.png]

After that, we start with widget creation:

                        create b.make (d.root, 200, 350, new_img("b1u.png"),
                                new_img("b1p.png"),
                                new_img("b1d.png"))

This creates a new object attached to b, which was declared as a BUTTON_IMAGE. This class is for buttons where the appearence is set using images. This class uses three images to determine its look in each of its different states. The first one is for the "up" state, which is the normal state of the button; the second one is for the "prelight" state, used when hovering the mouse pointer over the button without clicking it. The third one is for the "down" state, and will be seen when you push the button, before releasing it (i.e., while holding your left mouse button down). These are the images used in the example:

b1u.png

b1p.png

b1d.png

As said before, some widgets have appearance, behavior, or both. BUTTON_IMAGE has both: the appearence determined by the images, and the change state when you move the mouse or click on the button. There is also a class called BUTTON which contains all the button behaviour but no appearance. It is used as ancestor of BUTTON_IMAGE and other classes, possible ancestor for some different kind of button you might like to code, and in some cases to provide clickable areas where you don't need something special to be shown.

Besides being able to click it, you can attach actions to BUTTONs (this includes BUTTON_IMAGEs). This is done in the code above like this:

                        b.set_click_handler (agent on_click("YES!%N") )

The method set_click_handler receives an agent with an action that will be called when the button is clicked, in this case calling on_click("YES!%N"). The procedure on_click is defined in our example:

        on_click (message: STRING) is
                do
                        print (message)
                        d.add_event (create {EVENT_QUIT})
                end

It just prints a message (on the program text output, not at the EWS display!), and quits. The second line of this routine will be explained later, at this time it is enough to say that this signals the display to stop the event loop as soon as possible (it will not stop immediatly).

Let's add another button. This is the same idea as above:

                        create b.make (d.root, 350, 350, new_img("b2u.png"),
                                new_img("b2p.png"),
                                new_img("b2d.png"))
                        b.set_click_handler (agent on_click("OK!%N") )

There is nothing new here. This button is a little more to the right, and has a different set of pictures:

b2u.png

b2p.png

b2d.png

This button will call to the same procedure (but you can, of course, call any other procedure), with a different argument.

Something that may surprise you here is that we're attaching this new button to b. This will detach the previous button from b; that is ok because the old button is anyway already in the EWS window tree, and we will not need a reference to the first button any longer.

You may have noted when running the previous examples that the mouse pointer disappears when moving over the EWS display. This is because EWS uses its internal mouse pointer, and although it is working, it is invisible unless you tell EWS how it should look. To do that you must create a MOUSE_POINTER object. These objects are little more than an image with information about which part of the image is the pointer "hot spot": the coordinates of the arrow tip if your pointer is an arrow, the center of the crosshairs if you're using crosshairs as a pointer, etc.

The following code creates our pointer:

                        -- Make pointer
                        create c.make (new_img ("cursor1.png"), 6, 4)

As said before, it is just a creation call with an image and coordinates. The image used in this example is a simple arrow; the PNG file is slightly transparent just to show that you can do that:

[cursor1.png]

After that call, we have a MOUSE_POINTER attached to c, but the display does not know about it. You can attach pointers not just to the whole display, but to specific windows. So if you want you could have a text pointer when putting your mouse over text entries, a forbidden sign when putting your mouse in a disabled area, and some other default pointer. The pointer used will be the one indicated by the pointed window; if that window does not define a pointer, the parents are searched until finding a pointer and using that, or reaching the top and using none.

We just want a single pointer to be used for the whole screen, to do that we just set the pointer for the root window:

                        -- Set pointer
                        d.root.set_pointer (c)
                        w1.set_pointer (Void)

The method set_pointer of WINDOW allows to setting the pointer shown, or using Void if you want to use the pointer of the parent. The code above setting the pointer for w1 is useless, it's just ther to show that you can use Void to unset the pointer and inherit from the parent (not hiding it!).

The rest of the code should be familiar now, it is the usual event loop and finalization. When any button is clicked, The event loop will stop, and the program flow will continue normally after the call to do_event_loop.

The application looks like this (this screenshot was taken in windowed mode instead of fullscreen):

Screenshot

Let's move on to an example with more widgets...
Previous Table of contents Next