Chapter 33: A Redraw Cookbook
In the previous chapter we got our Results window up and running, storing data and displaying it to the user. The use of Wimp_PlotIcon for drawing the window contents worked well for the tabular nature of the content, and we will be sticking with it going forward.
There are, however, many other ways to plot window contents. The ‘correct’ one to use will depend a lot on what that content is, how it appears to the user, and individual developer preference – and there will inevitably be more than one valid way to solve most problems. Before we move on to other things, we will have a quick look at some of these to show what else is possible and introduce a few useful tools to have on hand.
Free textops
If we need to plot text in a window, without the need for icons but using the desktop font, then Wimp_TextOp is a handy SWI to know about. It only arrived with the RiscPC in RISC OS 3.50, so it isn’t one for the retro audience, and the exact options on offer vary depending on which branch of the OS you’re on. Despite that, the core functions are useful and, if you’re requiring the Nested Wimp for any reason, it comes for free with that.
Its purpose is to manipulate text in the desktop font, switching seamlessly between outline and System fonts. It’s one of those SWIs which takes a reason code in R0 to determine exactly what it does, and from the start we had three routines at our disposal.
- Wimp_TextOp 0 sets the foreground and background colours for the current Wimp_TextOp 2 plotting operation, using &BBGGRR00 values.
- Wimp_TextOp 1 calculates the width of a string in OS units using the current desktop font, with the option to include only the first n characters. This can be very useful for working out how to wrap or truncate text.
- Wimp_TextOp 2 plots a string to the screen in the current desktop font, and is the one that we’re particularly interested in here. It must be used from within a redraw loop.
If you’re targetting RISC OS 5 or a recent version of Nested Wimp, then a couple of additional opcodes give useful variations on Wimp_TextOp 1. Wimp_TextOp 3 will take a string and, given a width in OS units, return the offset in characters of the necessary split point if splitting at a specific character. Wimp_TextOp 4 will take a string and a width in OS units and, given a suitable output buffer, write a string which will fit the available space when truncated with three full stops or an ellipsis.
For plotting our text, Wimp_TextOp 2 is the one that we need. OSLib defines it as follows:
extern void wimptextop_paint( wimptextop_paint_flags flags, char const *s, int x0, int y0 );
It takes a pointer to a string *s, x0 and y0 coordinates of a point on the baseline of the text, and some flags to control how these values are interpreted.
#define wimptextop_VJUSTIFY ((wimptextop_paint_flags) 0x10000000u) #define wimptextop_OFFSETS_FOR_REDRAW ((wimptextop_paint_flags) 0x20000000u) #define wimptextop_GIVEN_BASELINE ((wimptextop_paint_flags) 0x40000000u) #define wimptextop_RJUSTIFY ((wimptextop_paint_flags) 0x80000000u)
The wimptextop_GIVEN_BASELINE flag indicates that the y0 coordinate should be adjusted so that System and outline fonts appear to use the same vertical baseline. The text will usually be plotted left-aligned to x0, but if wimptextop_RJUSTIFY is set then it will be right-aligned to this point instead. The other two flags aren’t mentioned in the Programmer’s Reference Manual or the StrongHelp Manuals, but the ROOL Wiki indicates that wimptextop_VJUSTIFY will cause the text to be vertically centred on y0 while wimptextop_OFFSETS_FOR_REDRAW causes the supplied coordinates to be treated as work area coordinates in the same way as with Wimp_PlotIcon. It isn’t clear on which versions of RISC OS these latter two flags can be used, but it may be fair to assume RISC OS 5.
Armed with this information, we can replace our results_plot_icon() function with a new results_plot_text() as seen in Listing 33.1. Superficially this looks similar to its predecessor, but there are some significant changes that we will need to investigate.
#define RESULTS_LINE_INSET 12 /* Plot a piece of text in the redraw loop. */ static void results_plot_text(wimp_i icon, int ox, int y, char *text) { wimptextop_paint_flags flags = wimptextop_GIVEN_BASELINE; int x; if (icon == RESULTS_ICON_TITLE_SHAPE) { x = ox + results_icons[icon].extent.x0 + RESULTS_LINE_INSET; } else { x = ox + results_icons[icon].extent.x1 - RESULTS_LINE_INSET; flags |= wimptextop_RJUSTIFY; } wimptextop_paint(flags, text, x, y + RESULTS_LINE_INSET); }
Listing 33.1: Plotting text with Wimp_TextOp
The function still takes an icon, but instead of being a template to use with Wimp_PlotIcon this is now an icon from the title row to use for positioning. The ox parameter is the screen coordinate of the horizontal work area origin, which we introduced a couple of chapters ago. On the other hand, y is the vertical screen coordinate of the bottom of the row in which we want to plot the text. Unlike Wimp_PlotIcon, Wimp_TextOp 2 works in screen coordinates unless we set the wimptextop_VJUSTIFY flag, and so there are no work area coordinates here. The *text parameter continues to point to the text to be plotted, however.
We want to plot on a standard baseline, so we initialise flags with wimptextop_GIVEN_BASELINE set. All of the columns in our window are right-aligned apart from the Shape column, so we test for this and treat it as a special case. If we are in the left-aligned column, then
x = ox + results_icons[icon].extent.x0 + 12;
takes the screen coordinate of the horizontal work area origin ox and adds the work area coordinate of the left-hand side of the icon in order to get the screen coordinate of the left-hand side of the column. It then adds on a constant inset value to provide some margin around the text. For all of the other columns, which are right-aligned,
x = ox + results_icons[icon].extent.x1 - RESULTS_LINE_INSET; flags |= wimptextop_RJUSTIFY;
does a similar thing but uses the right-hand side of the icon in order to get the screen coordinate of the right-hand side of the column. The constant inset is subtracted so that it insets the text in from the right, and the wimptextop_RJUSTIFY flag is set to tell Wimp_TextOp 2 that we want to right-justify the text.
Finally, Wimp_TextOp 2 is called – passing the flags, the text pointer and the x and y screen coordinates. An inset up is applied to the vertical coordinate, in a similar way that we did conditionally for the horizontal position.
Returning to the redraw loop within results_redraw(), we have updated the code as follows.
/* Redraw the window. */ more = wimp_redraw_window(redraw); wimp_set_colour(wimp_COLOUR_BLACK); wimp_set_colour(wimp_COLOUR_VERY_LIGHT_GREY | 0x80u); while (more) { ox = redraw->box.x0 - redraw->xscroll; oy = redraw->box.y1 - redraw->yscroll; top = (oy - redraw->clip.y1) / row_height; if (top < 1) top = 1; bottom = ((oy - redraw->clip.y0) - 1) / row_height; if (bottom > RESULTS_MAX_ROWS) bottom = RESULTS_MAX_ROWS; if (bottom > results_row_count) bottom = results_row_count; for (int y = top; y <= bottom; y++) { y0 = oy - ((y + 1) * row_height); results_plot_text(RESULTS_ICON_TITLE_SHAPE, ox, y0, results_rows[y - 1].shape); results_plot_text(RESULTS_ICON_TITLE_SIDES, ox, y0, results_rows[y - 1].sides); results_plot_text(RESULTS_ICON_TITLE_INTERNAL_ANGLES, ox, y0, results_rows[y - 1].internal_angle); results_plot_text(RESULTS_ICON_TITLE_SIDE_LENGTH, ox, y0, results_rows[y - 1].side_length); results_plot_text(RESULTS_ICON_TITLE_PERIMETER, ox, y0, results_rows[y - 1].perimeter); results_plot_text(RESULTS_ICON_TITLE_AREA, ox, y0, results_rows[y - 1].area); } more = wimp_get_rectangle(redraw); }
Before calling Wimp_TextOp 2, we need to set the colours which it will use. These can be changed at any point if we need multi-colour text, but since we are only using black text we set this at the start of the redraw code. The use of anti-aliased outline fonts means that we must set the foreground and background colours. We can use either Wimp_TextOp 0 or Wimp_SetColour for this purpose, which OSLib defines as follows; we have chosen to use the latter.
extern void wimptextop_set_colour( os_colour fg, os_colour bg ); extern void wimp_set_colour( wimp_colour colour );
We need both the ox and oy screen coordinates for the work area origin, so we calculate these at the top of the outer redraw loop. For each line in the inner loop, we calculate the baseline y0 in screen coordinates, and then pass ox and y0 to each of the calls to results_plot_text().
If we compile the application with these changes, we can see from Figure 33.1 that there is little visible change.

Figure 33.1: Plotting the rows of results using Wimp_TextOp
There is at least one important difference, however. If we adjust some of the column widths as we have in Figure 33.2, we can see that when plotting the table cells using Wimp_PlotIcon (as the window at the back is doing) the Wimp clips the text for us when it would otherwise spill outside of the area allowed for it. The version which uses Wimp_TextOp 2 (the window in fromt) does not do this as standard: it would be up to us to use the other Wimp_TextOp calls to calculate the widths of the strings and truncate them as necessary before plotting them. This may be something which swings the decision towards one or the other of the approaches.

Figure 33.2: Wimp_PlotIcon clips to the icon area, while Wimp_TextOp doesn’t
The full application code with these changes included can be found in Download 33.1.
Going old-school
There’s no requirement to use any fancy Wimp calls to do the text plotting, however: it’s possible to use printf() for the job. The Wimp issues a VDU 5 before exiting from Wimp_RedrawWindow and Wimp_GetRectangle, so we can position the graphics cursor with a call to OS_Plot and print the text that we need.
/* Redraw the window. */ more = wimp_redraw_window(redraw); wimp_set_colour(wimp_COLOUR_BLACK); while (more) { ox = redraw->box.x0 - redraw->xscroll; oy = redraw->box.y1 - redraw->yscroll; top = (oy - redraw->clip.y1) / row_height; if (top < 1) top = 1; bottom = ((oy - redraw->clip.y0) - 1) / row_height; if (bottom > RESULTS_MAX_ROWS) bottom = RESULTS_MAX_ROWS; if (bottom > results_row_count) bottom = results_row_count; for (int y = top; y <= bottom; y++) { y1 = oy - (y * row_height); os_plot(os_MOVE_TO, ox + RESULTS_LINE_INSET, y1 - RESULTS_LINE_INSET); printf("%-8s %4s %14s %14s %14s %14s", results_rows[y - 1].shape, results_rows[y - 1].sides, results_rows[y - 1].internal_angle, results_rows[y - 1].side_length, results_rows[y - 1].perimeter, results_rows[y - 1].area ); } more = wimp_get_rectangle(redraw); }
The results seen in Figure 33.3 might look a bit nostalgic, but may well be all that is required for some quick-but-functional applications. It should be noted that some care was needed with spaces and field widths in the printf() format string in order to get the columns to line up – how robust this would be in different screen modes is another question! If we wanted more repeatability then we could, of course, make six calls to printf() for each line, and align each one to the appropriate column heading icon – but it’s quickly becoming a lot of effort compared to the icons in Download 32.3 from the previous chapter.

Figure 33.3: Outputting the results using printf()
The full application code with printf() can be found in Download 33.2.


