aboutsummaryrefslogtreecommitdiffstats
path: root/src/gwin/gwin_widget.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gwin/gwin_widget.c')
-rw-r--r--src/gwin/gwin_widget.c521
1 files changed, 521 insertions, 0 deletions
diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c
new file mode 100644
index 00000000..f85e6f20
--- /dev/null
+++ b/src/gwin/gwin_widget.c
@@ -0,0 +1,521 @@
+/*
+ * 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://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gwin/gwin_widget.c
+ * @brief GWIN sub-system widget code
+ */
+
+#include "gfx.h"
+
+#if GFX_USE_GWIN && GWIN_NEED_WIDGET
+
+#include <string.h>
+
+#include "gwin_class.h"
+
+/* Our listener for events for widgets */
+static GListener gl;
+
+/* Our default style - a white background theme */
+const GWidgetStyle WhiteWidgetStyle = {
+ HTML2COLOR(0xFFFFFF), // window background
+
+ // enabled color set
+ {
+ HTML2COLOR(0x000000), // text
+ HTML2COLOR(0x404040), // edge
+ HTML2COLOR(0xE0E0E0), // fill
+ HTML2COLOR(0xE0E0E0), // progress - inactive area
+ },
+
+ // disabled color set
+ {
+ HTML2COLOR(0xC0C0C0), // text
+ HTML2COLOR(0x808080), // edge
+ HTML2COLOR(0xE0E0E0), // fill
+ HTML2COLOR(0xC0E0C0), // progress - active area
+ },
+
+ // pressed color set
+ {
+ HTML2COLOR(0x404040), // text
+ HTML2COLOR(0x404040), // edge
+ HTML2COLOR(0x808080), // fill
+ HTML2COLOR(0x00E000), // progress - active area
+ },
+};
+
+/* Our black style */
+const GWidgetStyle BlackWidgetStyle = {
+ HTML2COLOR(0x000000), // window background
+
+ // enabled color set
+ {
+ HTML2COLOR(0xC0C0C0), // text
+ HTML2COLOR(0xC0C0C0), // edge
+ HTML2COLOR(0x606060), // fill
+ HTML2COLOR(0x404040), // progress - inactive area
+ },
+
+ // disabled color set
+ {
+ HTML2COLOR(0x808080), // text
+ HTML2COLOR(0x404040), // edge
+ HTML2COLOR(0x404040), // fill
+ HTML2COLOR(0x004000), // progress - active area
+ },
+
+ // pressed color set
+ {
+ HTML2COLOR(0xFFFFFF), // text
+ HTML2COLOR(0xC0C0C0), // edge
+ HTML2COLOR(0xE0E0E0), // fill
+ HTML2COLOR(0x008000), // progress - active area
+ },
+};
+
+static const GWidgetStyle * defaultStyle = &BlackWidgetStyle;
+
+/* We use these everywhere in this file */
+#define gw ((GWidgetObject *)gh)
+#define wvmt ((gwidgetVMT *)gh->vmt)
+
+/* Process an event */
+static void gwidgetEvent(void *param, GEvent *pe) {
+ #define pme ((GEventMouse *)pe)
+ #define pte ((GEventToggle *)pe)
+ #define pde ((GEventDial *)pe)
+
+ GHandle h;
+ GHandle gh;
+ #if GFX_USE_GINPUT && (GINPUT_NEED_TOGGLE || GINPUT_NEED_DIAL)
+ uint16_t role;
+ #endif
+ (void) param;
+
+ // Process various events
+ switch (pe->type) {
+
+ #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
+ case GEVENT_MOUSE:
+ case GEVENT_TOUCH:
+ // Cycle through all windows
+ for(gh = 0, h = gwinGetNextWindow(0); h; h = gwinGetNextWindow(h)) {
+
+ // The window must be on this display and visible to be relevant
+ if (h->display != pme->display || !(h->flags & GWIN_FLG_SYSVISIBLE))
+ continue;
+
+ // Is the mouse currently captured by this widget?
+ if ((h->flags & (GWIN_FLG_WIDGET|GWIN_FLG_MOUSECAPTURE)) == (GWIN_FLG_WIDGET|GWIN_FLG_MOUSECAPTURE)) {
+ gh = h;
+ if ((pme->last_buttons & ~pme->current_buttons & GINPUT_MOUSE_BTN_LEFT)) {
+ gh->flags &= ~GWIN_FLG_MOUSECAPTURE;
+ if (wvmt->MouseUp)
+ wvmt->MouseUp(gw, pme->x - gh->x, pme->y - gh->y);
+ } else if (wvmt->MouseMove)
+ wvmt->MouseMove(gw, pme->x - gh->x, pme->y - gh->y);
+
+ // There is only ever one captured mouse. Prevent normal mouse processing if there is a captured mouse
+ gh = 0;
+ break;
+ }
+
+ // Save the highest z-order window that the mouse is over
+ if (pme->x >= h->x && pme->x < h->x + h->width && pme->y >= h->y && pme->y < h->y + h->height)
+ gh = h;
+ }
+
+ // Process any mouse down over the highest order window if it is an enabled widget
+ if (gh && (gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) == (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) {
+ if ((~pme->last_buttons & pme->current_buttons & GINPUT_MOUSE_BTN_LEFT)) {
+ gh->flags |= GWIN_FLG_MOUSECAPTURE;
+ if (wvmt->MouseDown)
+ wvmt->MouseDown(gw, pme->x - gh->x, pme->y - gh->y);
+ }
+ }
+ break;
+ #endif
+
+ #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
+ case GEVENT_TOGGLE:
+ // Cycle through all windows
+ for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
+
+ // check if it a widget that is enabled and visible
+ if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE))
+ continue;
+
+ for(role = 0; role < wvmt->toggleroles; role++) {
+ if (wvmt->ToggleGet(gw, role) == pte->instance) {
+ if (pte->on) {
+ if (wvmt->ToggleOn)
+ wvmt->ToggleOn(gw, role);
+ } else {
+ if (wvmt->ToggleOff)
+ wvmt->ToggleOff(gw, role);
+ }
+ }
+ }
+ }
+ break;
+ #endif
+
+ #if GFX_USE_GINPUT && GINPUT_NEED_DIAL
+ case GEVENT_DIAL:
+ // Cycle through all windows
+ for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
+
+ // check if it a widget that is enabled and visible
+ if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE))
+ continue;
+
+ for(role = 0; role < wvmt->dialroles; role++) {
+ if (wvmt->DialGet(gw, role) == pte->instance) {
+ if (wvmt->DialMove)
+ wvmt->DialMove(gw, role, pde->value, pde->maxvalue);
+ }
+ }
+ }
+ break;
+ #endif
+
+ default:
+ break;
+ }
+
+ #undef pme
+ #undef pte
+ #undef pde
+}
+
+#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
+ static GHandle FindToggleUser(uint16_t instance) {
+ GHandle gh;
+ uint16_t role;
+
+ for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
+ if (!(gh->flags & GWIN_FLG_WIDGET)) // check if it a widget
+ continue;
+
+ for(role = 0; role < wvmt->toggleroles; role++) {
+ if (wvmt->ToggleGet(gw, role) == instance)
+ return gh;
+ }
+ }
+ return 0;
+ }
+#endif
+
+#if GFX_USE_GINPUT && GINPUT_NEED_DIAL
+ static GHandle FindDialUser(uint16_t instance) {
+ GHandle gh;
+ uint16_t role;
+
+ for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
+ if (!(gh->flags & GWIN_FLG_WIDGET)) // check if it a widget
+ continue;
+
+ for(role = 0; role < wvmt->dialroles; role++) {
+ if (wvmt->DialGet(gw, role) == instance)
+ return gh;
+ }
+ }
+ return 0;
+ }
+#endif
+
+void _gwidgetInit(void)
+{
+ geventListenerInit(&gl);
+ geventRegisterCallback(&gl, gwidgetEvent, 0);
+}
+
+void _gwidgetDeinit(void)
+{
+ /* ToDo */
+}
+
+GHandle _gwidgetCreate(GDisplay *g, GWidgetObject *pgw, const GWidgetInit *pInit, const gwidgetVMT *vmt) {
+ if (!(pgw = (GWidgetObject *)_gwindowCreate(g, &pgw->g, &pInit->g, &vmt->g, GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED)))
+ return 0;
+
+ #if GWIN_NEED_COLLECTIONS
+ // This window can't be system enabled if the parent is not enabled
+ if (pgw->parent && !(pgw->parent->flags & GWIN_FLG_SYSENABLED))
+ pgw->g.flags &= ~GWIN_FLG_SYSENABLED;
+ #endif
+ pgw->text = pInit->text ? pInit->text : "";
+ pgw->fnDraw = pInit->customDraw ? pInit->customDraw : vmt->DefaultDraw;
+ pgw->fnParam = pInit->customParam;
+ pgw->pstyle = pInit->customStyle ? pInit->customStyle : defaultStyle;
+ #if GWIN_WIDGET_TAGS
+ pgw->tag = pInit->tag;
+ #endif
+
+ return &pgw->g;
+}
+
+void _gwidgetDestroy(GHandle gh) {
+ #if GFX_USE_GINPUT && (GINPUT_NEED_TOGGLE || GINPUT_NEED_DIAL)
+ uint16_t role, instance;
+ #endif
+
+ // Deallocate the text (if necessary)
+ if ((gh->flags & GWIN_FLG_ALLOCTXT)) {
+ gh->flags &= ~GWIN_FLG_ALLOCTXT;
+ gfxFree((void *)gw->text);
+ }
+
+ #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
+ // Detach any toggles from this object
+ for(role = 0; role < wvmt->toggleroles; role++) {
+ instance = wvmt->ToggleGet(gw, role);
+ if (instance != GWIDGET_NO_INSTANCE) {
+ wvmt->ToggleAssign(gw, role, GWIDGET_NO_INSTANCE);
+ if (!FindToggleUser(instance))
+ geventDetachSource(&gl, ginputGetToggle(instance));
+ }
+ }
+ #endif
+
+ #if GFX_USE_GINPUT && GINPUT_NEED_DIAL
+ // Detach any dials from this object
+ for(role = 0; role < wvmt->dialroles; role++) {
+ instance = wvmt->DialGet(gw, role);
+ if (instance != GWIDGET_NO_INSTANCE) {
+ wvmt->DialAssign(gw, role, GWIDGET_NO_INSTANCE);
+ if (!FindDialUser(instance))
+ geventDetachSource(&gl, ginputGetDial(instance));
+ }
+ }
+ #endif
+
+ // Remove any listeners on this object.
+ geventDetachSourceListeners((GSourceHandle)gh);
+}
+
+void _gwidgetRedraw(GHandle gh) {
+ if (!(gh->flags & GWIN_FLG_SYSVISIBLE))
+ return;
+
+ gw->fnDraw(gw, gw->fnParam);
+}
+
+void _gwinSendEvent(GHandle gh, GEventType type) {
+ GSourceListener * psl;
+ GEventGWin * pge;
+
+ // Trigger a GWIN Event
+ psl = 0;
+ while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) {
+ if (!(pge = (GEventGWin *)geventGetEventBuffer(psl)))
+ continue;
+ pge->type = type;
+ pge->gwin = gh;
+ #if GWIN_WIDGET_TAGS
+ pge->tag = (gh->flags & GWIN_FLG_WIDGET) ? ((GWidgetObject *)gh)->tag : 0;
+ #endif
+ geventSendEvent(psl);
+ }
+}
+
+void gwinWidgetClearInit(GWidgetInit *pwi) {
+ char *p;
+ unsigned len;
+
+ for(p = (char *)pwi, len = sizeof(GWidgetInit); len; len--)
+ *p++ = 0;
+}
+
+void gwinSetDefaultStyle(const GWidgetStyle *pstyle, bool_t updateAll) {
+ if (!pstyle)
+ pstyle = &BlackWidgetStyle;
+
+ if (updateAll) {
+ GHandle gh;
+
+ for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) {
+ if ((gh->flags & GWIN_FLG_WIDGET) && ((GWidgetObject *)gh)->pstyle == defaultStyle)
+ gwinSetStyle(gh, pstyle);
+ else
+ gwinRedraw(gh);
+ }
+ }
+ gwinSetDefaultBgColor(pstyle->background);
+ defaultStyle = pstyle;
+}
+
+/**
+ * @brief Get the current default style.
+ *
+ * @api
+ */
+const GWidgetStyle *gwinGetDefaultStyle(void) {
+ return defaultStyle;
+}
+
+
+void gwinSetText(GHandle gh, const char *text, bool_t useAlloc) {
+ if (!(gh->flags & GWIN_FLG_WIDGET))
+ return;
+
+ // Dispose of the old string
+ if ((gh->flags & GWIN_FLG_ALLOCTXT)) {
+ gh->flags &= ~GWIN_FLG_ALLOCTXT;
+ if (gw->text) {
+ gfxFree((void *)gw->text);
+ gw->text = "";
+ }
+ }
+
+ // Alloc the new text if required
+ if (!text || !*text)
+ gw->text = "";
+ else if (useAlloc) {
+ char *str;
+
+ if ((str = gfxAlloc(strlen(text)+1))) {
+ gh->flags |= GWIN_FLG_ALLOCTXT;
+ strcpy(str, text);
+ }
+ gw->text = (const char *)str;
+ } else
+ gw->text = text;
+ _gwinUpdate(gh);
+}
+
+const char *gwinGetText(GHandle gh) {
+ if (!(gh->flags & GWIN_FLG_WIDGET))
+ return 0;
+
+ return gw->text;
+}
+
+void gwinSetStyle(GHandle gh, const GWidgetStyle *pstyle) {
+ if (!(gh->flags & GWIN_FLG_WIDGET))
+ return;
+ gw->pstyle = pstyle ? pstyle : defaultStyle;
+ gh->bgcolor = pstyle->background;
+ gh->color = pstyle->enabled.text;
+ _gwinUpdate(gh);
+}
+
+const GWidgetStyle *gwinGetStyle(GHandle gh) {
+ if (!(gh->flags & GWIN_FLG_WIDGET))
+ return 0;
+
+ return gw->pstyle;
+}
+
+void gwinSetCustomDraw(GHandle gh, CustomWidgetDrawFunction fn, void *param) {
+ if (!(gh->flags & GWIN_FLG_WIDGET))
+ return;
+
+ gw->fnDraw = fn ? fn : wvmt->DefaultDraw;
+ gw->fnParam = param;
+ _gwinUpdate(gh);
+}
+
+bool_t gwinAttachListener(GListener *pl) {
+ return geventAttachSource(pl, GWIDGET_SOURCE, 0);
+}
+
+#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
+ bool_t gwinAttachMouse(uint16_t instance) {
+ GSourceHandle gsh;
+
+ if (!(gsh = ginputGetMouse(instance)))
+ return FALSE;
+
+ return geventAttachSource(&gl, gsh, GLISTEN_MOUSEMETA|GLISTEN_MOUSEDOWNMOVES);
+ }
+#endif
+
+#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
+ bool_t gwinAttachToggle(GHandle gh, uint16_t role, uint16_t instance) {
+ GSourceHandle gsh;
+ uint16_t oi;
+
+ // Is this a widget
+ if (!(gh->flags & GWIN_FLG_WIDGET))
+ return FALSE;
+
+ // Is the role valid
+ if (role >= wvmt->toggleroles)
+ return FALSE;
+
+ // Is this a valid device
+ if (!(gsh = ginputGetToggle(instance)))
+ return FALSE;
+
+ // Is this already done?
+ oi = wvmt->ToggleGet(gw, role);
+ if (instance == oi)
+ return TRUE;
+
+ // Remove the old instance
+ if (oi != GWIDGET_NO_INSTANCE) {
+ wvmt->ToggleAssign(gw, role, GWIDGET_NO_INSTANCE);
+ if (!FindToggleUser(oi))
+ geventDetachSource(&gl, ginputGetToggle(oi));
+ }
+
+ // Assign the new
+ wvmt->ToggleAssign(gw, role, instance);
+ return geventAttachSource(&gl, gsh, GLISTEN_TOGGLE_ON|GLISTEN_TOGGLE_OFF);
+ }
+#endif
+
+#if GFX_USE_GINPUT && GINPUT_NEED_DIAL
+ bool_t gwinAttachDial(GHandle gh, uint16_t role, uint16_t instance) {
+ GSourceHandle gsh;
+ uint16_t oi;
+
+ if (!(gh->flags & GWIN_FLG_WIDGET))
+ return FALSE;
+
+ // Is the role valid
+ if (role >= wvmt->dialroles)
+ return FALSE;
+
+ // Is this a valid device
+ if (!(gsh = ginputGetDial(instance)))
+ return FALSE;
+
+ // Is this already done?
+ oi = wvmt->DialGet(gw, role);
+ if (instance == oi)
+ return TRUE;
+
+ // Remove the old instance
+ if (oi != GWIDGET_NO_INSTANCE) {
+ wvmt->DialAssign(gw, role, GWIDGET_NO_INSTANCE);
+ if (!FindDialUser(oi))
+ geventDetachSource(&gl, ginputGetDial(oi));
+ }
+
+ // Assign the new
+ wvmt->DialAssign(gw, role, instance);
+ return geventAttachSource(&gl, gsh, 0);
+ }
+#endif
+
+#if GWIN_WIDGET_TAGS
+ void gwinSetTag(GHandle gh, WidgetTag tag) {
+ if ((gh->flags & GWIN_FLG_WIDGET))
+ gw->tag = tag;
+ }
+
+ WidgetTag gwinGetTag(GHandle gh) {
+ return ((gh->flags & GWIN_FLG_WIDGET)) ? gw->tag : 0;
+ }
+#endif
+
+#endif /* GFX_USE_GWIN && GWIN_NEED_WIDGET */
+/** @} */