/* * 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 #include "gwin_class.h" // Our listener for events for widgets static GListener gl; #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD // Our current focus window static GHandle _widgetInFocus; #endif // Our default style - a white background theme const GWidgetStyle WhiteWidgetStyle = { HTML2COLOR(0xFFFFFF), // window background HTML2COLOR(0x2A8FCD), // focused // enabled color set { HTML2COLOR(0x000000), // text HTML2COLOR(0x404040), // edge HTML2COLOR(0xE0E0E0), // fill HTML2COLOR(0x00E000) // progress - active 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 HTML2COLOR(0x2A8FCD), // focused // enabled color set { HTML2COLOR(0xC0C0C0), // text HTML2COLOR(0xC0C0C0), // edge HTML2COLOR(0x606060), // fill HTML2COLOR(0x008000) // progress - active 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 pke ((GEventKeyboard *)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->buttons & GMETA_MOUSE_UP)) { 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->buttons & GMETA_MOUSE_DOWN)) { gh->flags |= GWIN_FLG_MOUSECAPTURE; #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD // We should try and capture the focus on this window. // If we can't then we don't change the focus gwinSetFocus(gh); #endif if (wvmt->MouseDown) wvmt->MouseDown(gw, pme->x - gh->x, pme->y - gh->y); } } break; #endif #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD case GEVENT_KEYBOARD: // If Tab key pressed then set focus to next widget if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) { if (!(pke->keystate & GKEYSTATE_KEYUP)) _gwinMoveFocus(); break; } // Otherwise, send keyboard events only to widget in focus if (_widgetInFocus) ((gwidgetVMT*)_widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)_widgetInFocus, pke); 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 pke #undef pde } #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD GHandle gwinGetFocus(void) { return _widgetInFocus; } bool_t gwinSetFocus(GHandle gh) { GHandle oldFocus; // Do we already have the focus? if (gh == _widgetInFocus) return TRUE; // The new window must be NULLL or a visible enabled widget with a keyboard handler if (!gh || ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE) && ((gwidgetVMT*)gh->vmt)->KeyboardEvent)) { // Move the current focus oldFocus = _widgetInFocus; _widgetInFocus = gh; if (oldFocus) _gwinUpdate(oldFocus); if (gh) _gwinUpdate(gh); return TRUE; } return FALSE; } void _gwinMoveFocus(void) { GHandle gh; bool_t looponce; // Find a new focus window (one may or may not exist). looponce = FALSE; for(gh = gwinGetNextWindow(_widgetInFocus); ; gh = gwinGetNextWindow(gh)) { if (!gh && !looponce) { looponce = TRUE; gh = gwinGetNextWindow(0); } if (gwinSetFocus(gh)) break; } } void _gwinFixFocus(GHandle gh) { GHandle oldFocus; if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE) && ((gwidgetVMT*)gh->vmt)->KeyboardEvent) { // We are a candidate to be able to claim the focus // Claim the focus if no-one else has if (!_widgetInFocus) _widgetInFocus = gh; return; } // We have lost any right to the focus // Did we have the focus if (gh != _widgetInFocus) return; // We did - we need to find a new focus window oldFocus = _widgetInFocus; for(gh = gwinGetNextWindow(oldFocus); gh && gh != oldFocus; gh = gwinGetNextWindow(gh)) { // Must be a visible enabled widget with a keyboard handler if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE) && ((gwidgetVMT*)gh->vmt)->KeyboardEvent) { // Grab the focus for the new window _widgetInFocus = gh; // This new window still needs to be marked for redraw (but don't actually do it yet). gh->flags |= GWIN_FLG_NEEDREDRAW; // RedrawPending |= DOREDRAW_VISIBLES; - FIX LATER return; } } // No-one has the right to the focus _widgetInFocus = 0; } void _gwidgetDrawFocusRect(GWidgetObject *gx, coord_t x, coord_t y, coord_t cx, coord_t cy) { uint16_t i; // Don't do anything if we don't have the focus if (&gx->g != _widgetInFocus) return; // Use the very simplest possible focus rectangle for now for (i = 0; i < GWIN_FOCUS_HIGHLIGHT_WIDTH; i++) { gdispGDrawBox(gx->g.display, gx->g.x+x+i, gx->g.y+y+i, cx-2*i, cy-2*i, gx->pstyle->focus); } } #endif #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); #if GINPUT_NEED_MOUSE geventAttachSource(&gl, ginputGetMouse(GMOUSE_ALL_INSTANCES), GLISTEN_MOUSEMETA|GLISTEN_MOUSEDOWNMOVES); #endif #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD geventAttachSource(&gl, ginputGetKeyboard(GKEYBOARD_ALL_INSTANCES), GLISTEN_KEYUP); #endif } 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_CONTAINERS // This window can't be system enabled if the parent is not enabled if (pgw->g.parent && !(pgw->g.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 // Make the window is invisible so it is not eligible for focus gh->flags &= ~GWIN_FLG_VISIBLE; _gwinFixFocus(gh); // 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); } #if GFX_USE_GFILE && GFILE_NEED_PRINTG && GFILE_NEED_STRINGS #include void gwinPrintg(GHandle gh, const char * fmt, ...) { char *str; va_list va; int size; 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 va_start (va, fmt); size = vsnprintg(0, 0, fmt, va) + 1; //determine the buffer size required if ((str = gfxAlloc(size))) { gh->flags |= GWIN_FLG_ALLOCTXT; vsnprintg(str, size, fmt, va); gw->text = (const char *)str; } else gw->text = ""; va_end (va); _gwinUpdate(gh); } #endif const char *gwinGetText(GHandle gh) { if (!(gh->flags & GWIN_FLG_WIDGET)) return 0; return gw->text; } bool_t gwinIsWidget(GHandle gh) { if (gh->flags & GWIN_FLG_WIDGET) { return TRUE; } return FALSE; } 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 DEPRECATED("This call can now be removed. Attaching the mouse to GWIN is now automatic.") gwinAttachMouse(uint16_t instance) { // This is now a NULL event because we automatically attach to all mice in the system. (void) instance; return TRUE; } #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); } bool_t gwinDetachToggle(GHandle gh, uint16_t role) { uint16_t oi; // Is this a widget if (!(gh->flags & GWIN_FLG_WIDGET)) return FALSE; // Is the role valid if (role >= ((gwidgetVMT *)gh->vmt)->toggleroles) return FALSE; oi = ((gwidgetVMT *)gh->vmt)->ToggleGet(gw, role); // Remove the instance if (oi != GWIDGET_NO_INSTANCE) { ((gwidgetVMT *)gh->vmt)->ToggleAssign(gw, role, GWIDGET_NO_INSTANCE); if (!FindToggleUser(oi)) geventDetachSource(&gl, ginputGetToggle(oi)); } return TRUE; } #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 #undef gw #undef wvmt #endif /* GFX_USE_GWIN && GWIN_NEED_WIDGET */ /** @} */