Difference between revisions of "Creating a custom rendering routine"
(Fix typo) |
|||
(20 intermediate revisions by 2 users not shown) | |||
Line 3: | Line 3: | ||
== The interface == | == The interface == | ||
Every widget provides a function to submit a custom rendering function: | Every widget provides a function to submit a custom rendering function: | ||
− | <syntaxhighlight lang=c> | + | <syntaxhighlight lang="c"> |
− | void gwinSetCustomDraw(GHandle gh, CustomWidgetDrawFunction fn, void*param); | + | void gwinSetCustomDraw(GHandle gh, CustomWidgetDrawFunction fn, void* param); |
</syntaxhighlight> | </syntaxhighlight> | ||
The ''CustomWidgetDrawFunction'' is a typedef'ed function pointer: | The ''CustomWidgetDrawFunction'' is a typedef'ed function pointer: | ||
<syntaxhighlight lang=c> | <syntaxhighlight lang=c> | ||
− | typedef void (*CustomWidgetDrawFunction)( | + | typedef void (*CustomWidgetDrawFunction)(GWidgetObject* gw, void* param); |
</syntaxhighlight> | </syntaxhighlight> | ||
Line 19: | Line 19: | ||
== Widget information == | == Widget information == | ||
− | In order to render the widget you need some information about it (eg. position, size, font, ...). All these information can be fetched from the ''GWidgetObject'' struct which is passed as a parameter to the custom rendering function. This is the only time where you're not only allowed to but even obligated to directly access the | + | In order to render the widget you need some information about it (eg. position, size, font, ...). All these information can be fetched from the ''GWidgetObject'' struct which is passed as a parameter to the custom rendering function. This is the only time where you're not only allowed to but even obligated to directly access the struct members directly instead of using the equivalent GWIN API calls to retrieve these information. Do never access struct members directly outside of the custom rendering routine! |
− | + | ||
+ | '''''Important:''' Do never use the <code>gwinDrawXxx()</code> calls inside a rendering routine as this would lock the widget again. Use <code>gdispDrawXxx()</code> instead.'' | ||
+ | |||
+ | To retrieve all the widget specific information the ''GWidgetObject'' struct needs to be casted to the actual widget type object. | ||
+ | |||
+ | '''''Important:''' All drawing operations must take place inside of the widget area. Do not draw outside of the widget area.'' | ||
+ | |||
+ | == Example == | ||
+ | We are now going to show on a real world example how a custom rendering routine can be implemented. We will write a custom rendering routine for the button widget that will allow to display an icon on the left side of the text. We will keep it as simple as possible: We won't do color blending for gradients and other things that would make this look nicer in order to focus on the things that are actually important. The expected result can be seen in the image on the right. | ||
+ | [[File:Buttons custom rendering.png|thumbnail|The expected result. The ''Settings'' button on the right side is being pressed]] | ||
+ | |||
+ | === Implementation === | ||
+ | <source lang="c" line="1"> | ||
+ | void myButtonRendering(GWidgetObject* gw, void* param) | ||
+ | { | ||
+ | const GColorSet* colors; | ||
+ | |||
+ | // Get the icon/image. User must open the image beforehand. | ||
+ | gdispImage* icon = (gdispImage*)param; | ||
+ | if (!icon) { | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | // Get the appropriate color pallete from the widget style | ||
+ | if (!gwinGetEnabled((GHandle)gw)) | ||
+ | colors = &gw->pstyle->disabled; | ||
+ | else if ((gw->g.flags & GBUTTON_FLG_PRESSED)) | ||
+ | colors = &gw->pstyle->pressed; | ||
+ | else | ||
+ | colors = &gw->pstyle->enabled; | ||
+ | |||
+ | // Draw the basic rectangle with border | ||
+ | gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, colors->fill); | ||
+ | gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, colors->edge); | ||
+ | |||
+ | // Draw the string. Use StringBox() for proper justification and word-wrapping support | ||
+ | gdispGDrawStringBox(gw->g.display, gw->g.x+gw->g.height, gw->g.y, gw->g.width-gw->g.height, gw->g.height, gw->text, gw->g.font, colors->text, justifyLeft); | ||
+ | |||
+ | // Draw the image | ||
+ | gdispGImageDraw(gw->g.display, icon, gw->g.x+(gw->g.height-icon->width)/2, gw->g.y+(gw->g.height-icon->height)/2, icon->width, icon->height, 0, 0); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | === Usage === | ||
+ | The following program will show how the same rendering routine can be applied to two buttons that are using two different icons: | ||
+ | <source lang="c" line="1"> | ||
+ | #include "gfx.h" | ||
+ | |||
+ | GHandle ghButton1; | ||
+ | GHandle ghButton2; | ||
+ | gdispImage iconWrench; | ||
+ | gdispImage iconUgfx; | ||
+ | font_t font1; | ||
+ | font_t font2; | ||
+ | |||
+ | void myButtonRendering(GWidgetObject* gw, void* param) | ||
+ | { | ||
+ | const GColorSet* colors; | ||
+ | |||
+ | // Get the icon/image | ||
+ | gdispImage* icon = (gdispImage*)param; | ||
+ | if (!icon) { | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | // Get the appropriate color pallete from the widget style | ||
+ | if (!gwinGetEnabled((GHandle)gw)) | ||
+ | colors = &gw->pstyle->disabled; | ||
+ | else if ((gw->g.flags & GBUTTON_FLG_PRESSED)) | ||
+ | colors = &gw->pstyle->pressed; | ||
+ | else | ||
+ | colors = &gw->pstyle->enabled; | ||
+ | |||
+ | // Draw the basic rectangle with border | ||
+ | gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, colors->fill); | ||
+ | gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, colors->edge); | ||
+ | |||
+ | // Draw the string. Use StringBox() for proper justification and word-wrapping support | ||
+ | gdispGDrawStringBox(gw->g.display, gw->g.x+gw->g.height, gw->g.y, gw->g.width-gw->g.height, gw->g.height, gw->text, gw->g.font, colors->text, justifyLeft); | ||
+ | |||
+ | // Draw the image | ||
+ | gdispGImageDraw(gw->g.display, icon, gw->g.x+(gw->g.height-icon->width)/2, gw->g.y+(gw->g.height-icon->height)/2, icon->width, icon->height, 0, 0); | ||
+ | } | ||
+ | |||
+ | static void createWidgets(void) { | ||
+ | GWidgetInit wi; | ||
+ | |||
+ | // Prepare the images | ||
+ | gdispImageOpenFile(&iconWrench, "settings.gif"); | ||
+ | gdispImageOpenFile(&iconUgfx, "padlock.gif"); | ||
+ | |||
+ | // Apply some default values for GWIN | ||
+ | gwinWidgetClearInit(&wi); | ||
+ | wi.g.show = TRUE; | ||
+ | |||
+ | // Create a regular button | ||
+ | wi.g.width = 180; | ||
+ | wi.g.height = 52; | ||
+ | wi.g.y = 10; | ||
+ | wi.g.x = 10; | ||
+ | wi.customParam = &iconWrench; | ||
+ | wi.customDraw = myButtonRendering; | ||
+ | wi.text = "Setting"; | ||
+ | ghButton1 = gwinButtonCreate(0, &wi); | ||
+ | |||
+ | // Create a regular button | ||
+ | wi.g.width = 180; | ||
+ | wi.g.height = 52; | ||
+ | wi.g.y = 70; | ||
+ | wi.g.x = 10; | ||
+ | wi.customParam = &iconUgfx; | ||
+ | wi.customDraw = myButtonRendering; | ||
+ | wi.text = "Lock"; | ||
+ | ghButton2 = gwinButtonCreate(0, &wi); | ||
+ | } | ||
+ | |||
+ | int main(void) | ||
+ | { | ||
+ | gfxInit(); | ||
+ | |||
+ | gwinSetDefaultFont(gdispOpenFont("*")); | ||
+ | gwinSetDefaultStyle(&WhiteWidgetStyle, FALSE); | ||
+ | gdispClear(White); | ||
− | + | createWidgets(); | |
− | + | while(1) { | |
− | + | gfxSleepMilliseconds(250); | |
+ | } | ||
+ | } | ||
+ | </source> |
Latest revision as of 12:39, 20 August 2023
Every widget comes with a custom render interface. The default style in which a widget is drawn is very basic and minimalistic in order to make it run on the even lowest performance systems smoothly. However, the custom render interface allows you to submit your own rendering routines. This does not only provide a very flexible way to render a widget matching to your systems performance, but it gives you also the possibility to render a widget matching your applications style.
The interface
Every widget provides a function to submit a custom rendering function:
void gwinSetCustomDraw(GHandle gh, CustomWidgetDrawFunction fn, void* param);
The CustomWidgetDrawFunction is a typedef'ed function pointer:
typedef void (*CustomWidgetDrawFunction)(GWidgetObject* gw, void* param);
The param parameter can be used to pass a custom parameter such as an image pointer in case of you're rendering routine needs to draw an image. However, in most of the cases, this parameter will be NULL.
Note: The pointer to the custom rendering routine can also be passed through the initialization struct (the customDraw field).
Note: Do never use the gwinDrawXxx()
calls inside a rendering routine as this would lock the widget again. Use gdispDrawXxx()
instead
Widget information
In order to render the widget you need some information about it (eg. position, size, font, ...). All these information can be fetched from the GWidgetObject struct which is passed as a parameter to the custom rendering function. This is the only time where you're not only allowed to but even obligated to directly access the struct members directly instead of using the equivalent GWIN API calls to retrieve these information. Do never access struct members directly outside of the custom rendering routine!
Important: Do never use the gwinDrawXxx()
calls inside a rendering routine as this would lock the widget again. Use gdispDrawXxx()
instead.
To retrieve all the widget specific information the GWidgetObject struct needs to be casted to the actual widget type object.
Important: All drawing operations must take place inside of the widget area. Do not draw outside of the widget area.
Example
We are now going to show on a real world example how a custom rendering routine can be implemented. We will write a custom rendering routine for the button widget that will allow to display an icon on the left side of the text. We will keep it as simple as possible: We won't do color blending for gradients and other things that would make this look nicer in order to focus on the things that are actually important. The expected result can be seen in the image on the right.
Implementation
void myButtonRendering(GWidgetObject* gw, void* param)
{
const GColorSet* colors;
// Get the icon/image. User must open the image beforehand.
gdispImage* icon = (gdispImage*)param;
if (!icon) {
return;
}
// Get the appropriate color pallete from the widget style
if (!gwinGetEnabled((GHandle)gw))
colors = &gw->pstyle->disabled;
else if ((gw->g.flags & GBUTTON_FLG_PRESSED))
colors = &gw->pstyle->pressed;
else
colors = &gw->pstyle->enabled;
// Draw the basic rectangle with border
gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, colors->fill);
gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, colors->edge);
// Draw the string. Use StringBox() for proper justification and word-wrapping support
gdispGDrawStringBox(gw->g.display, gw->g.x+gw->g.height, gw->g.y, gw->g.width-gw->g.height, gw->g.height, gw->text, gw->g.font, colors->text, justifyLeft);
// Draw the image
gdispGImageDraw(gw->g.display, icon, gw->g.x+(gw->g.height-icon->width)/2, gw->g.y+(gw->g.height-icon->height)/2, icon->width, icon->height, 0, 0);
}
Usage
The following program will show how the same rendering routine can be applied to two buttons that are using two different icons:
#include "gfx.h"
GHandle ghButton1;
GHandle ghButton2;
gdispImage iconWrench;
gdispImage iconUgfx;
font_t font1;
font_t font2;
void myButtonRendering(GWidgetObject* gw, void* param)
{
const GColorSet* colors;
// Get the icon/image
gdispImage* icon = (gdispImage*)param;
if (!icon) {
return;
}
// Get the appropriate color pallete from the widget style
if (!gwinGetEnabled((GHandle)gw))
colors = &gw->pstyle->disabled;
else if ((gw->g.flags & GBUTTON_FLG_PRESSED))
colors = &gw->pstyle->pressed;
else
colors = &gw->pstyle->enabled;
// Draw the basic rectangle with border
gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, colors->fill);
gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, colors->edge);
// Draw the string. Use StringBox() for proper justification and word-wrapping support
gdispGDrawStringBox(gw->g.display, gw->g.x+gw->g.height, gw->g.y, gw->g.width-gw->g.height, gw->g.height, gw->text, gw->g.font, colors->text, justifyLeft);
// Draw the image
gdispGImageDraw(gw->g.display, icon, gw->g.x+(gw->g.height-icon->width)/2, gw->g.y+(gw->g.height-icon->height)/2, icon->width, icon->height, 0, 0);
}
static void createWidgets(void) {
GWidgetInit wi;
// Prepare the images
gdispImageOpenFile(&iconWrench, "settings.gif");
gdispImageOpenFile(&iconUgfx, "padlock.gif");
// Apply some default values for GWIN
gwinWidgetClearInit(&wi);
wi.g.show = TRUE;
// Create a regular button
wi.g.width = 180;
wi.g.height = 52;
wi.g.y = 10;
wi.g.x = 10;
wi.customParam = &iconWrench;
wi.customDraw = myButtonRendering;
wi.text = "Setting";
ghButton1 = gwinButtonCreate(0, &wi);
// Create a regular button
wi.g.width = 180;
wi.g.height = 52;
wi.g.y = 70;
wi.g.x = 10;
wi.customParam = &iconUgfx;
wi.customDraw = myButtonRendering;
wi.text = "Lock";
ghButton2 = gwinButtonCreate(0, &wi);
}
int main(void)
{
gfxInit();
gwinSetDefaultFont(gdispOpenFont("*"));
gwinSetDefaultStyle(&WhiteWidgetStyle, FALSE);
gdispClear(White);
createWidgets();
while(1) {
gfxSleepMilliseconds(250);
}
}