## Chapter 18: Graphical User Interfaces

## Chapter Goals

- To learn about event driven programming
- To learn how to use an application framework
- To implement menus and buttons and their associated actions
- To understand the concept of layout management for graphical user interface components
- To understand window repainting
- To be able to implement simple applications with graphical user interfaces

## The wxWindows Toolkit

- Modern operating systems provide libraries for GUI programming.
- The most commonly used C++ GUI toolkit is the MFC (Microsoft Foundation Classes), which is used to write Microsoft Windows programs.
- We will use a different toolkit, called wxWindows:
  - wxWindows is freely available.
  - wxWindows runs on several platforms (including Linux and Macintosh)
  - wxWindows works with a large number of compilers
  - wxWindows is more transparent to the beginning programmer.
  - wxWindows is structurally similar to MFC
- You can download the wxWindows software from `www.wxwindows.org`

## Frames

- To get started with wxWindows, we will write a very simple program that simply puts up a frame, a window with the typical decorations that the windowing system provides.
- The icons for minimizing or closing the window (etc.) vary from system to system, but are not important to our discussion.
- To use the wzWindows toolkit in your program, you need to include the header file `wx/w.h`.
- To the define a class that contains details about the workings of your application.

  >     class BasicApp: public wxApp
  >     {
  >     public:
  >        BasicApp();
  >        virtual bool OnInit(); /* override base class implementation */
  >     private:
  >        wxFrame* frame; /* this application has a frame */
  >     };

## Frames

- The frame is initialized in the constructor.

  >     BasicApp::BasicApp()
  >     {
  >        frame = new wxFrame(NULL, -1,
  >           "My First GUI Program");
  >     }

- The parameters indicate the frame has no parent window, a default window ID, and is called "My First GUI Program"
- In the `OnInit` functions, you show the frame and return true to indicate that the initialization was successful.

  >     bool BasicApp::OnInit()
  >     {
  >        frame->Show(true);
  >        return true;
  >     }

## Frames

- Note that the names in wxWindows (such as `OnInit` and `Show`) do not follow the same style guidelines as the text.
- Two preprocessor macros carry out some magic that the framework needs to make an application out of the `BasicApp` class.

  >     DECLARE_APP(BasicAPP)
  >     IMPLEMENT_APP(BasicAPP)

- If you distribute the code into separate files
  - place `DECLARE_APP` in the `.h` file
  - place `IMPLEMENT_APP` in the `.cpp` file
- The wxWindows toolkit supplies an application framework - several base classes from which programmers derive classes to specify the behavior of their application.
- The application framework does complex work to interface with the operating system, but the programmer is insulated from these technical details.
- The instructions for compiling a wxWindows program differs quite a bit depending on compiler and platform.

## Frames (`basic.cpp`)

## Adding a Text Control to the Frame

- Again, we use inheritance to define our own, more interesting, type of frame.
- This frame contains a text area into which users can type any text.

  >     class TextFrame : public wxFrame
  >     {
  >     public:
  >        TextFrame();
  >     private:
  >        wxTextCtrl* text;
  >     };

- The constructor initializes the base class and the text control:

  >     TextFrame::TextFrame()
  >        : wxFrame(NULL, -1, "TextFrame")
  >     {
  >        text = new wxTextCtrl(this, -1, "Type some text here!",
  >           wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
  >     }

- Note that first parameter (`this`) indicates that the parent of the text control is this frame.
- The next two parameters indicate a default window ID and initial contents of the text control.
- The next two parameters indicate default size and position.
- The final parameter turns on the "multiline" style, which allows the text control to hold multiple lines of text.

## Adding a Text Control to the Frame

- We have to make a slight change to the application class to show a `TextFrame`, not a `wxFrame`.

  >     class TextApp : public wxApp
  >     {
  >     public:
  >        TextApp();
  >        virtual bool OnInit();
  >     private:
  >        TextFrame* frame;
  >     };

  >     TextApp::TextApp()
  >     {
  >        frame = new TextFrame();
  >     }

## Adding a Text Control to the Frame (`text.cpp`)

## Menus

- In this step, we will add a menu to the sample application.
- We will create an application with a menu bar that contains the names of top-level menus.
- There will be a single top-level menu named "Say" that contains two menu items, "Hello" and "Goodbye".

## Menus

- Each menu item must have an integer ID number, used to match up menu items with their actions.

  >     const int ID_SAY_HELLO = 1000;
  >     const int ID_SAY_GOODBYE = 1001;

- Now construct a menu and append the two menu items to it.

  >     wxMenu* menu = new wxMenu();
  >     menu->Append(ID_SAY_HELLO, "Hello");
  >     menu->Append(ID_SAY_GOODBYE, "Goodbye");

- Finally, constructor a menu bar object, set it as the menu bar of the frame, and append the menu.

  >     wxMenuBar* menu_bar = new wxMenuBar();
  >     SetMenuBar(menu_bar);
  >     menu_bar->Append(menu, "Say);

## Menus (`menu.cpp)`

## Event Handling

- We will now attach actions to the menu items.
- Define a function for each action.

  >     void EventFrame::OnSayHello(wxCommandEvent& event)

  >     {
  >        text->AppendText("Hello, World!\n");
  >     }

- We want this function to run whenever the program user selects the menu option.
- All event handler functions have a parameter that describes the triggering event.
- You must declare the function this way or get a bewildering error message (note that the parameter is not used in the function).

## Event Handling

- We create an event table out of macros that automatically produce correct code.
- The generic form of the event table is:

  >     BEGIN_EVENT_TABLE(ClassName, BaseClassName)
  >        EVT_TYPE(parameters, function)
  >        . . .
  >     END_EVENT_TABLE()

- There are several different event types; here we use the `MENU` event type that requires a menu ID as a parameter.

  >     BEGIN_EVENT_TABLE(EventFrame, wxFrame)
  >        EVT_MENU(IS_SAY_HELLO, EVentFrame::OnSayHello)
  >     END_EVENT_TABLE()

- We need to insert another macro into the definition of each class that has an event table.

  >     class EventFrame : public wxFrame
  >     {
  >        . . .
  >     private:
  >        wxTextCtrl* text;
  >        DECLARE_EVENT_TABLE()
  >     };

## Event Handling (`event.cpp`)

## Layout Management

- The next sample program uses buttons instead of menus.
- Each button has an ID, and the event table matches the ID with a function.

  >     BEGIN_EVENT_TABLE(ButtonFrame, wxFrame)
  >        EVT_BUTTON(IS_SAY_HELLO, ButtonFrame::OnSayHello)
  >     END_EVENT_TABLE()

- When you construct a button, you specify the parent window, the button ID, and the button label.

  >     wxButton* hello_button = new wxButton(this,
  >        IS_SAY_HELLO, "Say Hello");

- The event handler has the same form as the menu event handler.

  >     void ButtonFrame::OnSayHello(wxCommandEvent& event)
  >     {
  >        text->AppendText("Hello, World!\n");
  >     }

## Layout Management

- The problem is making user the buttons are placed correctly inside the frame.
- Some user interface toolkits supply a graphical layout tool to define the placement of buttons, text controls, and other user interface elements in a frame.
- Such tools make designing a user interface simple, but the design tends to be fragile.
- If a component size changes (perhaps the button text is translated to another language: "Goodbye" to "Auf Wiedersehen") the entire layout needs to be rebuilt.
- More robust layouts require a description of the logic behind the placement of the interface elements.

## Layout Management

- In wxWindows, use objects of the `wxSizer` class or one of its derived classes (such as `wxBoxSizer`) to specify the layout of user interface elements.
- These buttons would be lined up horizontally:

  >     wxBoxSizer* button_sizer = new wxBoxSizer(wxHORIZONTAL);
  >     button_sizer->Add(hello_button);
  >     button_sizer->Add(goodbye_button);

- A second sizer places the text control on top of the button row.

  >     wxBoxSizer* frame_sizer = new wxBoxSizer(wxVERTICAL);
  >     frame_sizer->Add(text, 1, wxGROW);
  >     frame_sizer->Add(button_sizer, 0, wxALIGN_CENTER);

## Layout Management

- You must include commands to turn on auto layout and tell the frame which sizer to use.

  >     SetAutoLayout(true);
  >     SetSizer(frame_sizer);

- The additional parameters in the `Add` function explain how the layout is maintained when the user resizes.

  >     wxBoxSizer* frame_sizer = new wxBoxSizer(wxVERTICAL);
  >     frame_sizer->Add(text, 1, wxGROW);
  >     frame_sizer->Add(button_sizer, 0, wxALIGN_CENTER);

- The second parameter indicates the ratio of size used in vertical components.
  - 0 means don't resize.
  - If you had a component with parameter 1, and another with parameter 2, the second would always use twice as much (vertical) space as the first.
- The third parameter describes horizontal growth behavior.
  - The text control grows with the window.
  - The button bar doesn't grow, but should be centered.

## Layout Management (`button.cpp)`

## Painting

- Drawing images in a windowing environment is not quite as straightforward as you might think.
  - What happens when a user re-sizes the window? ... minimizes the window? ... restores the window?
  - What happens when another frame pops up over the image and then vanishes?
- The program has no idea when these events will happen.
- When they do happen, the window manager knows the contents of the window have been corrupted and sends a paint event to the program.
- The program needs to draw the image not just once, but every time a paint even occurs.
- All drawing instructions must be placed into a function that is is the target of paint events.

  >     EVT_PAINT(EllipseWindow::OnPaint)

## Painting

- The paint function obtains a device context, which is an object that represents the surface of the window.
- The device context class has drawing functions such as `DrawLine`, `DrawEllipse`, and `DrawText`.

  >     void EllipseWindow::OnPaint(wxPaintEvent& event)
  >     {
  >        wxPaintDC dc(this);
  >        dc.SetBrush(*wxTRANSPARENT_BRUSH);
  >        wxSize size = GetSize();
  >        int x = 0;
  >        int y = 0;
  >        int width = size.GetWidth();
  >        int height = size.GetHeight();
  >        dc.DrawEllipse(x, y, width, height);
  >     }

## Painting

- The `DrawEllipse` function requires you specify the top left corner of the bounding box.
- The device context coordinates are in pixels, with the point (0, 0) in the top left corner.

## Painting

- The `OnPaint` function does not draw on the application frame, but on a separate window of type `EllipseWindow`, derived from `wxWindow`.
- We believe that an "ellipse window" is a user interface element, just like a text control or button, and deserves it's own class.
- We will see another example later.

## Painting (`paint.cpp`)

## Mouse Events

- To handle mouse input in a graphical window, you install a function that is notified when mouse events occur.
- There are several kinds of mouse events:
  - motion
  - dragging (moving while depressing a mouse button)
  - mouse button going down
  - mouse button going up
  - clicking (mouse button going up and down within a short period)
  - double clicking
- You install the mouse handler with the `EVT_MOUSE_EVENTS` macros.

  >     BEGIN_EVENT_TABLE(TriangleWindow, wxWindow)
  >        EVT_MOUSE_EVENTS(TriangleWindow::OnMouseEvent)
  >        . . .

## Mouse Events

- In the notification functions, you can query the `wxMouseEvent` about the event type (e.g. `ButtonDown()` returns `true` or `false`, `GetX()` returns the x-position of the mouse, etc.).

  >     void TriangleWindow::OnMouseEvent(wxMouseEvent& event)
  >     {
  >        if (event.ButtonDown() && corners < 3)
  >        {
  >           x[corners] = event.GetX();
  >           y[corners] = event.GetY();
  >           corners++;
  >           Refresh();
  >        }
  >     }

- In our program, we allow the user to specify a triangle by clicking on the three corners.
- With each press, we record the mouse position.
- All drawing should happen in the paint handler.
- The `Refresh` function generates a paint event, which eventually causes the paint function to be called.
- Calling the paint function directly should never be done - always refresh instead.

## Mouse Events

- Depending on the number of corners that have already been specified, the paint handler draws
  - a small circle for the first mouse click
  - a line after the first two mouse clicks
  - a triangle after three mouse clicks

>     void TriangleWindow::OnPaint(wxPaintEvent & event)
>     {
>        const int RADIUS = 2;
>        wxPaintDC(this);
>        if (corners == 1)
>           dc.DrawEllpse(x[0] - RADIUS, y[0] - RADIUS,
>              2 * RADIUS, 2 * RADIUS);
>        if (corners >= 2)
>           dc.DrawLine(x[0], y[0], x[1], y[1]);
>        if (corners >= 3)
>        {
>           dc.DrawLine(x[1], y[1], x[2], y[2]);
>           dc.DrawLine(x[2], y[2], x[0], y[0]);
>        }
>     }

## Mouse Events (`mouse.cpp`)

## Dialogs

- A mode restricts what a user can do at any given time, or interprets a user in a way that depends on the mode.
- Example: In over type mode in a word processor, the typed characters replace existing characters instead of inserting themselves before the cursor.
- Experience has shown that modes burden program users - the user must expend some mental effort and keep track of the current mode.
- When designing a user interface, it is generally preferred to minimize modes.

## Dialogs

- Another example of a special program mode is a dialog box that requires immediate input from the user.
- Because the user can do nothing else except fill or cancel the dialog, it can be a burden.
- Nevertheless, modal dialog boxes are necessary whenever a program simply cannot proceed with user intervention.
- The wxWindows toolkit makes is easy to program several kinds of dialogs.

  >     wxMessageDialog* dialog = new wxMessageDialog(parent, message);
  >     dialog->ShowModal();
  >     dialog->Destroy();

- The dialog pops up and is displayed until the user clicks the "OK" button.

## Dialogs

- The parent parameter is a pointer to the parent window, over which the dialog box is placed.
- The message parameter is of type `wxString`, a class that is similar to the standard `string` type.
- We recommend you use the standard string class for all string computations, then use the `c_str` function to convert to a C string, which is then automatically converted to a `wxString`.

  >     string message = "Hello, " + name;
  >     dialog = new wxMessageDialog(this, message.c_str());

- When you are done with a dialog, you should destroy it.

## Dialogs

- Another convenient dialog is the text entry dialog that asks the user to supply a single line of text.

  >     wxTextEntryDialog* dialog = new wxTextEntryDialog(this,
  >        "What is your name?";
  >     dialog->ShowModal();
  >     string name = dialog->GetValue().c_str();
  >     dialog->Destroy();

- The `GetValue` function returns a `wxString`. That class also has a `c_str` function to convert to a C string.

## A Complete Example

- The final example is a wxWindows version of the clock game of Chapter 13.
- Because of the event-driven nature of GUI programming, several modifications had to be made to the program logic.
- In this program, each guess is communicated to the program in the handler of the "Guess" button.
- A data field `tries` has been added to keep track of whether the guess is a first or second guess.
- The user selects menu options that lead to dialog boxes to enter the user name and choose level.
- Now the user can change level at any time during the game.
- To simplify compilation, the nonstandard Point and Time classes have been eliminated from the program.

## A Complete Example

- Classes of the Clock Game

## A Complete Example (`game.cpp`)