From 562072780e467fd1182494e945b15121a807cc4f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Apr 2015 13:18:26 -0400 Subject: [PATCH] Started writing the Windows new control documentation. --- docs/newcontrol_windows.md | 102 +++++++++++++++++++++++++++++++++++++ ui_windows.h | 1 + 2 files changed, 103 insertions(+) create mode 100644 docs/newcontrol_windows.md diff --git a/docs/newcontrol_windows.md b/docs/newcontrol_windows.md new file mode 100644 index 00000000..0d250209 --- /dev/null +++ b/docs/newcontrol_windows.md @@ -0,0 +1,102 @@ +# Writing new controls for the Windows backend + +The Unix backend uses the raw Windows API to do its UI work. The good news is that you don't need to implement most of uiControl to make things work. The bad news is that you *do* need to implement uiControlPreferredSize(). (TODO rewrite this?) + +For this document, we will write a basic wrapper around the [trackbar control](xxxTODOxxxx) as a slider that goes from 0 to 99 inclusive. It will conform to the following interface: + +```c +typedef struct mySlider mySlider; +struct mySlider { + uiControl base; + int (*Pos)(mySlider *); +}; +#define mySlider(x) ((mySwitch *) (x)) +#define mySliderPos(s) ((*((s)->Pos))((s))) +``` + +Error checking will be omitted for the purposes of demonstration. + +To be able to create Windows backend controls, we need to include `ui_windows.h`, the file that contains the Windows backend function declarations. It requires that you include both `ui.h` and `` (and any other macros like `UNICODE` and Windows API headers like ``) beforehand: + +```c +#define UNICODE +#define _UNICODE +#define STRICT +#include +#include +#include "ui.h" +#include "ui_windows.h" +``` + +Before we can create trackbars, though, we need to make sure the trackbar control class is loaded. As the trackbar is provided by the Common Controls library, we do this with the `InitCommonControlsEx()` function: + +```c +static void registerSliderClass(void) +{ + static int initialized = 0; + INITCOMMONCONTROLSEX icc; + + if (initialized) + return; + initialized = 1; + ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX)); + icc.dwSize = sizeof (INITCOMMONCONTROLSEX); + icc.dwICC = xxxTODOxxxxxx; + InitCommonControlsEx(&icc); +} +``` + +Now that we have that done, we can move on to writing the control itself. The first thing we need to do is define the data structure used for our new control. The first field of this data structure must be a mySlider (not a pointer to a mySlider). This allows us to use our data structure *as* the mySlider: + +```c +struct slider { + mySlider s; +``` + +We'll also store a copy of the slider's HWND to make things easier for us: + +```c + HWND hwnd; +}; +``` + +The good news is that most of the work of implementing uiControl is done for you by libui. You only need to provide a few things: + +- the parameters to the `CreateWindowExW()` call that creates the control's window +- handlers for `WM_COMMAND` and `WM_NOTIFY` that handle any `WM_COMMAND` and `WM_NOTIFY` messages the control sends to its parent window +- a handler for `WM_DESTROY` that does any cleanup that needs to be done +- an implementation of `uiControlPreferredSize()` +- the other methods of your interface + +We'll deal with the first bullet later. + +The handlers for `WM_COMMAND` and `WM_NOTIFY` that libui expects to see have specific signatures: + +```c +BOOL onWM_COMMAND(uiControl *c, WORD code, LRESULT *lResult); +BOOL onWM_NOTIFY(uiControl *c, NMHDR *nm, LRESULT *lResult); +``` + +You'll notice that they each take three parameters and return a `BOOL`. The first parameter to each is the uiControl itself. The last parameter is the address of an `LRESULT`. If your handler returns non-`FALSE`, libui will return the value you store in that `LRESULT` as the return value for the message. Otherwise, libui will send the message down the subclass chain. + +The second parameter is what's different. In the case of `WM_COMMAND`, this is just the command code (equivalent to `HIWORD(wParam)` in a real `WM_COMMAND` handler). In the case of `WM_NOTIFY`, this is the `NMHDR` of the notification (equivalent to `(NMHDR *) lParam` in a real `WM_NOTIFY` handler). + +The trackbar doesn't use `WM_COMMAND` at all, so we can simply return `FALSE` for that one: + +```c +static BOOL onWM_COMMAND(uiControl *c, WORD code, LRESULT *lResult) +{ + return FALSE; +} +``` + +A trackbar *does* use `WM_NOTIFY`, however. For the purposes of this example, we won't be processing notifications. If you were to add some event, such as `OnChanged()`, you would handle that here (in this case, via `xxTODOxxx`). + +```c +static BOOL onWM_NOTIFY(uiControl *c, NMHDR *nm, LRESULT *lResult) +{ + return FALSE; +} +``` + +ok TODO this was a bad example diff --git a/ui_windows.h b/ui_windows.h index f249ae15..f2bfd4bd 100644 --- a/ui_windows.h +++ b/ui_windows.h @@ -26,6 +26,7 @@ struct uiWindowsNewControlParams { // Store the result in *lResult and return any non-FALSE value (such as TRUE) to return the given result; return FALSE to pass the notification up to your window procedure. // Note that these are only issued if they come from the uiControl itself; notifications from children of the uiControl (such as a header control) will be received normally. BOOL (*onWM_COMMAND)(uiControl *c, WORD code, LRESULT *lResult); + // TODO set idFrom to 0? BOOL (*onWM_NOTIFY)(uiControl *c, NMHDR *nm, LRESULT *lResult); // This is called in WM_DESTROY. void (*onWM_DESTROY)(uiControl *c);