diff --git a/include/gfx_rules.h b/include/gfx_rules.h index dfabe728..bfe017d3 100644 --- a/include/gfx_rules.h +++ b/include/gfx_rules.h @@ -46,7 +46,12 @@ #warning "GWIN: Drawing can occur outside the defined windows as GDISP_NEED_CLIP is FALSE" #endif #endif - #if GWIN_NEED_BUTTON || GWIN_NEED_SLIDER || GWIN_NEED_CHECKBOX + #if GWIN_NEED_IMAGE + #if !GDISP_NEED_IMAGE + #error "GWIN: GDISP_NEED_IMAGE is required when GWIN_NEED_IMAGE is TRUE." + #endif + #endif + #if GWIN_NEED_BUTTON || GWIN_NEED_SLIDER || GWIN_NEED_CHECKBOX || GWIN_NEED_LABEL #if !GWIN_NEED_WIDGET #if GFX_DISPLAY_RULE_WARNINGS #warning "GWIN: GWIN_NEED_WIDGET is required when a Widget is used. It has been turned on for you." diff --git a/include/gwin/gwin.h b/include/gwin/gwin.h index 21d465b2..4fb28bef 100644 --- a/include/gwin/gwin.h +++ b/include/gwin/gwin.h @@ -111,6 +111,13 @@ extern "C" { */ void gwinSetDefaultColor(color_t clr); + /** + * @brief Get the default foreground color for all new GWIN windows + * + * @api + */ + color_t gwinGetDefaultColor(void); + /** * @brief Set the default background color for all new GWIN windows * @@ -120,6 +127,13 @@ extern "C" { */ void gwinSetDefaultBgColor(color_t bgclr); + /** + * @brief Get the default background color for all new GWIN windows + * + * @api + */ + color_t gwinGetDefaultBgColor(void); + #if GDISP_NEED_TEXT || defined(__DOXYGEN__) /** * @brief Set the default font for all new GWIN windows diff --git a/include/gwin/image.h b/include/gwin/image.h index 95a26687..d32e9a0c 100644 --- a/include/gwin/image.h +++ b/include/gwin/image.h @@ -32,9 +32,7 @@ // An image window typedef struct GImageWidget_t { GWindowObject g; - - gdispImage *image; - color_t bgColor; + gdispImage image; } GImageWidget; #ifdef __cplusplus @@ -50,11 +48,10 @@ extern "C" { * @param[in] pInit The initialization parameters to use. * * @note The default background color gets set to the current default one. - * @note An image widget does not save the current drawing state. It is not automatically redrawn if the window - * is moved or its visibility state is changed. + * @note An image window knows how to redraw. * * @api - */ + */ GHandle gwinImageCreate(GImageWidget *widget, GWindowInit *pInit); /** @@ -98,7 +95,7 @@ bool_t gwinImageOpenMemory(GHandle gh, const void* memory); #endif /** - * @brief Cache an image. + * @brief Cache the image. * @details Decodes and caches the current frame into RAM. * * param[in] gh The widget (must be an image widget) @@ -109,20 +106,6 @@ bool_t gwinImageOpenMemory(GHandle gh, const void* memory); */ gdispImageError gwinImageCache(GHandle gh); -/** - * @brief Set the background color of an image widget. - * @details Transparent images need a background color. If no background color has been set, the current default - * on is used. - * - * @param[in] gh The widget (must be an image widget) - * @param[in] bgColor The background color to be set - * - * @api - */ -void gwinImageSetBgColor(GHandle gh, color_t bgColor); - -void gwinImageDraw(GHandle gh); - #ifdef __cplusplus } #endif diff --git a/src/gwin/gimage.c b/src/gwin/gimage.c new file mode 100644 index 00000000..464bc595 --- /dev/null +++ b/src/gwin/gimage.c @@ -0,0 +1,173 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://chibios-gfx.com/license.html + */ + +/** + * @file src/gwin/image.c + * @brief GWIN sub-system image code. + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_IMAGE + +#include "gwin/class_gwin.h" + +#define widget(gh) ((GImageWidget*)gh) + +static void _destroy(GWindowObject *gh) { + if (gdispImageIsOpen(&widget(gh)->image)) + gdispImageClose(&widget(gh)->image); +} + +static void _redraw(GHandle gh) { + coord_t x, y, w, h, dx, dy; + + // The default display area + x = gh->x; + y = gh->y; + w = gh->width; + h = gh->height; + + // If the image isn't open just clear the area + if (!gdispImageIsOpen(&widget(gh)->image)) { + gdispFillArea(x, y, w, h, gh->bgcolor); + return; + } + + // Center horizontally if the area is larger than the image + if (widget(gh)->image.width < w) { + w = widget(gh)->image.width; + dx = (gh->width-w)/2; + x += dx; + if (dx) + gdispFillArea(gh->x, y, dx, h, gh->bgcolor); + gdispFillArea(x+w, y, gh->width-dx-w, h, gh->bgcolor); + dx = 0; + } + + // Center image horizontally if the area is smaller than the image + else if (widget(gh)->image.width > w) { + dx = (widget(gh)->image.width - w)/2; + } + + // Center vertically if the area is larger than the image + if (widget(gh)->image.height < h) { + h = widget(gh)->image.height; + dy = (gh->height-h)/2; + y += dy; + if (dy) + gdispFillArea(x, gh->y, w, dy, gh->bgcolor); + gdispFillArea(x, y+h, w, gh->height-dy-h, gh->bgcolor); + dy = 0; + } + + // Center image vertically if the area is smaller than the image + else if (widget(gh)->image.height > h) { + dy = (widget(gh)->image.height - h)/2; + } + + // Reset the background color in case it has changed + gdispImageSetBgColor(&widget(gh)->image, gh->bgcolor); + + // Display the image + gdispImageDraw(&widget(gh)->image, x, y, w, h, dx, dy); +} + + +static const gwinVMT imageVMT = { + "Image", // The class name + sizeof(GImageWidget), // The object size + _destroy, // The destroy routine + _redraw, // The redraw routine + 0, // The after-clear routine +}; + +GHandle gwinImageCreate(GImageWidget *gobj, GWindowInit *pInit) { + if (!(gobj = (GImageWidget *)_gwindowCreate(&gobj->g, pInit, &imageVMT, 0))) + return 0; + + // Ensure the gdispImageIsOpen() gives valid results + gobj->image.type = 0; + + gwinSetVisible((GHandle)gobj, pInit->show); + + return (GHandle)gobj; +} + +bool_t gwinImageOpenMemory(GHandle gh, const void* memory) { + if (gdispImageIsOpen(&widget(gh)->image)) + gdispImageClose(&widget(gh)->image); + + if (!gdispImageSetMemoryReader(&widget(gh)->image, memory)) + return FALSE; + + if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK) + return FALSE; + + if ((gh->flags & GWIN_FLG_VISIBLE)) { + // Setting the clip here shouldn't be necessary if the redraw doesn't overdraw + // but we put it in for safety anyway + #if GDISP_NEED_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + _redraw(gh); + } + return TRUE; +} + +#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_POSIX || defined(__DOXYGEN__) +bool_t gwinImageOpenFile(GHandle gh, const char* filename) { + if (gdispImageIsOpen(&widget(gh)->image)) + gdispImageClose(&widget(gh)->image); + + if (!gdispImageSetFileReader(&widget(gh)->image, filename)) + return FALSE; + + if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK) + return FALSE; + + if ((gh->flags & GWIN_FLG_VISIBLE)) { + // Setting the clip here shouldn't be necessary if the redraw doesn't overdraw + // but we put it in for safety anyway + #if GDISP_NEED_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + _redraw(gh); + } + return TRUE; +} +#endif + +#if GFX_USE_OS_CHIBIOS || defined(__DOXYGEN__) +bool_t gwinImageOpenStream(GHandle gh, void *streamPtr) { + if (gdispImageIsOpen(&widget(gh)->image)) + gdispImageClose(&widget(gh)->image); + + if (!gdispImageSetBaseFileStreamReader(&widget(gh)->image, streamPtr)) + return FALSE; + + if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK) + return FALSE; + + if ((gh->flags & GWIN_FLG_VISIBLE)) { + // Setting the clip here shouldn't be necessary if the redraw doesn't overdraw + // but we put it in for safety anyway + #if GDISP_NEED_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + _redraw(gh); + } + return TRUE; +} +#endif + +gdispImageError gwinImageCache(GHandle gh) { + return gdispImageCache(&widget(gh)->image); +} + +#endif // GFX_USE_GWIN && GWIN_NEED_IMAGE +/** @} */ diff --git a/src/gwin/gwin.c b/src/gwin/gwin.c index f080ac64..de1673cb 100644 --- a/src/gwin/gwin.c +++ b/src/gwin/gwin.c @@ -63,6 +63,16 @@ static color_t defaultBgColor = Black; if (gh->height < MIN_WIN_HEIGHT) { gh->height = MIN_WIN_HEIGHT; } if (gh->x+gh->width > gdispGetWidth()) gh->width = gdispGetWidth() - gh->x; if (gh->y+gh->height > gdispGetHeight()) gh->height = gdispGetHeight() - gh->y; + + // Redraw the window + if ((gh->flags & GWIN_FLG_VISIBLE)) { + if (gh->vmt->Redraw) { + #if GDISP_NEED_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gh->vmt->Redraw(gh); + } + } } #endif @@ -135,10 +145,18 @@ void gwinSetDefaultColor(color_t clr) { defaultFgColor = clr; } +color_t gwinGetDefaultColor(void) { + return defaultFgColor; +} + void gwinSetDefaultBgColor(color_t bgclr) { defaultBgColor = bgclr; } +color_t gwinGetDefaultBgColor(void) { + return defaultBgColor; +} + #if GDISP_NEED_TEXT void gwinSetDefaultFont(font_t font) { defaultFont = font; diff --git a/src/gwin/gwin.mk b/src/gwin/gwin.mk index 9a81728e..b9e6a9ee 100644 --- a/src/gwin/gwin.mk +++ b/src/gwin/gwin.mk @@ -6,6 +6,6 @@ GFXSRC += $(GFXLIB)/src/gwin/gwin.c \ $(GFXLIB)/src/gwin/button.c \ $(GFXLIB)/src/gwin/slider.c \ $(GFXLIB)/src/gwin/checkbox.c \ - $(GFXLIB)/src/gwin/image.c \ + $(GFXLIB)/src/gwin/gimage.c \ $(GFXLIB)/src/gwin/label.c \ diff --git a/src/gwin/gwm.c b/src/gwin/gwm.c index c3405d83..75b08be6 100644 --- a/src/gwin/gwm.c +++ b/src/gwin/gwm.c @@ -25,7 +25,7 @@ static void WM_Init(void); static void WM_DeInit(void); -static bool_t WM_Add(GHandle gh, GWindowInit *pInit); +static bool_t WM_Add(GHandle gh, const GWindowInit *pInit); static void WM_Delete(GHandle gh); static void WM_Visible(GHandle gh); static void WM_Redim(GHandle gh, coord_t x, coord_t y, coord_t w, coord_t h); @@ -65,23 +65,25 @@ static void WM_DeInit(void) { // A full window manager would remove any borders etc } -static bool_t WM_Add(GHandle gh, GWindowInit *pInit) { +static bool_t WM_Add(GHandle gh, const GWindowInit *pInit) { + // Note the window will not be marked as visible yet + // Put it on the queue gfxQueueASyncPut(&_GWINList, &gh->wmq); // Make sure the size is valid WM_Redim(gh, pInit->x, pInit->y, pInit->width, pInit->height); - - // Display it if it is visible - WM_Visible(gh); return TRUE; } static void WM_Delete(GHandle gh) { - // A real window manager would make the window invisible - // (and then clear the area underneath) + // Make the window invisible and clear the area underneath + if ((gh->flags & GWIN_FLG_VISIBLE)) { + gh->flags &= ~GWIN_FLG_VISIBLE; + gdispFillArea(gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor()); + } - // Just remove it from the queue + // Remove it from the queue gfxQueueASyncRemove(&_GWINList, &gh->wmq); } @@ -97,10 +99,8 @@ static void WM_Visible(GHandle gh) { // A real window manager would also redraw the borders } - // else - // A real window manager would make the window invisible - // (and then clear the area underneath) - + else + gdispFillArea(gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor()); } static void WM_Redim(GHandle gh, coord_t x, coord_t y, coord_t w, coord_t h) { @@ -114,8 +114,28 @@ static void WM_Redim(GHandle gh, coord_t x, coord_t y, coord_t w, coord_t h) { if (h < MIN_WIN_HEIGHT) { h = MIN_WIN_HEIGHT; } if (x+w > gdispGetWidth()) w = gdispGetWidth() - x; if (y+h > gdispGetHeight()) h = gdispGetHeight() - y; + + // If there has been no resize just exit + if (gh->x == x && gh->y == y && gh->width == w && gh->height == h) + return; + + // Clear the old area + if ((gh->flags & GWIN_FLG_VISIBLE)) + gdispFillArea(gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor()); + + // Set the new size gh->x = x; gh->y = y; gh->width = w; gh->height = h; + + // Redraw the window (if possible) + if ((gh->flags & GWIN_FLG_VISIBLE)) { + if (gh->vmt->Redraw) { + #if GDISP_NEED_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + gh->vmt->Redraw(gh); + } + } } static void WM_MinMax(GHandle gh, GWindowMinMax minmax) { diff --git a/src/gwin/image.c b/src/gwin/image.c deleted file mode 100644 index bba92238..00000000 --- a/src/gwin/image.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://chibios-gfx.com/license.html - */ - -/** - * @file src/gwin/image.c - * @brief GWIN sub-system image code. - */ - -#include "gfx.h" - -#if GFX_USE_GWIN && GWIN_NEED_IMAGE - -#include "gwin/class_gwin.h" - -#define widget(gh) ((GImageWidget*)gh) - -static void _destroy(GWindowObject *gh) { - if (gdispImageIsOpen(&widget(gh)->image)) - gdispImageClose(&widget(gh)->image); - - return; -} - -static void _afterClear(GWindowObject *gh) { - (void)gh; - - return; -} - -static const gwinVMT imageVMT = { - "Image", // The class name - sizeof(GImageWidget), // The object size - _destroy, // The destroy routine - 0, - _afterClear, // The after-clear routine -}; - -GHandle gwinImageCreate(GImageWidget *widget, GWindowInit *pInit) { - if (!(widget = (GImageWidget *)_gwindowCreate(&widget->g, pInit, &imageVMT, 0))) - return 0; - - widget->image = gfxAlloc(sizeof(gdispImage)); - if (widget->image == NULL) - return 0; - - widget->g.x = pInit->x; - widget->g.y = pInit->y; - widget->g.width = pInit->width; - widget->g.height = pInit->height; - widget->bgColor = Black; - gwinSetVisible((GHandle)widget, pInit->show); - - return (GHandle)widget; -} - -bool_t gwinImageOpenMemory(GHandle gh, const void* memory) { - bool_t err; - - err = gdispImageSetMemoryReader(widget(gh)->image, memory); - gdispImageOpen(widget(gh)->image); - - return err; -} - -#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_POSIX || defined(__DOXYGEN__) -bool_t gwinImageOpenFile(GHandle gh, const char* filename) { - return gdispImageSetFileReader(widget(gh)->image, filename); -} -#endif - -#if GFX_USE_OS_CHIBIOS || defined(__DOXYGEN__) -bool_t gwinImageOpenStream(GHandle gh, void *streamPtr) { - return gdispImageSetBaseFileStreamReader(widget(gh)->image, streamPtr); -} -#endif - -gdispImageError gwinImageCache(GHandle gh) { - return gdispImageCache(widget(gh)->image); -} - -void gwinImageSetBgColor(GHandle gh, color_t bgColor) { - widget(gh)->bgColor = bgColor; -} - -void gwinImageDraw(GHandle gh) { - gdispImageDraw(widget(gh)->image, widget(gh)->g.x, widget(gh)->g.y, widget(gh)->image->width, widget(gh)->image->height, 0, 0); -} - -#endif // GFX_USE_GWIN && GWIN_NEED_IMAGE -/** @} */ - diff --git a/src/gwin/label.c b/src/gwin/label.c index 7f8ab814..e757892d 100644 --- a/src/gwin/label.c +++ b/src/gwin/label.c @@ -21,26 +21,36 @@ #include "gwin/class_gwin.h" -#define widget(gh) ((GLabelWidget*)gh) #define GLABEL_FLG_WAUTO (GWIN_FIRST_CONTROL_FLAG<<0) #define GLABEL_FLG_HAUTO (GWIN_FIRST_CONTROL_FLAG<<1) -static void gwinLabelDefaultDraw(GHandle gh) { - // if( check if auto flag is set ) - // if( call current size != font size ) - // gwinResize(); +// Simple: single line with no wrapping +static coord_t getwidth(const char *txt, font_t font, coord_t maxwidth) { + (void) maxwidth; + return gdispGetStringWidth(txt, font)+2; // Allow one pixel of padding on each side +} - gdispFillString( widget(gh)->w.g.x, - widget(gh)->w.g.y, - widget(gh)->w.txt, - widget(gh)->w.g.font, - widget(gh)->w.g.color, - widget(gh)->w.g.bgcolor - ); +// Simple: single line with no wrapping +static coord_t getheight(const char *txt, font_t font, coord_t maxwidth) { + (void) txt; + (void) maxwidth; - gdispFillArea( widget(gh)->w.g.x, widget(gh)->w.g.y, widget(gh)->w.g.width, widget(gh)->w.g.height, Green); + return gdispGetFontMetric(font, fontHeight); +} - printf("Text: %s\r\n", widget(gh)->w.txt); +static void gwinLabelDefaultDraw(GWidgetObject *gw, void *param) { + (void) param; + coord_t w, h; + + w = (gw->g.flags & GLABEL_FLG_WAUTO) ? getwidth(gw->txt, gw->g.font, gdispGetWidth() - gw->g.x) : gw->g.width; + h = (gw->g.flags & GLABEL_FLG_HAUTO) ? getheight(gw->txt, gw->g.font, gdispGetWidth() - gw->g.x) : gw->g.height; + + if (gw->g.width != w || gw->g.height != h) { + gwinResize(&gw->g, w, h); + return; + } + + gdispFillStringBox(gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->txt, gw->g.font, gw->g.color, gw->g.bgcolor, justifyLeft); } static const gwidgetVMT labelVMT = { @@ -78,21 +88,21 @@ GHandle gwinLabelCreate(GLabelWidget *widget, GWidgetInit *pInit) { // auto assign width if (pInit->g.width <= 0) { flags |= GLABEL_FLG_WAUTO; - pInit->g.width = gdispGetStringWidth(pInit->text, gwinGetDefaultFont()); + pInit->g.width = getwidth(pInit->text, gwinGetDefaultFont(), gdispGetWidth() - pInit->g.x); } // auto assign height if (pInit->g.height <= 0) { flags |= GLABEL_FLG_HAUTO; - pInit->g.height = gdispGetFontMetric(gwinGetDefaultFont(), fontHeight); + pInit->g.height = getheight(pInit->text, gwinGetDefaultFont(), gdispGetWidth() - pInit->g.x); } if (!(widget = (GLabelWidget *)_gwidgetCreate(&widget->w, pInit, &labelVMT))) return 0; - gwinLabelDefaultDraw((GHandle)widget); widget->w.g.flags |= flags; + gwinSetVisible(&widget->w.g, pInit->g.show); return (GHandle)widget; }