== Lesson 20: Menus and Screens image::learn/20-gui.png[The finished lesson, 480] You can build a menu out of what you already know. A box, some text, a cursor that moves when the stick moves, an `if` for every choice, and arithmetic to keep it all lined up when you add a fourth option. It works. It is also about a hundred lines for a page with three buttons on it, and every one of those lines is yours to maintain. Singe has a whole document engine inside it -- RmlUi -- and a menu written for it is a page, like a web page, with a style sheet. It wraps its own text. It lays itself out when you add an option. It knows what a button is, what a slider is, and which control the player is pointing at. This lesson builds a pause menu with it, over a game that keeps running behind. === Two Files A GUI page is two things: a *document* that says what is on the page, and *Lua* that says what the page does. They live in separate files, and this is the whole point of the exercise -- the layout stops being code. Start with a script called `menu.singe` and a game so small it fits in a paragraph: a ship crossing the screen, and a score that goes up each time it gets across. You have written this kind of thing since lesson seven. [source,lua] ---- dofile("Singe/Framework.singe") overlaySetResolution(discGetWidth(), discGetHeight()) local ship = spriteLoad(DIR .. "art/ship.png") local shipX = 0 local speed = 2 local score = 0 function onOverlayUpdate() overlayClear() shipX = shipX + speed if shipX > overlayGetWidth() then shipX = -spriteGetWidth(ship) score = score + 10 end spriteDraw(ship, shipX, 300) return OVERLAY_UPDATED end ---- That runs. Now the document. Beside your script, make a file called `pause.rml`: [source,html] ---- Pause

Paused

Score 000000

Arrow keys move, Return chooses.

---- If you have ever seen a web page's source, that is familiar. If you have not, here is everything you need to read it. A document is made of *elements*. An element starts with a name in angle brackets, `

`, ends with the same name and a slash, `

`, and whatever is between them is inside it. Some elements have nothing inside and close themselves with a slash at the end, like the `` above. Elements nest, and the indentation shows the nesting the same way it shows it in Lua. The words inside the opening tag are *attributes*, written `name="value"`. Two of them matter to you more than the rest. `id` gives an element a name your script can find it by, which is how Lua and the document talk to each other. `class` puts the element in a group the style sheet can aim at; `panel`, `muted`, and `list` are groups the engine's own style sheet already knows about. The markup language is called RML and the style language is called RCSS. They are RmlUi's, not Singe's. The manual's GUI chapter points at RmlUi's own documentation for the full list of what you can write, and you will want it eventually. Everything in this lesson works without it. === Show It Back in the script. Above `onOverlayUpdate`, add: [source,lua] ---- local gui = guiNew(overlayGetWidth(), overlayGetHeight()) local page = guiLoad(gui, DIR .. "pause.rml") ---- `guiNew` makes a *GUI*: a rectangle of a fixed size that documents are laid out and drawn into. It returns a handle, the way `fontLoad` and `spriteLoad` do. Making it the size of the overlay means one pixel of the document is one pixel of your overlay, which keeps the arithmetic in your head simple. `guiLoad` reads a document into that GUI and returns a second handle, for the document itself. Nearly every other call takes both: the GUI and the document in it. One GUI can hold up to thirty-two documents -- a title page, an options page, and a game over page can share one -- and up to sixteen GUIs can exist at once. Nothing is on screen yet, because loading a document does not draw it. Add one line to `onOverlayUpdate`, just before the `return`: [source,lua] ---- guiDraw(gui) ---- Run it. The panel is there, over the ship, with its heading and its three buttons and its slider, and you have written no drawing code at all. `guiDraw` composites the GUI over the overlay *for this frame only*, like every other drawing call, so it belongs in `onOverlayUpdate` and has to be called again next frame. Where you put it decides what is on top: everything you drew before it is underneath, and everything you draw after it goes over it. A crosshair drawn after `guiDraw` is never hidden by the menu. === Let the Player Use It Click a button. Nothing happens, and the button does not even light up. A new GUI is a picture, not a control panel. That is deliberate: a HUD or a sign should not swallow the fire button. To make a page take input: [source,lua] ---- guiSetInput(gui, true) ---- Now the mouse works, the buttons light up under the pointer, and the arrow keys move between the controls, because the document asked for that with `nav: auto`. On a pad or a stick the four directions arrive as the arrow keys, the first action button arrives as Return, and the second arrives as Escape. A cabinet with a joystick and two buttons drives this page without knowing it is a document. Clicking still does nothing, though, because nothing is listening. === Talk to It Three calls join the document to your script, and between them they do almost everything. [source,lua] ---- guiSetHandler(gui, page, "resume", "click", function() closeMenu() end) ---- `guiSetHandler` says: when the element with this `id` does this thing, call this function. The `id` is the one in the document. The event name is RmlUi's: `"click"` for buttons, `"change"` for anything the player adjusts, and a long list of others you can look up when you need them. One function is kept per element and per event, so setting another replaces it, and passing `nil` instead of a function removes it. Your function is called with five arguments -- the GUI, the document, the id, the event, and the element's current value as a string. Take as many as you want and ignore the rest, the way you already ignore arguments in Lua. [source,lua] ---- guiSetHandler(gui, page, "speed", "change", function(g, d, id, event, value) speed = math.floor(tonumber(value)) end) ---- The slider hands its value over as a string, and RmlUi formats numbers its own way, so `4` arrives as `"4.000000"`. `tonumber` turns the string into a number and `math.floor` throws away the fraction. Skipping that step and using the string as a number is a mistake you will make once. [source,lua] ---- guiSetValue(gui, page, "score", string.format("%06d", score)) ---- `guiSetValue` writes into an element. For a form control it sets the control's value; for anything else -- a paragraph, a `div`, the `` in your document -- it replaces what is inside the element, and the page lays itself out again on the next frame. `guiGetValue` is the same call backwards, and it answers `nil` for an id nothing has, so you can ask about an element that might not be there without dying. That is the whole everyday API: `guiSetHandler`, `guiSetValue`, `guiGetValue`. There is far more available through RmlUi's own Lua objects, reachable from the global `rmlui`, and the manual's GUI chapter shows how. You will get a long way before you need it. === Hide It Again A pause menu is not up all the time. `guiHide` takes a document off screen and `guiShow` puts it back, keeping everything the player typed or chose. The pause itself is `singeSetPauseFlag(true)`, which stops the disc, the videos, and the sounds but *keeps calling your callbacks*, so you can still draw. The engine has its own pause key, which freezes the script completely, and the two would fight; `singeSetPauseKeyEnabled(false)` takes that key away from the engine so `SWITCH_PAUSE` arrives at `onInputPressed` like any other switch. On a keyboard that key is *P*. [source,lua] ---- local function closeMenu() paused = false singeSetPauseFlag(false) guiHide(gui, page) guiSetInput(gui, false) end local function openMenu() paused = true singeSetPauseFlag(true) guiSetValue(gui, page, "score", string.format("%06d", score)) guiShow(gui, page) guiSetInput(gui, true) end ---- Turning input off with the page is not decoration. A hidden page that still takes input eats the player's arrow keys for the rest of the game. === What the Page Does Not Use, You Still Get This is the rule that makes a GUI safe to leave up during play, and it is worth saying plainly: *an event an element used never reaches your callbacks.* A Return that pressed the focused button does not arrive as `onInputPressed`. An arrow that moved the focus does not arrive as `SWITCH_LEFT`. A click on a button is not a mouse switch. Everything the page did not use falls straight through as usual. That is why `P` closes this menu: no element in the document uses `P`, so the key arrives at `onInputPressed` exactly as it does when the menu is down. It is also why a HUD that takes input still lets the fire button through. One key does not follow the rule. RmlUi uses Tab to move focus and never gives it back, so Singe only hands Tab to a GUI while a text field is actually being typed in. A game that binds Tab to something keeps it. === Style It Look back at the document's ``. One line does most of the work: [source,html] ---- ---- That is the theme the engine ships, and it is why your panel has a border, a gradient, and a gold heading without you choosing any of it. Without a style sheet RmlUi draws almost nothing, because every element starts out inline and sized to its contents, so linking this first and overriding what you dislike is the fastest road to a page that looks like something. The theme gives you `.panel` (a bordered, rounded box), `.list` (a scrolling box whose child `div` rows highlight and take focus), `.muted` (dimmer text for hints), headings, and every form control with its hover, focus, and pressed states. The manual's GUI chapter lists them. Everything after the link is yours. The `