Text widgets

Some of the widgets included in EWS are able to show text. In this chapter we will show some of them, and use the occassion to introduce a few new components of EWS.

In the same way that EWS does not have a predefined look for buttons or mouse pointers, EWS does not have a predefined look for text. Text is shown using fonts, which are objects of type FONT. This class is actually deferred, allowing specific implementations; some of them may be tied to a display type. Note that the structur is similar to what you have with IMAGE.

At the moment the only kind of font implemented for the SDL backend is a class called SDL_BITMAP_FONT (An implementation of TrueType fonts will be added later). These are variable-spacing, bitmapped fonts. The bitmap can have smooth borders (working as antialiasing), and textures (is not necessarily monochrome).

Bitmap fonts can be loaded from image files; the image file should contain all the glyphs from ASCII 33 onwards (up to ASCII 128 if you want; international text is not supported yet). The glyphs are separated by magenta pixels at the top row of the image (where magenta is 100% red, 0% green, 100% blue, completely opaque). This is a magnified piece of a font, showing the first few characters (this is not an actual font, just a screenshot of the gimp show the font with zoom):

Note the magenta lines at the top marking the limits of each glyph. You can use one or more magenta pixels to mark glyph separations. Note that the areas below magenta pixels are ignored, if you want to add spacing to the font add transparent space at the sides of the glyph but outside the magenta separators. By the way, the font should have a transparent background (using a GIF or PNG image) to mix properly with the text background; in the screenshot the background is shown as black. (Just if you're wondering, this font format came from JEGL, a set of Eiffel-SDL bindings that I used as a starting point for the library).

A few fonts are included with the demos.

The following code is from two of the included themos; one of them show labels and the other shows text entries:

Source: font_test.e

class FONT_TEST

inherit
	TEXT_CONSTANTS

create 
	make

feature {NONE} -- Creation

	d: SDL_DISPLAY
	c: MOUSE_POINTER

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

	make is
		local
			font2: FONT
			rect: RECTANGLE
			label: LABEL
			multi: MULTILINE_LABEL
			w1: WINDOW_IMAGE
			marq: HORZ_MARQUEE
		do
			-- Init video
			create {SDL_DISPLAY}d.make (640,480, 16, false)

			-- Raw Writing - 
			d.set_default_font (create {SDL_BITMAP_FONT}.make ("small_font.png"))
			create {SDL_BITMAP_FONT}font2.make ("white_font.png")

			-- Labels
			rect.set_with_size(5, 420, 190, 50)
			create label.make(d.root, rect, "Hi! I'm a label")
			label.set_bg (create {SDL_SOLID_IMAGE}.make (label.width, label.height, 30, 40, 50))

			rect.set_with_size(200, 420, 235, 50)
			create label.make(d.root, rect, "I'm right-bottom aligned with a border")
			label.set_bg (create {SDL_SOLID_IMAGE}.make (label.width, label.height, 30, 90, 100))
			label.set_h_alignment (horz_align_right)
			label.set_v_alignment (vert_align_bottom)
			label.set_border_width (4)

			rect.set_with_size(445, 420, 190, 50)
			create label.make(d.root, rect, "My own font")
			label.set_bg (create {SDL_SOLID_IMAGE}.make (label.width, label.height, 100, 200, 50))
			label.set_font (font2)

			rect.set_with_size(480, 390, 40, 20)
			create label.make(d.root, rect, "I'm too big for my boots")
			label.set_bg (create {SDL_SOLID_IMAGE}.make (label.width, label.height, 80, 80, 80))

			-- Multiline label
			rect.set_with_size (20, 20, 100, 120)
			create multi.make (d.root, rect, "This is a very long text that must be splitted over several lines to be displayed.%NLuckily, we have a multiline label to do that")
			multi.set_h_alignment (0.5)

			-- Rectangles (background for the marquee too)
			create w1.make (d.root, 300, 200, create {SDL_SOLID_IMAGE}.make (100, 100, 200,0,0))
			create w1.make (d.root, 400, 200, create {SDL_SOLID_IMAGE}.make (150, 100, 0,0,200))
			-- Marquee
			rect.set_with_size(350, 250, 150, 20)
			create marq.make(d.root, rect, "Hello.  I am a Horizontal Marquee.  Horizontal Marquees make your life easier allowing you to show extremely long texts in drastically short spaces, with almost no effort.  Make you life easier today.  Enjoy your own Horizontal Marquee.")

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

			-- Set Pointer
			d.root.set_pointer (c)

			-- Main loop
			d.set_timer_interval (100)
			d.do_event_loop
			d.close
		end

end -- class FONT_TEST

I won't explain code similar to the previous examples (display initialization, cursor setting, new_img wrapper, etc.)

You might note that this class inherits from TEXT_CONSTANTS this is an utility class defining several text alignment constants, as you might check seeing its short form.

Let's take a look at:

			d.set_default_font (create {SDL_BITMAP_FONT}.make ("small_font.png"))

Note that this creates a new SDL_BITMAP_FONT, the argument is an image file formatted as explained above. You can download the font file small_font.png to check it out (I'm not including font images in this document, they are too wide). It is also included in the EWS distribution. This new created font is passed to the set_default_font routine of the display. This default font will be used for widgets that don't have set another specific font.

We now then create a different font and attach it to font2 using the file white_font.png.

			create {SDL_BITMAP_FONT}font2.make ("white_font.png")

With that done, we can now add some text widgets:

			-- Labels
			rect.set_with_size(5, 420, 190, 50)
			create label.make(d.root, rect, "Hi! I'm a label")
			label.set_bg (create {SDL_SOLID_IMAGE}.make (label.width, label.height, 30, 40, 50))

A new EWS class is used here, RECTANGLE. RECTANGLE is an expanded class representing rectangles with the sides parallel to the screen. It is used all over EWS (internals and interface) to represent positions and sizes. It is not a graphic element; it is used for geometry. If you check the short form of this class you will se that it has a lot of operations but all of them are quite obvious. Get used to RECTANGLE manipulation because you will be doing a lot of it while programming EWS.

The first line in the last code block shown configures the rectangle rect at position (5,420), with width 190 and height 50. After that, a new LABEL is created at the display root, using the position and size given in the rectangle, and with the greeting message "Hi, I'm a label!". Labels are simple widgets that show a text message. They have no behaviour, but you will see that they have lots of features to control their presentation. The above code-block shows one of them: set_bg, which takes an image that will be used as background behind the label. Otherwise, the label lets you see what is behind it.

In this case we are also introducing a new kind of IMAGE implemented in the SDL driver, which is SDL_SOLID_IMAGE. This image is a solid rectangular color block; the first two arguments of its creation call are the image size, and the remaining three the red, green, and blue color components. 30,40,50 is a dark blue color like the background of this text

			rect.set_with_size(200, 420, 235, 50)
			create label.make(d.root, rect, "I'm right-bottom aligned with a border")
			label.set_bg (create {SDL_SOLID_IMAGE}.make (label.width, label.height, 30, 90, 100))
			label.set_h_alignment (horz_align_right)
			label.set_v_alignment (vert_align_bottom)
			label.set_border_width (4)

Here we create a new label and attach it to label. Again, we use a background color, but a different one. Besides we set the label alignment to the bottom right of the label rectangle. Note that the label text, depending on the font and what is written, may be bigger or smaller than the label window. Usually the text is horizontally and vertically centered, but the set_h_alignment and set_v_alignment features allow you to change that. You can also reserve a small space from the border of the window calling set_border_width; in this case the label will hava a 4 pixel border separting it from the bottom right corner.

			rect.set_with_size(445, 420, 190, 50)
			create label.make(d.root, rect, "My own font")
			label.set_bg (create {SDL_SOLID_IMAGE}.make (label.width, label.height, 100, 200, 50))
			label.set_font (font2)

Here we have yet another label, using font2 instead of the default font.

			rect.set_with_size(480, 390, 40, 20)
			create label.make(d.root, rect, "I'm too big for my boots")
			label.set_bg (create {SDL_SOLID_IMAGE}.make (label.width, label.height, 80, 80, 80))

This label shows what happens when the rectangle is too small. The text will be clipped to fit inside the window.

			-- Multiline label
			rect.set_with_size (20, 20, 100, 120)
			create multi.make (d.root, rect, "This is a very long text that must be splitted over several lines to be displayed.%NLuckily, we have a multiline label to do that")
			multi.set_h_alignment (0.5)

This code block shows a slightly different widget, the MULTILINE_LABEL. It allows showing long pieces of text by splitting them in new lines. It also handles newline ( '%N') characters properly. Note that the rectangle assigned above is taller, and the alignment is set to centered. By default it splits lines at word boundaries, but you can change that using set_wordwrap.

			-- Rectangles (background for the marquee too)
			create w1.make (d.root, 300, 200, create {SDL_SOLID_IMAGE}.make (100, 100, 200,0,0))
			create w1.make (d.root, 400, 200, create {SDL_SOLID_IMAGE}.make (150, 100, 0,0,200))
			-- Marquee
			rect.set_with_size(350, 250, 150, 20)
			create marq.make(d.root, rect, "Hello.  I am a Horizontal Marquee.  Horizontal Marquees make your life easier allowing you to show extremely long texts in drastically short spaces, with almost no effort.  Make you life easier today.  Enjoy your own Horizontal Marquee.")

The first two lines here create a blue window and a red window, side by side; they will be used to show text without background over them. The next widget created is a variant of label, the HORZ_MARQUEE. This widget shows its text scrolling from right to left, so you will be able to read the whole text even when the window is smaller than the text. It is not to use frequently, but it is interesting to give a first glimpse at EWS animation capabilities. You will not that the message does not scroll unless you also include this line:

			d.set_timer_interval (100)

This enables an internal timer of the display, which generate events periodically, allowing you to synchronize timed widgets. The timer is set here at a 100ms interval. You can play around with the interval to see the marquee scroll at different rates. Note that most systems don't have enough resolution to set this value under 10 milliseconds reliably.

The above application, when running, looks like this:

We will see here shortly another of the included examples:

Source: entry_test.e

class ENTRY_TEST

create 
	make

feature {NONE} -- Creation

	d: DISPLAY

	e1, e2: TEXT_ENTRY

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

	make is
		local
			r: RECTANGLE
			c: MOUSE_POINTER
			sdl: SDL
			i: IMAGE
			w: WINDOW
			f: SDL_BITMAP_FONT
		do
			-- Init video
			create {SDL_DISPLAY}d.make (640,480, 16, False)

			-- Set Pointer
			create c.make (new_img ("cursor1.png"), 6, 4)
			d.root.set_pointer (c)

			-- Background
			create {SDL_IMAGE}i.make_from_file ("entry.png")
			create {WINDOW_IMAGE}w.make (d.root, 20, 20, i)
			create {WINDOW_IMAGE}w.make (d.root, 20, 50, i)

			create f.make ("default_font.png")
			r.set_with_size (20, 20, 124, 24)
			create e1.make (d.root, r)
			e1.set_font (f)
			e1.set_border_size (5)
			r.set_with_size (20, 50, 124, 24)
			create e2.make (d.root, r)
			e2.set_font (f)
			e2.set_border_size (5)

			e1.set_next_in_tab_order (e2)
			e1.set_default_action(agent e2.grab)
			e2.set_next_in_tab_order (e1)
			e2.set_default_action(agent print_contents)

			sdl.enable_keyboard_repeat (250, 50)
			e1.grab

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

	print_contents is
		do
			print (e1.text + "%N")
			print (e2.text + "%N%N")
		end

end -- class ENTRY_TEST

You should be able now to read most of this code and understand what it does. Let's jump to the interesting part:

			r.set_with_size (20, 20, 124, 24)
			create e1.make (d.root, r)
			e1.set_font (f)
			e1.set_border_size (5)
			r.set_with_size (20, 50, 124, 24)
			create e2.make (d.root, r)
			e2.set_font (f)
			e2.set_border_size (5)

This attachs to e1 and e2new instances of TEXT_ENTRY. This widget looks a lot like a label, but you can edit its text. It has some operations similar to label, like set_font and set_border_size. It has a few extra interesting operations:

			e1.set_next_in_tab_order (e2)
			e1.set_default_action(agent e2.grab)

The text entry is one of the widgets that can get "focus". Having focus means that it will get the keyboard input. If you run the example you will note that clicking on one of the two entries moves the text cursor to the entries. That is because the entry behavior is programmed to grab the focus when being clicked. Other way to switch focus is to tab-cycle between the widgets (i.e., pressing the tab key repeatedly). To make that work, you need to define the sequence of widgets that will cycle pressing the TAB key. The first line is doing exactly that.

A way to forcefully set the focus on a widget is to call the procedure grab. grab is defined in WINDOW, and makes the window to take the focus.

The procedure set_default_action of entries allows you to assign an action that will be called when pressing the Enter key while the focus is in the entry. So, the code above configures e1 to give the focus to e2 when pressing Enter (you probably have seen this behaviour in some username/password dialogs).

			e2.set_next_in_tab_order (e1)
			e2.set_default_action(agent print_contents)

Here we can see that pressing TAB with focus in e2 will send the focus back to e1, while pressing Enter on e2 will call to print_contents. This last procedure was defined in our example in the following way:

	print_contents is
		do
			print (e1.text + "%N")
			print (e2.text + "%N%N")
		end

You can see that you can retrieve the content of the entries calling their function text. You can also modify the text in any text widget calling set_text (not shown in this example).

You can also see that the example calls:

			e1.grab

This is to set the focus on the first entry at the beginning; otherwise you should have to click one entry before being able to write.

This finishes our tutorial on using widgets. Working with other kinds of widgets is similar to what was shown in the previous chapter and this one. Take some time to read the short form of the widget classes (installed as widgets/*.e) on your installation directory, and the examples included with the documentation (installed at /usr/share/doc/ews/test). The examples cover all the widget kinds.

After this, we will move to explaining EWS at a lower level, which will allow us to design our oun windows/widgets.
Previous Table of contents Next