crteensy Posted April 13, 2015 Report Posted April 13, 2015 Is it possible to drag a widget around? Currently I just need a label, though I'm not yet sure if that will change in the future. I'd use this in a force directed graph demo.The buttonVMT table in gwin_button.c seems to indicate that dragging is not possible because there's no function that handles mouse move events (I have no idea if I'm looking in the right place!)
inmarket Posted April 13, 2015 Report Posted April 13, 2015 You are looking in the right place. Widgets are in the place they are put. The application can move them with gwinMove but that is something not well tested as the moving of widgets seldom happens in embedded platforms (or even desktop applications except in dynamic sizing forms). It should be possible however to copy the label code into your project and modify it to get the functionality you want.You are indeed looking at the right spot in the code to be doing something like that.
crteensy Posted April 13, 2015 Author Report Posted April 13, 2015 gwinMove works, I've tested that with a label. So I'll see if I can get it to work with custom code and report back.
crteensy Posted April 13, 2015 Author Report Posted April 13, 2015 Incredibly sketchy and hacked together from button and label code, but it works, sort of.The code below does more than just show a draggable label because I added the label code to an existing draft. It's kind of a main menu with two buttons, one of which opens a sub menu with a "back" button. These work fine.The magic is inside the MouseDown, MouseUp and MouseMove functions. Once clicked a hotspot is stored and while the mouse is down the label is moved relative to this hotspot. There is one problem, though: If the label's parent container doesn't cover the whole screen (see cwidth and cheight in createWidgets()), the calculated coordinates for gwinMove are wrong. How can I move the label relative to its parent?#include #include "gfx.h"#include "src/gwin/gwin_class.h"// macros to assist in data type conversions#define gh2obj ((GLabelObject *)gh)#define gw2obj ((GLabelObject *)gw)// flags for the GLabelObject#define GLABEL_FLG_WAUTO (GWIN_FIRST_CONTROL_FLAG << 0)#define GLABEL_FLG_HAUTO (GWIN_FIRST_CONTROL_FLAG << 1)#define GLABEL_FLG_BORDER (GWIN_FIRST_CONTROL_FLAG << 2)static GHandle ghMainContainer;static GHandle ghSubContainer;static GHandle ghButton1;static GHandle ghButton1Back;static GHandle ghButton2;static GHandle ghDragLabel;static GWidgetStyle myStyle = { Black, // background {White, Orange, Orange, Black}, // enabled (text edge fill progress) {White, Gray, Gray, Black}, // disabled {White, Gray, Orange, Black} // pressed};typedef struct GDragLabelObject { GWidgetObject w; coord_t hotspot_x; coord_t hotspot_y; bool_t beingDragged;} GDragLabelObject;#if GINPUT_NEED_MOUSE // A mouse down has occurred over the button static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) { (void) x; (void) y; GDragLabelObject* p = (GDragLabelObject*)gw; p->beingDragged = TRUE; p->hotspot_x = x; p->hotspot_y = y; } // A mouse up has occurred (it may or may not be over the button) static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) { (void) x; (void) y; GDragLabelObject* p = (GDragLabelObject*)gw; p->beingDragged = FALSE; } // A mouse move has occurred static void MouseMove(GWidgetObject *gw, coord_t x, coord_t y) { if (((GDragLabelObject*)gw)->beingDragged == TRUE) { GDragLabelObject* p = (GDragLabelObject*)gw; gwinMove((GWindowObject *)gw, p->w.g.x+x-p->hotspot_x, p->w.g.y+y-p->hotspot_y); } }#endifstatic void gwinDragLabelDefaultDraw(GWidgetObject *gw, void *param);static const gwidgetVMT dragLabelVMT = { { "DragLabel", // The classname sizeof(GDragLabelObject), // The object size _gwidgetDestroy, // The destroy routine _gwidgetRedraw, // The redraw routine 0, // The after-clear routine }, gwinDragLabelDefaultDraw, // The default drawing routine #if GINPUT_NEED_MOUSE { MouseDown, // Process mouse down events MouseUp, // Process mouse up events MouseMove, // Process mouse move events (NOT USED) }, #endif #if GINPUT_NEED_TOGGLE { 0, 0, 0, 0, 0, // No toggles }, #endif #if GINPUT_NEED_DIAL { 0, 0, 0, 0, // No dials }, #endif};// simple: single line with no wrappingstatic coord_t getwidth(const char *text, font_t font, coord_t maxwidth) { (void) maxwidth; return gdispGetStringWidth(text, font)+2; // Allow one pixel of padding on each side}// simple: single line with no wrappingstatic coord_t getheight(const char *text, font_t font, coord_t maxwidth) { (void) text; (void) maxwidth; return gdispGetFontMetric(font, fontHeight);}static void gwinDragLabelDefaultDraw(GWidgetObject *gw, void *param) { coord_t w, h; color_t c; (void) param; // is it a valid handle? if (gw->g.vmt != (gwinVMT *)&dragLabelVMT) return; w = (gw->g.flags & GLABEL_FLG_WAUTO) ? getwidth(gw->text, gw->g.font, gdispGGetWidth(gw->g.display) - gw->g.x) : gw->g.width; h = (gw->g.flags & GLABEL_FLG_HAUTO) ? getheight(gw->text, gw->g.font, gdispGGetWidth(gw->g.display) - gw->g.x) : gw->g.height; c = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text; if (gw->g.width != w || gw->g.height != h) { gwinResize(&gw->g, w, h); return; } #if GWIN_LABEL_ATTRIBUTE if (gw2obj->attr) { gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw2obj->tab, h, gw2obj->attr, gw->g.font, c, gw->pstyle->background, justifyLeft); gdispGFillStringBox(gw->g.display, gw->g.x + gw2obj->tab, gw->g.y, w-gw2obj->tab, h, gw->text, gw->g.font, c, gw->pstyle->background, justifyLeft); } else gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, w, h, gw->text, gw->g.font, c, gw->pstyle->background, justifyLeft); #else gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, w, h, gw->text, gw->g.font, c, gw->pstyle->background, justifyLeft); #endif // render the border (if any) if (gw->g.flags & GLABEL_FLG_BORDER) gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, w, h, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge);}GHandle gwinGDragLabelCreate(GDisplay *g, GDragLabelObject *gw, GWidgetInit *pInit) { uint16_t flags = 0; // auto assign width if (pInit->g.width <= 0) { flags |= GLABEL_FLG_WAUTO; pInit->g.width = getwidth(pInit->text, gwinGetDefaultFont(), gdispGGetWidth(g) - pInit->g.x); } // auto assign height if (pInit->g.height <= 0) { flags |= GLABEL_FLG_HAUTO; pInit->g.height = getheight(pInit->text, gwinGetDefaultFont(), gdispGGetWidth(g) - pInit->g.x); } if (!(gw = (GDragLabelObject *)_gwidgetCreate(g, &gw->w, pInit, &dragLabelVMT))) return 0; gwinSetVisible((GHandle)gw, pInit->g.show); return (GHandle)gw;}#define gwinDragLabelCreate(gb, pInit) gwinGDragLabelCreate(GDISP, gb, pInit)static void createWidgets(void) { uint8_t btnSize = 48; uint8_t btnDist = btnSize/2; coord_t height = gdispGetHeight(); coord_t width = gdispGetWidth(); coord_t cheight = height/2; coord_t cwidth = width/2; GWidgetInit wi; // Apply some default values for GWIN gwinWidgetClearInit(&wi); // Apply the container parameters wi.g.show = FALSE; wi.g.width = cwidth; wi.g.height = cheight; wi.g.x = (width - cwidth)/2; wi.g.y = (height - cheight)/2; wi.text = "Main Container"; ghMainContainer = gwinContainerCreate(0, &wi, 0);//GWIN_CONTAINER_BORDER); wi.g.show = TRUE; wi.text = "Bake Container"; wi.g.show = FALSE; ghSubContainer = gwinContainerCreate(0, &wi, 0);//GWIN_CONTAINER_BORDER); // Apply the button parameters wi.g.width = btnSize; wi.g.height = btnSize; wi.g.x = cwidth/2 - btnSize - btnDist/2; wi.g.y = cheight/2-btnSize/2; wi.text = "Button 1"; wi.g.parent = ghMainContainer; wi.g.show = TRUE; ghButton1 = gwinButtonCreate(0, &wi); // Apply the button parameters wi.g.x = cwidth/2 + btnDist/2; wi.text = "Button 2"; wi.g.parent = ghMainContainer; ghButton2 = gwinButtonCreate(0, &wi); // Apply the button parameters wi.g.x = 3*cwidth/4; wi.g.y = 3*cheight/4; wi.text = "drag me"; wi.g.parent = ghMainContainer; ghDragLabel = gwinDragLabelCreate(0, &wi); wi.g.x = btnDist; wi.g.y = btnDist; wi.text = "Back"; wi.g.parent = ghSubContainer; ghButton1Back = gwinButtonCreate(0, &wi); // Make the container become visible - therefore all its children // become visible as well gwinHide(ghSubContainer); gwinShow(ghMainContainer);}static GListener gl;int main(void) { // Initialize the display gfxInit(); // Set the widget defaults gwinSetDefaultFont(gdispOpenFont("*")); gwinSetDefaultStyle(&myStyle, FALSE);// gwinSetDefaultStyle(&WhiteWidgetStyle, FALSE); gdispClear(Black); // Create the widget createWidgets(); geventListenerInit(&gl); gwinAttachListener(&gl); GEvent* pe; while(1) { pe = geventEventWait(&gl, TIME_INFINITE); switch(pe->type) { case GEVENT_GWIN_BUTTON: if (((GEventGWinButton*)pe)->gwin == ghButton1) { gwinHide(ghMainContainer); gwinShow(ghSubContainer); } else if (((GEventGWinButton*)pe)->gwin == ghButton1Back) { gwinHide(ghSubContainer); gwinShow(ghMainContainer); } break; default: break; } } return 0;}
crteensy Posted April 13, 2015 Author Report Posted April 13, 2015 Also, when the label is dragged across one of the buttons, the button is "damaged". It should be redrawn at least after the label is placed at its destination (I solved that by now, when the label is placed after being moved it calls gwinRedrawDisplay()).
crteensy Posted April 13, 2015 Author Report Posted April 13, 2015 My current workaround is this: // A mouse move has occurred static void MouseMove(GWidgetObject *gw, coord_t x, coord_t y) { if (((GDragLabelObject*)gw)->beingDragged == TRUE) { GDragLabelObject* p = (GDragLabelObject*)gw; p->moved = TRUE;// gwinMove((GWindowObject *)gw, p->w.g.x+x-p->hotspot_x, p->w.g.y+y-p->hotspot_y); coord_t winx = gwinGetScreenX((GWindowObject*)gw); coord_t winy = gwinGetScreenY((GWindowObject*)gw); printf("winx = %d, winy = %d\n", winx, winy); #if(GWIN_NEED_CONTAINERS) if (gw->g.parent) { GWindowObject* parent = gw->g.parent; winx -= parent->x; winy -= parent->y; printf(" relx = %d, rely = %d\n", winx, winy); } #endif gwinMove((GWindowObject *)gw, winx+x-p->hotspot_x, winy+y-p->hotspot_y); } else { printf("empty move\n"); } }is there something like gwinGetRelX() and gwinGetRelY() functions or maybe even a more elegant way that I haven't found?
inmarket Posted April 13, 2015 Report Posted April 13, 2015 Overlapping window redrawing is something that ugfx doesn't handle because the code to do so is very complex and it requires fairly large ram tables (not good for an embedded platform). In practice this is seldom a problem due to containers and rudimentary support for Z order. Your use case however is slightly different and it is why you see the artifacts when dragging over a button. You solution is probably the best one, just to redraw the screen afterwards.With regard to parent relative coordinates, widgets always store their coordinates relative to the display. The reason for this is that it makes drawing in each widget much simpler. GwinMove, being a user api, takes coordinates relative to the parent. Your solution to correct the coordinates is the right approach however you also need to subtract the top and left border width. There are calls to get the border or at least the inner width and height of a container. Look at the code for gwinMove itself. Creating an api to get the relative position is probably a good idea. I will look at that when I get some time. Nice work.
crteensy Posted April 14, 2015 Author Report Posted April 14, 2015 It's not quite clear to me how I would displace a widget within its parent container. I have the following code, but the widget always ends up in one of the corners after this has been done a few times with dx = dy = 0:int dx = 0;//round(c4*it->force_x);int dy = 0;//round(c4*it->force_y);coord_t x = gwinGetScreenX(lblHandle) + dx;coord_t y = gwinGetScreenY(lblHandle) + dy;x -= gwinGetScreenX(ghContainer);y -= gwinGetScreenY(ghContainer);uint8_t border = 2;x -= border;y -= border;printf("d = %03d %03d, c = %03d %03d\n", dx, dy, x, y);gwinMove(lblHandle, x, y);with border < 2 it's in the lower right corner, with border = 2 it's in the upper left. I can't find a coordinate combo that does what I want. You already said that gwinMove() is not thoroughly tested - might that be the culprit? Working with the container's inner height and width might also work, but I expect to get off-by-one errors with that, too.
crteensy Posted April 14, 2015 Author Report Posted April 14, 2015 I'm ignoring that issue now because drawing with screen absolute coordinates works quite well (set x and y in the widget struct and then redraw). Here's a video:http://youtu.be/vMinMt-Qn8A
inmarket Posted April 14, 2015 Report Posted April 14, 2015 Of course. That makes lots of sense. Just change the x, y (provided they are still inside the patent container) and then redraw the parent container
inmarket Posted April 14, 2015 Report Posted April 14, 2015 Nice video.I noticed some redraw delays in a few instances. If that is really on the display and not just a video artifact have a look at the gwin window manager redraw options for your gfxconf.hThese trade of stack depth required, visual flashing, and visual responsiveness. You might find a setting that works better for your application.
Joel Bodenmann Posted April 15, 2015 Report Posted April 15, 2015 Here's a video: http://youtu.be/vMinMt-Qn8AWhat hardware platform are you targeting? This might definitely look and feel a lot less smooth on a small microcontroller ~ Tectu
crteensy Posted April 15, 2015 Author Report Posted April 15, 2015 I'm targeting a Cortex-M0 platform with an RA8875 controlled TFT (you know my other threads). I'll try to get it to run on that platform today. My impression regarding real hardware vs X11 drawing is that X11 looks more sluggish and less "crisp". When I draw a character on my TFT it is "just there", whereas it looks like being built on X11.My force directed graph code is probably too CPU hungry and I'd probably have to use fixed point calculations instead of floating point. Using std::list is probably also a very bad idea...I'll also have a look at the different window manager options, thanks for that hint!
crteensy Posted April 15, 2015 Author Report Posted April 15, 2015 Running on real hardware with a touch panel now. The Cortex-M0 I wanted to use simply doesn't have enough flash space to hold the application, so I switched to a larger Cortex-M4 board. It's still using floating point calculations for the forces acting on my labels and turns out the be quite responsive and fast. Crappy calibration turns out to be a problem when I try to drag items around, because not hitting an item results in another one being added, making the place quite crowded at times. Many force directed items are quite entertaining to look at, especially if their initial position is off screen. They are pushed into a place far far away that somewhen "rolls over" to zero, making the label appear on screen again and then trying to make its way to the button it belongs to.GWIN_REDRAW_IMMEDIATE (the only option I tried so far) results in far less drawing artifacts. I just don't know what exactly it does and what the tradeoff is. The problem is that at some (seemingly random) point my app crashes, which might be related to my memory management.I'll try to make a video later today!
crteensy Posted April 15, 2015 Author Report Posted April 15, 2015 When my application crashes/hangs, it seems to be stuck in gfxYield(). Without the call to gfxYield() I don't get any touch events, that's why I added it in the first place. I'm not sure why it hangs there, but it's after I touched the touch panel.
inmarket Posted April 15, 2015 Report Posted April 15, 2015 Perhaps stack on the main thread.Redraw immediate redraws the moment it is asked for rather than delaying it to the timer thread.That has the effect of more immediate redraws but it is less efficient (more overdraw) and requires more stack space on the main thread.
inmarket Posted April 15, 2015 Report Posted April 15, 2015 GfxYield or gfxSleepMilliseconds is required to give other threads a chance to run - in this case the gtimer thread which is responsible for redraw (as discussed above) and for polling input devices such as your touch panel.The reason the yield is required to make other threads run is that you are running bare metal and the threading implemented for the bare metal GOS is co-operative rather than preemptive.
crteensy Posted April 16, 2015 Author Report Posted April 16, 2015 The purpose of gfxYield() is now quite clear to me in theory, it just surprised me that the touch panel driver wouldn't get polled without it while mouse input (when using X11) "just worked".Nonetheless, the problem remains. At the moment I can add items withoug problems, and drag them around. However, when I have removed one by clicking on it, the system hangs when I try to add the next item. This is not 100% reproducible, sometimes adding a new item works fine after having removed one previously. FWIW, here's my code if anyone wants to have a look:ContainerButton is basically a container, but it sends a button event when its background is clicked. One of these is my main container that I add "Items" to.containerButton.h:#ifndef CONTAINER_BUTTON_H#define CONTAINER_BUTTON_H#include "gfx.h"//typedef GWidgetObject GContainerButtonObject;#ifdef __cplusplusextern "C" {#endiftypedef struct GContainerButtonObject { GWidgetObject w; coord_t click_x; coord_t click_y;} GContainerButtonObject;GHandle gwinGContainerButtonCreate(GDisplay *g, GContainerButtonObject *gc, const GWidgetInit *pInit, uint32_t flags);#define gwinContainerButtonCreate(gc, pInit, flags) gwinGContainerButtonCreate(GDISP, gc, pInit, flags)void gwinContainerButtonDraw_Std(GWidgetObject *gw, void *param);void gwinContainerButtonDraw_Transparent(GWidgetObject *gw, void *param);void gwinContainerButtonDraw_Image(GWidgetObject *gw, void *param);#ifdef __cplusplus}#endif#endif // CONTAINER_BUTTON_HcontainerButton.c:#include "containerButton.h"#if GFX_USE_GWIN && GWIN_NEED_CONTAINER#include "src/gwin/gwin_class.h"#define GBUTTON_FLG_PRESSED (GWIN_FIRST_CONTROL_FLAG<<0)#if GINPUT_NEED_MOUSE // A mouse down has occurred over the button static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) { (void) x; (void) y; gw->g.flags |= GBUTTON_FLG_PRESSED; _gwinUpdate((GHandle)gw); } // A mouse up has occurred (it may or may not be over the button) static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) { (void) x; (void) y; gw->g.flags &= ~GBUTTON_FLG_PRESSED; _gwinUpdate((GHandle)gw); #if !GWIN_BUTTON_LAZY_RELEASE // If the mouse up was not over the button then cancel the event if (x < 0 || y < 0 || x >= gw->g.width || y >= gw->g.height) return; #endif ((GContainerButtonObject*)gw)->click_x = x; ((GContainerButtonObject*)gw)->click_y = y; _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON); }#endif // GINPUT_NEED_MOUSE#if GWIN_CONTAINER_BORDER != GWIN_FIRST_CONTROL_FLAG #error "GWIN Container: - Flag definitions don't match"#endif#define BORDER_WIDTH 2static coord_t BorderSize(GHandle gh) { return (gh->flags & GWIN_CONTAINER_BORDER) ? BORDER_WIDTH : 0; }// The container VMT tablestatic const gcontainerVMT containerButtonVMT = { { { "Container", // The classname sizeof(GContainerObject), // The object size _gcontainerDestroy, // The destroy routine _gcontainerRedraw, // The redraw routine 0, // The after-clear routine }, gwinContainerButtonDraw_Std, // The default drawing routine #if GINPUT_NEED_MOUSE { MouseUp, MouseDown, 0, // not used }, #endif #if GINPUT_NEED_TOGGLE { 0, 0, 0, 0, 0, // No toggles }, #endif #if GINPUT_NEED_DIAL { 0, 0, 0, 0, // No dials }, #endif }, BorderSize, // The size of the left border (mandatory) BorderSize, // The size of the top border (mandatory) BorderSize, // The size of the right border (mandatory) BorderSize, // The size of the bottom border (mandatory) 0, // A child has been added (optional) 0, // A child has been deleted (optional)};GHandle gwinGContainerButtonCreate(GDisplay *g, GContainerButtonObject *gc, const GWidgetInit *pInit, uint32_t flags){ if (!(gc = (GContainerButtonObject *)_gcontainerCreate(g, &gc->w, pInit, &containerButtonVMT))) return 0; GWidgetObject* gw = (GWidgetObject*)gc; gw->g.flags |= (flags & GWIN_CONTAINER_BORDER); gwinSetVisible((GHandle)gw, pInit->g.show); return (GHandle)gc;}void gwinContainerButtonDraw_Transparent(GWidgetObject *gw, void *param) { (void)param; if (gw->g.vmt != (gwinVMT *)&containerButtonVMT) return; if ((gw->g.flags & GWIN_CONTAINER_BORDER)) gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge); // Don't touch the client area}void gwinContainerButtonDraw_Std(GWidgetObject *gw, void *param) { (void)param; if (gw->g.vmt != (gwinVMT *)&containerButtonVMT) return; gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); gwinContainerDraw_Transparent(gw, param);}#if GDISP_NEED_IMAGE void gwinContainerButtonDraw_Image(GWidgetObject *gw, void *param) { #define gi ((gdispImage *)param) coord_t x, y, iw, ih, mx, my; if (gw->g.vmt != (gwinVMT *)&containerButtonVMT) return; // Draw the frame gwinContainerDraw_Transparent(gw, param); // Draw the client area by tiling the image mx = gw->g.x+gw->g.width; my = gw->g.y+gw->g.height; y = gw->g.y; if ((gw->g.flags & GWIN_CONTAINER_BORDER)) { mx--; my--; y++; } for(ih=gi->height; y < my; y += ih) { if (ih > my - y) ih = my - y; x = gw->g.x; if ((gw->g.flags & GWIN_CONTAINER_BORDER)) x++; for(iw=gi->width; x < mx; x += iw) { if (iw > mx - x) iw = mx - x; gdispGImageDraw(gw->g.display, gi, x, y, ih, iw, 0, 0); } } #undef gi }#endif#endifDraggableButton is a button, but it can also be dragged around with the mouse.draggableButton.h:#include "gfx.h"#ifdef __cplusplusextern "C" {#endiftypedef struct GDraggableButtonObject { GWidgetObject w; #if GINPUT_NEED_TOGGLE uint16_t toggle; #endif coord_t hotspot_x; coord_t hotspot_y;} GDraggableButtonObject;GHandle gwinGDraggableButtonCreate(GDisplay *g, GDraggableButtonObject *gb, const GWidgetInit *pInit);#define gwinDraggableButtonCreate(gb, pInit) gwinGDraggableButtonCreate(GDISP, gb, pInit)void gwinDraggableButtonDraw_Normal(GWidgetObject *gw, void *param); // @< A standard button#if GDISP_NEED_ARC || defined(__DOXYGEN__) void gwinDraggableButtonDraw_Rounded(GWidgetObject *gw, void *param); // @< A rounded rectangle button#endif#if GDISP_NEED_ELLIPSE || defined(__DOXYGEN__) void gwinDraggableButtonDraw_Ellipse(GWidgetObject *gw, void *param); // @< A circular button#endif#if GDISP_NEED_CONVEX_POLYGON || defined(__DOXYGEN__) void gwinDraggableButtonDraw_ArrowUp(GWidgetObject *gw, void *param); // @< An up arrow button void gwinDraggableButtonDraw_ArrowDown(GWidgetObject *gw, void *param); // @< A down arrow button void gwinDraggableButtonDraw_ArrowLeft(GWidgetObject *gw, void *param); // @< A left arrow button void gwinDraggableButtonDraw_ArrowRight(GWidgetObject *gw, void *param); // @< A right arrow button#endif#if GDISP_NEED_IMAGE || defined(__DOXYGEN__) void gwinDraggableButtonDraw_Image(GWidgetObject *gw, void *param); // @< An image button - see the notes above on the param.#endif#ifdef __cplusplus}#endifdraggableButton.h:#include "draggableButton.h"#include #include "src/gwin/gwin_class.h"// Our pressed state#define GBUTTON_FLG_PRESSED (GWIN_FIRST_CONTROL_FLAG<<0)#define GBUTTON_FLG_MOVED (GWIN_FIRST_CONTROL_FLAG<<1)#if GINPUT_NEED_MOUSE // A mouse down has occurred over the button static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) { GDraggableButtonObject* p = (GDraggableButtonObject*)gw; gw->g.flags |= GBUTTON_FLG_PRESSED; p->hotspot_x = x; p->hotspot_y = y; _gwinUpdate((GHandle)gw); } // A mouse up has occurred (it is over the button, because the button can be dragged) static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) { (void) x; (void) y; gw->g.flags &= ~GBUTTON_FLG_PRESSED; _gwinUpdate((GHandle)gw); if(!(gw->g.flags & GBUTTON_FLG_MOVED)) { _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON); } else // dropped { gw->g.flags &= ~GBUTTON_FLG_MOVED; gwinRedrawDisplay(GDISP, FALSE); } } // A mouse move has occurred static void MouseMove(GWidgetObject *gw, coord_t x, coord_t y) { GDraggableButtonObject* p = (GDraggableButtonObject*)gw; coord_t winx = gwinGetScreenX((GWindowObject*)gw); coord_t winy = gwinGetScreenY((GWindowObject*)gw); gw->g.flags |= GBUTTON_FLG_MOVED; #if(GWIN_NEED_CONTAINERS) if (gw->g.parent) { GWindowObject* parent = gw->g.parent; winx -= parent->x; winy -= parent->y; } #endif gwinMove((GWindowObject *)gw, winx+x-p->hotspot_x, winy+y-p->hotspot_y); }#endif#if GINPUT_NEED_TOGGLE // A toggle off has occurred static void ToggleOff(GWidgetObject *gw, uint16_t role) { (void) role; gw->g.flags &= ~GBUTTON_FLG_PRESSED; _gwinUpdate((GHandle)gw); } // A toggle on has occurred static void ToggleOn(GWidgetObject *gw, uint16_t role) { (void) role; gw->g.flags |= GBUTTON_FLG_PRESSED; _gwinUpdate((GHandle)gw); // Trigger the event on button down (different than for mouse/touch) _gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON); } static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) { (void) role; ((GButtonObject *)gw)->toggle = instance; } static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) { (void) role; return ((GButtonObject *)gw)->toggle; }#endif// The button VMT tablestatic const gwidgetVMT draggableButtonVMT = { { "Draggable Button", // The classname sizeof(GButtonObject), // The object size _gwidgetDestroy, // The destroy routine _gwidgetRedraw, // The redraw routine 0, // The after-clear routine }, gwinDraggableButtonDraw_Normal, // The default drawing routine #if GINPUT_NEED_MOUSE { MouseDown, // Process mouse down events MouseUp, // Process mouse up events MouseMove, // Process mouse move events }, #endif #if GINPUT_NEED_TOGGLE { 1, // 1 toggle role ToggleAssign, // Assign Toggles ToggleGet, // Get Toggles ToggleOff, // Process toggle off events ToggleOn, // Process toggle on events }, #endif #if GINPUT_NEED_DIAL { 0, // No dial roles 0, // Assign Dials (NOT USED) 0, // Get Dials (NOT USED) 0, // Process dial move events (NOT USED) }, #endif};GHandle gwinGDraggableButtonCreate(GDisplay *g, GDraggableButtonObject *gw, const GWidgetInit *pInit) { if (!(gw = (GDraggableButtonObject *)_gwidgetCreate(g, &gw->w, pInit, &draggableButtonVMT))) return 0; #if GINPUT_NEED_TOGGLE gw->toggle = GWIDGET_NO_INSTANCE; #endif gwinSetVisible((GHandle)gw, pInit->g.show); return (GHandle)gw;}/*---------------------------------------------------------- * Custom Draw Routines *----------------------------------------------------------*/static const GColorSet *getDrawColors(GWidgetObject *gw) { if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled; if ((gw->g.flags & GBUTTON_FLG_PRESSED)) return &gw->pstyle->pressed; return &gw->pstyle->enabled;}#if GWIN_FLAT_STYLING void gwinDraggableButtonDraw_Normal(GWidgetObject *gw, void *param) { const GColorSet * pcol; (void) param; if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return; pcol = getDrawColors(gw); gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); }#else void gwinDraggableButtonDraw_Normal(GWidgetObject *gw, void *param) { const GColorSet * pcol; fixed alpha; fixed dalpha; coord_t i; color_t tcol, bcol; (void) param; if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return; pcol = getDrawColors(gw); /* Fill the box blended from variants of the fill color */ tcol = gdispBlendColor(White, pcol->fill, TOP_FADE); bcol = gdispBlendColor(Black, pcol->fill, BOTTOM_FADE); dalpha = FIXED(255)/gw->g.height; for(alpha = 0, i = 0; i < gw->g.height; i++, alpha += dalpha) gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+i, gw->g.x+gw->g.width-2, gw->g.y+i, gdispBlendColor(bcol, tcol, NONFIXED(alpha))); gdispGDrawStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width-1, gw->g.height-1, gw->text, gw->g.font, pcol->text, justifyCenter); gdispGDrawLine(gw->g.display, gw->g.x+gw->g.width-1, gw->g.y, gw->g.x+gw->g.width-1, gw->g.y+gw->g.height-1, pcol->edge); gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gw->g.height-1, gw->g.x+gw->g.width-2, gw->g.y+gw->g.height-1, pcol->edge); }#endif#if GDISP_NEED_ARC void gwinDraggableButtonDraw_Rounded(GWidgetObject *gw, void *param) { const GColorSet * pcol; (void) param; if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return; pcol = getDrawColors(gw); gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); if (gw->g.width >= 2*RND_CNR_SIZE+10) { gdispGFillRoundedBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, RND_CNR_SIZE-1, pcol->fill); gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+RND_CNR_SIZE, gw->g.width-2, gw->g.height-(2*RND_CNR_SIZE), gw->text, gw->g.font, pcol->text, justifyCenter); gdispGDrawRoundedBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, RND_CNR_SIZE, pcol->edge); } else { gdispGFillStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, pcol->fill, justifyCenter); gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); } }#endif#if GDISP_NEED_ELLIPSE void gwinDraggableButtonDraw_Ellipse(GWidgetObject *gw, void *param) { const GColorSet * pcol; (void) param; if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return; pcol = getDrawColors(gw); gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); gdispGFillEllipse(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width/2-1, gw->g.height/2-1, pcol->fill); gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); gdispGDrawEllipse(gw->g.display, gw->g.x, gw->g.y, gw->g.width/2, gw->g.height/2, pcol->edge); }#endif#if GDISP_NEED_CONVEX_POLYGON void gwinDraggableButtonDraw_ArrowUp(GWidgetObject *gw, void *param) { const GColorSet * pcol; (void) param; point arw[7]; if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return; pcol = getDrawColors(gw); arw[0].x = gw->g.width/2; arw[0].y = 0; arw[1].x = gw->g.width-1; arw[1].y = gw->g.height/ARROWHEAD_DIVIDER; arw[2].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[2].y = gw->g.height/ARROWHEAD_DIVIDER; arw[3].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[3].y = gw->g.height-1; arw[4].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[4].y = gw->g.height-1; arw[5].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[5].y = gw->g.height/ARROWHEAD_DIVIDER; arw[6].x = 0; arw[6].y = gw->g.height/ARROWHEAD_DIVIDER; gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); } void gwinDraggableButtonDraw_ArrowDown(GWidgetObject *gw, void *param) { const GColorSet * pcol; (void) param; point arw[7]; if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return; pcol = getDrawColors(gw); arw[0].x = gw->g.width/2; arw[0].y = gw->g.height-1; arw[1].x = gw->g.width-1; arw[1].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; arw[2].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[2].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; arw[3].x = (gw->g.width + gw->g.width/ARROWBODY_DIVIDER)/2; arw[3].y = 0; arw[4].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[4].y = 0; arw[5].x = (gw->g.width - gw->g.width/ARROWBODY_DIVIDER)/2; arw[5].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; arw[6].x = 0; arw[6].y = gw->g.height-1-gw->g.height/ARROWHEAD_DIVIDER; gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); } void gwinDraggableButtonDraw_ArrowLeft(GWidgetObject *gw, void *param) { const GColorSet * pcol; (void) param; point arw[7]; if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return; pcol = getDrawColors(gw); arw[0].x = 0; arw[0].y = gw->g.height/2; arw[1].x = gw->g.width/ARROWHEAD_DIVIDER; arw[1].y = 0; arw[2].x = gw->g.width/ARROWHEAD_DIVIDER; arw[2].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; arw[3].x = gw->g.width-1; arw[3].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; arw[4].x = gw->g.width-1; arw[4].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; arw[5].x = gw->g.width/ARROWHEAD_DIVIDER; arw[5].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; arw[6].x = gw->g.width/ARROWHEAD_DIVIDER; arw[6].y = gw->g.height-1; gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); } void gwinDraggableButtonDraw_ArrowRight(GWidgetObject *gw, void *param) { const GColorSet * pcol; (void) param; point arw[7]; if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return; pcol = getDrawColors(gw); arw[0].x = gw->g.width-1; arw[0].y = gw->g.height/2; arw[1].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[1].y = 0; arw[2].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[2].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; arw[3].x = 0; arw[3].y = (gw->g.height - gw->g.height/ARROWBODY_DIVIDER)/2; arw[4].x = 0; arw[4].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; arw[5].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[5].y = (gw->g.height + gw->g.height/ARROWBODY_DIVIDER)/2; arw[6].x = gw->g.width-1-gw->g.width/ARROWHEAD_DIVIDER; arw[6].y = gw->g.height-1; gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); gdispGFillConvexPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->fill); gdispGDrawPoly(gw->g.display, gw->g.x, gw->g.y, arw, 7, pcol->edge); gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); }#endif#if GDISP_NEED_IMAGE || defined(__DOXYGEN__) void gwinDraggableButtonDraw_Image(GWidgetObject *gw, void *param) { const GColorSet * pcol; coord_t sy; if (gw->g.vmt != (gwinVMT *)&draggableButtonVMT) return; pcol = getDrawColors(gw); if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) { sy = 2 * gw->g.height; } else if ((gw->g.flags & GBUTTON_FLG_PRESSED)) { sy = gw->g.height; } else { sy = 0; } gdispGImageDraw(gw->g.display, (gdispImage *)param, gw->g.x, gw->g.y, gw->g.width, gw->g.height, 0, sy); gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter); }#endifand my main.cpp, including the "Item" class that takes care of adding buttons, labels, and moving them around:#include #include #include #include #include #include "gfx.h"#include "src/gwin/gwin_class.h"#include "ugfx_user/containerButton/containerButton.h"#include "ugfx_user/draggableButton/draggableButton.h"#define DEBUG(x) Serial.printf x ;Serial.flush()//#define DEBUG(x)static GHandle ghContainer;static GWidgetStyle myStyle = { White, // background {Black, SkyBlue, SkyBlue, Black}, // enabled (text edge fill progress) {White, Gray, Gray, Black}, // disabled {White, Orange, Orange, Black} // pressed};static void createWidgets(void) { coord_t height = gdispGetHeight(); coord_t width = gdispGetWidth(); GWidgetInit wi; // Apply some default values for GWIN gwinWidgetClearInit(&wi); // Apply the container parameters wi.g.show = FALSE; wi.g.width = width; wi.g.height = height; wi.g.x = 0; wi.g.y = 0; wi.text = "Container"; ghContainer = gwinContainerButtonCreate(0, &wi, 0);//GWIN_CONTAINER_BORDER); wi.g.show = TRUE; gwinShow(ghContainer);}class Item{public: static void add(coord_t x, coord_t y) { // create a new button DEBUG(("Item::create (%d,%d)\n", x, y)); Item* p = new Item; if(p == NULL) { DEBUG(("Could not allocate space for a new item\n")); while(1); } DEBUG(("Initializing item @%p\n", p)); p->init(x,y); DEBUG(("Prepending item @%p to list\n", p)); p->next = pItems; pItems = p; DEBUG(("add: done\n")); } static void remove(GHandle gh) { // find item with btn handle gh DEBUG(("remove(%p)\n", gh)); Item* p = pItems; while((p != NULL) && (p->_btnHandle != gh)) { p = p->next; } if(p == NULL) { // not found DEBUG(("remove: item not found\n")); return; } // now we're sure that an object with this button handle exists and it is at p DEBUG(("remove p @%p\n", p)); DEBUG(("p->_btnHandle = %p\n", p->_btnHandle)); // p points to the item that is to be removed if(pItems == p) { DEBUG(("Removing root item, next = %p\n", p->next)); pItems = p->next; } else { DEBUG(("Removing non-root item, next = %p\n", p->next)); Item* prev = pItems; while((prev != NULL) && (prev->next != p)) { prev = prev->next; } if(prev->next == p) { // found DEBUG(("Found previous item @%p\n",prev)); DEBUG((" prev->next = %p\n",prev->next)); prev->next = p->next; } else { DEBUG(("your list is borked\n")); } } p->deinit(); DEBUG(("remove: item deinitialized\n")); delete p; } Item() { DEBUG(("Item being constructed @%p\n", this)); _btnHandle = NULL; _lblHandle = NULL; next = NULL; } ~Item() { DEBUG(("Item @%p being deleted\n", this)); } static bool update() { coord_t left = 0; coord_t right = gdispGetWidth()-1; coord_t top = 0; coord_t bottom = gdispGetHeight()-1; // calculate forces on labels Item* it = pItems; while(it != NULL) { // reset double fx = 0; double fy = 0; coord_t labelWidth = gwinGetWidth(it->_lblHandle); coord_t labelHeight = gwinGetHeight(it->_lblHandle); double label_x = gwinGetScreenX(it->_lblHandle) + labelWidth/2.; double label_y = gwinGetScreenY(it->_lblHandle) + labelHeight/2.; double btn_x = gwinGetScreenX(it->_btnHandle) + gwinGetWidth(it->_btnHandle)/2.; double btn_y = gwinGetScreenY(it->_btnHandle) + gwinGetHeight(it->_btnHandle)/2.; // add forces for screen boundary fx -= c3/pow(right-label_x, 2); fx += c3/pow(label_x - left, 2); fy -= c3/pow(bottom-label_y, 2); fy += c3/pow(label_y - top, 2); // add force for this item's button double dx = btn_x - label_x; double dy = btn_y - label_y; double dist_sq = pow(dx,2) + pow(dy,2); double dist = sqrt(dist_sq); double f = c1*log(dist/c2); fx += f*dx/dist; fy += f*dy/dist; // add forces for each other item Item* other = pItems; while(other != NULL) { if(other == it) { other = other->next; continue; } coord_t other_x = gwinGetScreenX(other->_lblHandle) + gwinGetWidth(other->_lblHandle)/2; coord_t other_y = gwinGetScreenY(other->_lblHandle) + gwinGetHeight(other->_lblHandle)/2; dx = other_x - label_x; dy = other_y - label_y; dist_sq = pow(dx,2) + pow(dy,2); dist = sqrt(dist_sq); f = c3/dist_sq; fx -= dx/dist*f; fy -= dy/dist*f; other_x = gwinGetScreenX(other->_btnHandle) + gwinGetWidth(other->_btnHandle)/2; other_y = gwinGetScreenY(other->_btnHandle) + gwinGetHeight(other->_btnHandle)/2; dx = other_x - label_x; dy = other_y - label_y; dist_sq = pow(dx,2) + pow(dy,2); dist = sqrt(dist_sq); f = c3/dist_sq; fx -= dx/dist*f; fy -= dy/dist*f; other = other->next; } it->force_x = fx; it->force_y = fy; it = it->next; } // move labels it = pItems; bool somethingMoved = false; while(it != NULL) { int dx = round(c4*it->force_x); int dy = round(c4*it->force_y); bool thisMoved = ((dx != 0) || (dy != 0)); somethingMoved |= thisMoved; coord_t x = gwinGetScreenX(it->_lblHandle) + dx; coord_t y = gwinGetScreenY(it->_lblHandle) + dy; it->_lblHandle->x = x; it->_lblHandle->y = y; if(thisMoved) { gwinRedraw(it->_lblHandle); } it = it->next; } return somethingMoved; } void init(coord_t x, coord_t y) { DEBUG(("Item::init (%d,%d)\n", x, y)); GWidgetInit wi; gwinWidgetClearInit(&wi); wi.g.x = x - btnSize/2; wi.g.y = y - btnSize/2; wi.g.width = btnSize; wi.g.height = btnSize; wi.g.parent = ghContainer; wi.text = " "; wi.g.show = TRUE; _btnHandle = gwinDraggableButtonCreate(0, &wi); if(_btnHandle == NULL) { DEBUG(("invalid button handle!\n")); while(1); } wi.g.x = x + btnSize/2 + 5; wi.g.y = y + btnSize/2 + 5; wi.g.width = 0; wi.g.height = 0; wi.text = " "; _lblHandle = gwinLabelCreate(0, &wi); if(_lblHandle == NULL) { DEBUG(("invalid label handle!\n")); while(1); } char str[4]; snprintf(str, 4, "%u", (unsigned int)nextNumber); gwinSetText(_lblHandle, str, TRUE); nextNumber++; DEBUG(("Item::init done\n", x, y)); } void deinit() { gwinDestroy(_btnHandle); gwinDestroy(_lblHandle); next = NULL; }private: static Item* lastItem() { Item* p = pItems; while((p != NULL ) && (p->next != NULL)) { p = p->next; } return p; } static Item* previousItem(const Item* i) { // fint item such that item->next = i Item* p = pItems; while(p != NULL) { if(p->next == i) { DEBUG(("previousItem found: p->next = %p, i = %p\n", p->next, i)); break; } p = p->next; } return p; } static Item* pItems; static uint8_t nextNumber; static const uint8_t btnSize = 32; static constexpr double c1 = 2; static constexpr double c2 = 1.5*btnSize; static constexpr double c3 = 2560; static constexpr double c4 = 1; Item* next; GHandle _btnHandle; GHandle _lblHandle; double force_x; double force_y;};Item* Item::pItems = NULL;uint8_t Item::nextNumber = 0;void setup(){// while(!Serial.available()); // wait for serial input before starting the app DEBUG(("Start")); SPI.begin();}static GListener gl;void loop(void) { gfxInit(); // Set the widget defaults gwinSetDefaultFont(gdispOpenFont("UI2 Narrow")); gwinSetDefaultStyle(&myStyle, FALSE); gdispClear(White); createWidgets(); geventListenerInit(&gl); gwinAttachListener(&gl); GEvent* pe; while(1) { // alive-check over serial if (Serial.available()) { Serial.read(); Serial.printf("alive!\n"); } pe = geventEventWait(&gl, 0); if (pe != NULL) { switch(pe->type) { case GEVENT_GWIN_BUTTON: if(((GEventGWinButton*)pe)->gwin == ghContainer) { GContainerButtonObject* p = (GContainerButtonObject*)((GEventGWinButton*)pe)->gwin; coord_t x = p->click_x; coord_t y = p->click_y; DEBUG(("add @ %d %d\n", x, y)); Item::add(x,y); } else { // if one of the buttons was clicked, remove it Item::remove(((GEventGWinButton*)pe)->gwin); gwinRedraw(ghContainer); } break; default: DEBUG(("unhandled event type\n")); break; } DEBUG(("Event handler done\n")); } static bool keepUpdating = false; static systemticks_t last = gfxSystemTicks(); systemticks_t now = gfxSystemTicks(); if (((now - last) >= 100) || keepUpdating) { last = now; if (Item::update()) { keepUpdating = true; } else { keepUpdating = false; } } gfxYield(); }}Still no video of the real hardware, sorry!
crteensy Posted April 16, 2015 Author Report Posted April 16, 2015 I'm making frequent use of gwinDestroy(). Is it possible that under certain circumstances ugfx tries to access or redraw a widget that has been destroyed previously? Can widget desctruction have any other side effects?my application does not crash/hang when I replace gwinDestroy() with gwinHide() (that's an obvious memory leak, but I wanted to test the behavior with that).
inmarket Posted April 16, 2015 Report Posted April 16, 2015 It is certainly possible there is a bug in that area. It is not not often used by most gfx users so it is possible there is a bug there (i wish we always wrote perfect code).I will look closer tomorrow As it is getting a bit late here.
inmarket Posted April 16, 2015 Report Posted April 16, 2015 Just a note on the gfxYield, if you replace the 0 in the geventEventWait call with a 1 I suspect the gfxYield will not be needed.
crteensy Posted April 16, 2015 Author Report Posted April 16, 2015 Just a note on the gfxYield, if you replace the 0 in the geventEventWait call with a 1 I suspect the gfxYield will not be needed.Yes indeed, thanks for that! I kinda hoped that this would also make my other problem disappear, but it's still there. Thanks for having a look even if it's next week.
inmarket Posted April 16, 2015 Report Posted April 16, 2015 Does the same behaviour occur in X with the same redraw flags?If not then it is possible the arduino memory allocator is stack sensitive (i have seen this before although it shouldn't happen in any well written host routine). Unfortunately the only way to test it is to replace the memory allocation/free routines in the gos module with the alternative routines from raw32 gos. Those routines require you to give it a one off big block of memory at init time and it will manage that block thereafter.
crteensy Posted April 16, 2015 Author Report Posted April 16, 2015 Is that done by setting GOS_RAW_HEAP_SIZE to some non-zero value in gfxconf.h? I can probably give ugfx a few kB as heap to play with and see how it goes.
crteensy Posted April 16, 2015 Author Report Posted April 16, 2015 (edited) Yup, that's it. I gave ugfx 4096 bytes and it now happily destroys and recreates widgets. I could now take this problem to the teensyduino people, but I think that's quite a rabbit hole to explore - I wouldn't even know how to start explaining this in such a way that someone else can even recreate the problem. Edited April 17, 2015 by Guest
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now