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:
- Fixed or animated images: to use as backgrounds
- Buttons: to trigger an action on a click
- Toggle buttons: to toggle an on/off setting
- Radio buttons: to allow you to select one item among several
- Drag handles: to allow a window to be moved dragging it
- Labels: to show text messages (single or multiple lines)
- Text entries: For text input
- Text marquees: to show scrolling text
- Scrollbar: to select a value in a range
EWS widgets are WINDOW
s, 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
:
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
BUTTON
s (this includes BUTTON_IMAGE
s). 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:
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):
Let's move on to an example with more widgets...
Previous | Table of contents | Next |