Adding user data to windows

From SEGGER Wiki
Revision as of 10:43, 27 April 2020 by Florian (talk | contribs) (Created page with "The following tutorial will explain how '''user data''' can be set for windows and widgets in emWin. User data are additional bytes that are allocated upon creation of a windo...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

The following tutorial will explain how user data can be set for windows and widgets in emWin. User data are additional bytes that are allocated upon creation of a window or widget. They allow the user to store additional data that is associated with the window. The user data is typically processed by the window.

Button increasing a counter. The child window has access to the counter variable.

Objective of this tutorial

This tutorial will give an in-depth description on how user data can be set for windows and widgets in emWin and how this data can be used. The goal is an application that consists of a parent window, that has a BUTTON widget and another window as child windows.

Tutorial

Prerequirements

Before we create the child window and add user data to it, we are going to create the basis of the application. We need to include the headers DIALOG.h and stdio.h.

#include "DIALOG.h"
#include <stdio.h>

Then, we are going to add a MainTask() to our application that initializes emWin, creates the parent window and has a superloop.

void MainTask(void) {
  GUI_Init();
  //
  // Create parent window.
  //
  WM_CreateWindow(0, 0, LCD_GetXSize(), LCD_GetYSize(), WM_CF_SHOW, _cbParent, 0);

  while(1) {
    GUI_Delay(100);
  }
}

Next, we need to define the callback routine _cbParent() that is used by the parent window. Upon creation of the window (see WM_CREATE), a button is created to increase the counter. After that, the child window is created. In the WM_PAINT case, the window displays the value of the counter variable. At last, there is a WM_NOTIFY_PARENT case to react on the before mentioned button. When the button is clicked, the counter is increased and the parent window is redrawn, so it can display the new value.

static void _cbParent(WM_MESSAGE * pMsg) {
  static int Counter;
  WM_HWIN    hItem;
  int        Id, NCode;
  char       acBuffer[64];

  switch(pMsg->MsgId) {
  case WM_CREATE:
    hItem = BUTTON_CreateEx(10, 30, 100, 20, pMsg->hWin, WM_CF_SHOW, 0, GUI_ID_BUTTON0);
    BUTTON_SetText(hItem, "Increase Counter");
    //
    // Create child window with the size of an int pointer as extra bytes.
    //
    hChild = WM_CreateWindowAsChild(10, 70, 150, 100, pMsg->hWin, WM_CF_SHOW, _cbChild, 0);
    break;
  case WM_PAINT:
    GUI_SetBkColor(GUI_WHITE);
    GUI_Clear();
    //
    // Display counter.
    //
    GUI_SetColor(GUI_BLACK);
    GUI_SetFont(&GUI_Font16B_1);
    sprintf(acBuffer, "Counter: %d", Counter);
    GUI_DispStringAt(acBuffer, 10, 10);
    break;
  case WM_NOTIFY_PARENT:
    Id    = WM_GetId(pMsg->hWinSrc);
    NCode = pMsg->Data.v;
    switch(Id) {
    case GUI_ID_BUTTON0:
      switch(NCode) {
      case WM_NOTIFICATION_RELEASED:
        //
        // Increase counter when button is pressed and redraw window.
        //
        Counter++;
        WM_InvalidateWindow(pMsg->hWin);
        break;
      }
      break;
    }
    break;
  default:
    WM_DefaultProc(pMsg);
  }
}

We also add a callback routine _cbChild() for the child window. For now, it simply redraws the window as red.

static void _cbChild(WM_MESSAGE * pMsg) {  
  switch(pMsg->MsgId) {
  case WM_PAINT:
    GUI_SetBkColor(GUI_RED);
    GUI_Clear();
    break;
  default:
    WM_DefaultProc(pMsg);
  }
}

Add user data to window

To add user data to the child window, we need to set a number of bytes that will be allocated upon the window's creation. Therefore, we edit the last parameter of WM_CreateWindowAsChild() to the size of a pointer to an integer.

//
// Create child window with the size of an int pointer as extra bytes.
//
hChild = WM_CreateWindowAsChild(10, 70, 150, 100, pMsg->hWin, WM_CF_SHOW, _cbChild, sizeof(int *));

Next, we add a static int pointer variable.

static int * pCounter;

Right after creating the child window, we use this pointer to store the address of the counter.

//
// Save the address of the counter in a pointer variable.
//
pCounter = &Counter;

Once we prepared the user data we want to attach to the window, we can add the data using WM_SetUserData(). We pass the handle of the target window as first parameter, which is the child window. Secondly, the address of the pointer is given and lastly, the byte size of the pointer variable.

//
// Set the user data to child window.
// The address of the pointer is the source of the user data.
//
WM_SetUserData(hChild, &pCounter, sizeof(int *));

Retrieve user data

To retrieve user data, we need to react on a WM_USER_DATA message in the window that received user data. Therefore, we add the code below to the _cbChild() callback.

case WM_USER_DATA:
  //
  // Get user data of this window and save it at the address of our pointer.
  //
  WM_GetUserData(pMsg->hWin, &pCounter, sizeof(int *));
  break;

We use the routine WM_GetUserData() to receive user data from the window. Again, we need to define a static int pointer to save the user data in that variable.

static int * pCounter;

Once the user data is retrieved, we can process it in the WM_PAINT case of the window. A buffer for output of the counter value also needs to be added.

char acBuffer[64];
...
//
// Check if pointer is valid.
//
if (pCounter) {
  GUI_SetColor(GUI_WHITE);
  sprintf(acBuffer, "Counter: %d", *pCounter);
  GUI_DispStringAt(acBuffer, 10, 10);
}

If we now run our application, the behavior is just as described in the introduction.

User data for widgets

The procedure of adding user data to widgets is exactly the same as described above. Alternatively to WM_SetUserData()/WM_GetUserData(), the equivalent of the corresponding widget can be called (<WIDGET>_SetUserData()), such as BUTTON_SetUserData().

Downloads

You can download the source code of this example here.