Customizing windows

From SEGGER Wiki
Revision as of 13:32, 1 April 2020 by Florian (talk | contribs) (Created page with "The following tutorial will demonstrate and explain in detail how a window can be customized in emWin. == Step-by-step tutorial == === Prerequirements === In order to be ab...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

The following tutorial will demonstrate and explain in detail how a window can be customized in emWin.

Step-by-step tutorial

Prerequirements

In order to be able to use emWin's features and the Window Manager, the file DIALOG.h has to be included.

#include "DIALOG.h"

Then we should add a MainTask() to the application, that initializes emWin and frequently calls emWin in a superloop.

void MainTask(void) {  
  //
  // Init GUI.
  //
  GUI_Init();
  //
  // When calling the delay routine, GUI_Exec() is called which executes emWin operations.
  //
  while (1) {
    GUI_Delay(100);
  }
}

Create a window

The next step to do is creating a window, that we will customize in the further steps of this tutorial. After emWin has been initialized in MainTask(), we can create a window by executing the following statement.

WM_CreateWindowAsChild(0, 0, 100, 100, WM_HBKWIN, WM_CF_SHOW, _cbWin, 0);

This will create a new window placed at the pixel position x = 0, y = 0 with the size of 100 pixels in height and width. The child window's parent is the "background window" which can be accessed via the macro WM_HBKWIN. For the next parameter, window create flags have to be specified. With WM_CF_SHOW, the window will be made visible after is has been created. Next, we specify _cbWin() as the window's callback. Lastly, we don't have to allocate additional bytes for this window, so we set 0 as the number of extra bytes. The callback has not been created yet, this will be done in the next step.

Create the window's callback routine

A window (or widget) callback has the following prototype.

typedef void WM_CALLBACK(WM_MESSAGE * pMsg);

With this prototype, we can create our own callback that we will use for the window we just created in the previous step. When a message is sent to a window, it will be processed by the window's callback routine. Therefore, we have to process the messages by checking the message ID, which is stored in the MsgId member of the WM_MESSAGE structure.

static void _cbWin(WM_MESSAGE * pMsg) {
  switch(pMsg->MsgId) {
  default:
    WM_DefaultProc(pMsg);
  }
}

First, we add a default case to the callback and call WM_DefaultProc(). This will ensure, that all other messages not handled by the user's callback will be handled by the default callback.

Add a WM_PAINT case

By adding a WM_PAINT case to the callback, we are defining how the window will be drawn.

case WM_PAINT:
  GUI_SetBkColor(GUI_RED);
  GUI_Clear();
  break;

By calling GUI_SetBkColor(GUI_RED) and GUI_Clear(), the entire window will be filled with red.

When we now execute our application, we will see the red window in the top left corner of the screen.

React on mouse input

For the next step, we want to define the behavior of the window when it receives a mouse click. If the window is clicked, it should change its color to green. When the click is released, the window should be red again.

In order that the window can react on mouse clicks, we have to add a WM_PID_STATE_CHANGED case to the window callback.

case WM_PID_STATE_CHANGED:
  break;

When the window receives a WM_PID_STATE_CHANGED message, a pointer to a WM_PID_STATE_CHANGED_INFO structure is sent with the message. The pointer can be accessed via pMsg->Data.p. We are going to save this pointer in a variable.

WM_PID_STATE_CHANGED_INFO * pInfo;
...
pInfo = (WM_PID_STATE_CHANGED_INFO *)pMsg->Data.p;

Now, we can access the data about the pointer input device (PID) state. The element pInfo->State is 0 if the mouse is unpressed and 1 if the mouse is pressed. Since we need to process this value in the WM_PAINT case, we are saving it in a static (!) integer variable. The variable has to be static so its value will be still be persistent when the callback is called the next time.

static int Pressed;
...
Pressed = pInfo->State;
WM_InvalidateWindow(pMsg->hWin);

After the PID state has been saved, we are also invalidating the window, which will ensure that it will be redrawn again. If we wouldn't do that, the state would be saved but no changes would appear on the screen. We specify the current window with pMsg->hWin. The hWin element of the WM_MESSAGE structure contains the window handle of the window the message is sent to.

Now that we saved the pressed state, we can define the drawing so that the window will appear green when it is clicked.

case WM_PAINT:
  if(Pressed) {
    GUI_SetBkColor(GUI_GREEN);
  } else {
    GUI_SetBkColor(GUI_RED);
  }
  GUI_Clear();
  break;

After this step is done, the callback should look like shown below. When executing the application, the window will appear green while it is clicked and red when it isn't.

static void _cbWin(WM_MESSAGE * pMsg) {
  WM_PID_STATE_CHANGED_INFO * pInfo;
  static int                  Pressed;

  switch(pMsg->MsgId) {
  case WM_PAINT:
    if(Pressed) {
      GUI_SetBkColor(GUI_GREEN);
    } else {
      GUI_SetBkColor(GUI_RED);
    }
    GUI_Clear();
    break;
  case WM_PID_STATE_CHANGED:
    pInfo = (WM_PID_STATE_CHANGED_INFO *)pMsg->Data.p;
    Pressed = pInfo->State;
    WM_InvalidateWindow(pMsg->hWin);
    break;
  default:
    WM_DefaultProc(pMsg);
  }
}