|
Post by turboxray on Nov 11, 2019 4:03:20 GMT
So I've been working on designing a widget framework. Over the weekend I've managed to implement a nice structured system for widgets. It's C code (technically a C++ file, but C code). I've emulated interface inheritance of OOP systems, but casting structs to void, but having a parent struct inside that identifies the type (for recasting), so I stuff a bunch of different struct widgets as dependencies to a parent widget, or a layer. Callbacks by function pointers are done the same wave (values are cast as void and recast back on match). They have internal states, collision boxes, etc. Honestly, I wrote the new structure over the weekend and it worked at first shot (which makes me REALLY suspicious hahaha). Next, I'll work on designing how relationships can work between parent and children widgets (or all the way down the tree), if and how widget siblings should be able to talk to each other (I'm thinking a messaging queue that they subscribe to when attached to a parent).
My first drawing widget:
DrawColorWX.check(&DrawColorWX, mouse_x, mouse_y, buttonEvent, mouseLeftButton); You have to pass itself to itself (which is passed as void, then casted as a generic widget type to get the type element, then casted to what it's supposed to be).
|
|
|
Post by turboxray on Nov 12, 2019 19:58:32 GMT
I refined the widget structure. Also made a typical pushbotton widget:
widget identifier (from when the widget is cast to void *, so attributes and type can still be read)
typedef struct WidgetHeader {
WidgetType widgetType; int hasFocus; int isDisabled; checkCollision process; void * WidgetSibling; void * WidgetParent; void * WidgetChildren;
} WidgetHeader;
push button definition
typedef struct WidgetButton {
WidgetHeader widgetHeader;
int x1, x2; int y1, y2; int buttonState; int undo; int hoverCounter; int hoverLimit; widgetState wxState; mouseButton wxButton; callback callbackType; callbackFn1 callback; callbackFn2 callbackXY; callbackFn3 callbackInt; transFormFn1 transForm1; transFormFn2 transForm2; transFormFn3 transForm3; transFormFn4 transForm4; char * title; drawButton buttonDown; drawButton buttonUp; drawButton buttonHighlight; widgetButtonDispState btnState; unsigned int arg1; unsigned int arg2; unsigned int arg3;
} WidgetButton; Processing logic for the button
void PushButtonProc(void * wid, InputData* inputEvent) {
WidgetButton * w = (WidgetButton *) wid; int x = inputEvent->mouse_x; int y = inputEvent->mouse_y; Uint8 R,G,B;
mouseButton mBtn = (mouseButton) (inputEvent->mouseButtonMask & (int) w->wxButton); mouseButtonState mBtnState = NULL_STATE;
if (w->wxButton & LEFT_BUTTON) mBtnState = (mouseButtonState) inputEvent->mouseLeftButtonState; if (w->wxButton & RIGHT_BUTTON) mBtnState = (mouseButtonState) inputEvent->mouseRightButtonState;
// Widget is disabled or no button was clicked if (w->widgetHeader.isDisabled || !mBtn) { w->wxState = STOP; return; }
// Check if within boundary if (x > w->x1 && x < w->x2 && y > w->y1 && y < w->y2) {
// Draw button first down state if (mBtnState == BUTTON_DOWN){ w->wxState = START; w->transForm4(&R,&G,&B, &w->arg2); w->buttonDown(w->title, (Uint32) w->x1, (Uint32) w->y1, R, G, B); }
// Draw button held down state else if (mBtnState == BUTTON_HELD && w->wxState == START){ w->wxState = ONGOING; w->transForm4(&R,&G,&B, &w->arg2); w->buttonDown(w->title, (Uint32) w->x1, (Uint32) w->y1, R, G, B); }
// button released, call action else if (mBtnState == BUTTON_UP && (w->wxState == START || w->wxState == ONGOING)){ w->wxState = STOP; w->transForm4(&R,&G,&B, &w->arg1); w->buttonUp(w->title, (Uint32) w->x1, (Uint32) w->y1, R, G, B); inputEvent->done = w->callback(); }
} else if ( (w->wxState == START || w->wxState == ONGOING) && mBtnState == BUTTON_UP) { w->wxState = STOP; w->transForm4(&R,&G,&B, &w->arg1); w->buttonUp(w->title, (Uint32) w->x1, (Uint32) w->y1, R, G, B); return; } }
How it's initialized
OpenBmpWX.x1 = 881; OpenBmpWX.x2 = 981; OpenBmpWX.y1 = 20; OpenBmpWX.y2 = 35; OpenBmpWX.undo = 0; OpenBmpWX.hoverLimit = 0; OpenBmpWX.widgetHeader.hasFocus = 0; OpenBmpWX.widgetHeader.isDisabled = 0; OpenBmpWX.widgetHeader.widgetType = SIMPLE_XY_XY; OpenBmpWX.widgetHeader.process = PushButtonProc; OpenBmpWX.wxState = STOP; OpenBmpWX.wxButton = LEFT_BUTTON; OpenBmpWX.callbackType = CALLBACK_VOID; OpenBmpWX.callback = OpenFile; OpenBmpWX.transForm4 = transForm_separateColor; OpenBmpWX.title = strdup(" Open BMP \0"); OpenBmpWX.buttonDown = SDL_blockprint; OpenBmpWX.buttonUp = SDL_blockprint; OpenBmpWX.buttonHighlight = SDL_blockprint; OpenBmpWX.btnState = DIS_BUTTON_UP; OpenBmpWX.arg1 = 0xffff00; OpenBmpWX.arg2 = 0x808000; OpenBmpWX.arg3 = 0xBFBF00;
Simple input event structure
inputEvent.mouse_x = mouse_x; inputEvent.mouse_y = mouse_y; inputEvent.mouseButtonMask = buttonEvent; inputEvent.mouseLeftButtonState = (int)mouseLeftButton; inputEvent.mouseRightButtonState = (int)mouseRightButton; inputEvent.timestamp = counter; inputEvent.keyBoardChar = 0; inputEvent.keyBoardCharState = 0; inputEvent.undoFlag = 0; inputEvent.done = 0;
And finally how it's deployed in the processing loop.
OpenBmpWX.widgetHeader.process(&OpenBmpWX, &inputEvent); I don't have a list yet to attach the widgets to, but since they can be cast to void* or a standard type it shouldn't be too difficult to implement. Ultimately I'd like to have a config data structure (xml or json) that's human readable, to create and initialize widgets. C doesn't support native dictionary type support, so I'd have to do a hashmap with function pointers, and the initialization code would match the function 'key' to the map (not too bad, just registering a function by adding it to the hashmap).
|
|