diff --git a/examples/basic/periodic.c b/examples/basic/periodic.c index c3ef154..069395f 100644 --- a/examples/basic/periodic.c +++ b/examples/basic/periodic.c @@ -6,16 +6,14 @@ #define Padding 20 #define PaddingContent 2 -MwWidget window, menu, box; -MwWidget boxes[32]; -int n = 0, row = -1; +MwWidget window, menu, table; MwMenu e; static void MWAPI resize(MwWidget handle, void* user, void* call) { int w = MwGetInteger(window, MwNwidth); int h = MwGetInteger(window, MwNheight); - MwVaApply(box, + MwVaApply(table, MwNx, Padding, MwNy, MwGetInteger(menu, MwNheight) + Padding, MwNwidth, w - Padding * 2, @@ -23,32 +21,6 @@ static void MWAPI resize(MwWidget handle, void* user, void* call) { NULL); } -static void newrow(void) { - if(row >= 0) { - int i; - - for(i = n; i < Columns; i++) MwCreateWidget(MwFrameClass, "frame", boxes[row], 0, 0, 0, 0); - } - - boxes[++row] = MwVaCreateWidget(MwBoxClass, "box", box, 0, 0, 0, 0, - MwNwaitLayout, 1, - NULL); - - n = 0; -} - -static void add(MwWidget w) { - int r = MwGetInteger(w, MwNratio); - - if(r == MwDEFAULT) r = 1; - - n += r; - - if(n == Columns) { - newrow(); - } -} - static void MWAPI resize_content(MwWidget handle, void* user, void* call) { MwWidget* ws = MwGetChildren(handle); if(ws != NULL) { @@ -97,14 +69,14 @@ static void MWAPI resize_frame(MwWidget handle, void* user, void* call) { } static MwWidget frame(const char* name, int width, int height, MwClass cl, ...) { - MwWidget f = MwVaCreateWidget(MwFrameClass, "frame", boxes[row], 0, 0, 0, 0, + MwWidget f = MwVaCreateWidget(MwFrameClass, "frame", table, 0, 0, 0, 0, MwNhasBorder, 1, MwNinverted, 1, NULL); - MwWidget box = MwVaCreateWidget(MwBoxClass, "box", f, 0, 0, 0, 0, + MwWidget table = MwVaCreateWidget(MwBoxClass, "table", f, 0, 0, 0, 0, MwNorientation, MwVERTICAL, NULL); - MwWidget label = MwVaCreateWidget(MwLabelClass, "label", box, 0, 0, 0, 0, + MwWidget label = MwVaCreateWidget(MwLabelClass, "label", table, 0, 0, 0, 0, MwNtext, name, MwNalignment, MwALIGNMENT_BEGINNING, MwNfixedSize, 20, @@ -112,7 +84,7 @@ static MwWidget frame(const char* name, int width, int height, MwClass cl, ...) va_list va; if(cl != NULL) { - MwWidget frame = MwCreateWidget(MwFrameClass, "frame", box, 0, 0, 0, 0); + MwWidget frame = MwCreateWidget(MwFrameClass, "frame", table, 0, 0, 0, 0); MwWidget w; va_start(va, cl); @@ -128,7 +100,8 @@ static MwWidget frame(const char* name, int width, int height, MwClass cl, ...) MwVaApply(f, "VhandledWidget", w, - MwNratio, MwGetInteger(w, MwNratio), + MwNcolumnSpan, MwGetInteger(w, MwNcolumnSpan), + MwNrowSpan, MwGetInteger(w, MwNrowSpan), NULL); } @@ -224,35 +197,32 @@ int main() { MwAddUserHandler(menu, MwNmenuHandler, menu_handler, NULL); - box = MwVaCreateWidget(MwBoxClass, "box", window, 0, 0, 0, 0, - MwNorientation, MwVERTICAL, - MwNwaitLayout, 1, - NULL); - - newrow(); + table = MwVaCreateWidget(MwTableClass, "table", window, 0, 0, 0, 0, + MwNwaitLayout, 1, + MwNcolumns, 5, + NULL); f = frame("Button", -PaddingContent, -PaddingContent, MwButtonClass, MwNtext, "Press me", NULL); - add(f); - add(MwVaCreateWidget(MwLabelClass, "label", boxes[row], 0, 0, 0, 0, - MwNbold, 1, - MwNtext, "The Periodic Table of Milsko Widgets", - MwNratio, Columns - 2, - NULL)); + MwVaCreateWidget(MwLabelClass, "label", table, 0, 0, 0, 0, + MwNbold, 1, + MwNtext, "The Periodic Table of Milsko Widgets", + MwNcolumnSpan, Columns - 2, + NULL); for(i = 0; i < 2; i++) { f = frame(i == 0 ? "CheckBox" : "RadioBox", -PaddingContent, -PaddingContent, MwBoxClass, MwNmargin, PaddingContent, NULL); w = child(f); - b = MwVaCreateWidget(MwBoxClass, "box1", w, 0, 0, 0, 0, + b = MwVaCreateWidget(MwBoxClass, "table1", w, 0, 0, 0, 0, MwNorientation, MwVERTICAL, MwNfixedSize, 16, NULL); for(j = 0; j < 5; j++) MwCreateWidget(i == 0 ? MwCheckBoxClass : MwRadioBoxClass, i == 0 ? "cb" : "rb", b, 0, 0, 0, 0); - b = MwVaCreateWidget(MwBoxClass, "box1", w, 0, 0, 0, 0, + b = MwVaCreateWidget(MwBoxClass, "table1", w, 0, 0, 0, 0, MwNorientation, MwVERTICAL, NULL); for(j = 0; j < 5; j++) { @@ -263,32 +233,25 @@ int main() { MwNalignment, MwALIGNMENT_BEGINNING, NULL); } - add(f); } f = frame("Calendar", -PaddingContent, -PaddingContent, MwCalendarClass, MwNscale, 1, NULL); - add(f); f = frame("ComboBox", -PaddingContent, 24, MwComboBoxClass, NULL); w = child(f); MwComboBoxAdd(w, -1, "Hello!"); - add(f); f = frame("Entry", -PaddingContent, 24, MwEntryClass, NULL); - add(f); f = frame("Image", -PaddingContent, -PaddingContent, MwImageClass, MwNpixmap, px, NULL); - w = child(f); - add(f); f = frame("Label", -PaddingContent, -PaddingContent, MwLabelClass, MwNtext, "Epic text\nNewline can be used too!", NULL); - add(f); f = frame("ListBox", -PaddingContent, -PaddingContent, MwListBoxClass, MwNhasHeading, 1, @@ -296,26 +259,21 @@ int main() { w = child(f); index = MwListBoxSet(w, -1, 0, "Epic title..."); index = MwListBoxSet(w, -1, 0, "Hello"); - add(f); f = frame("NumberEntry", -PaddingContent, 24, MwNumberEntryClass, NULL); - add(f); f = frame("ProgressBar", -PaddingContent, 24, MwProgressBarClass, MwNvalue, 25, NULL); - add(f); f = frame("ScrollBar", -PaddingContent, 16, MwScrollBarClass, MwNorientation, MwHORIZONTAL, NULL); - add(f); f = frame("Separator", -PaddingContent, -PaddingContent, MwSeparatorClass, NULL); - add(f); f = frame("SubWindow", -PaddingContent, -PaddingContent, MwViewportClass, - MwNratio, 2, + MwNcolumnSpan, 2, NULL); w = child(f); MwViewportSetSize(w, 512, 512); @@ -325,23 +283,20 @@ int main() { MwVaCreateWidget(MwSubWindowClass, "swnd", MwViewportGetViewport(w), 64, 64, 256, 128, MwNtitle, "Sub window 2", NULL); - add(f); f = frame("Tab", -PaddingContent, -PaddingContent, MwTabClass, - MwNratio, 2, + MwNcolumnSpan, 2, NULL); w = child(f); MwSetText(MwTabAdd(w, "ABC"), MwNbackground, "#f00"); MwSetText(MwTabAdd(w, "DEF"), MwNbackground, "#0f0"); MwSetText(MwTabAdd(w, "GHI"), MwNbackground, "#00f"); - add(f); f = frame("TreeView", -PaddingContent, -PaddingContent, MwTreeViewClass, NULL); w = child(f); v = MwTreeViewAdd(w, NULL, NULL, "abc"); v = MwTreeViewAdd(w, v, NULL, "def"); v = MwTreeViewAdd(w, v, NULL, "ghi"); - add(f); f = frame("Viewport", -PaddingContent, -PaddingContent, MwViewportClass, NULL); w = child(f); @@ -349,23 +304,14 @@ int main() { MwVaCreateWidget(MwButtonClass, "btn", MwViewportGetViewport(w), 64, 64, 128, 128, MwNtext, "Thing", NULL); - add(f); - - if(n != 0) newrow(); - if(n == 0) MwDestroyWidget(boxes[row]); MwAddUserHandler(window, MwNresizeHandler, resize, NULL); resize(window, NULL, NULL); - MwVaApply(box, + MwVaApply(table, MwNwaitLayout, 0, NULL); - for(i = 0; i < row; i++) { - MwVaApply(boxes[i], - MwNwaitLayout, 0, - NULL); - } MwLoop(window); diff --git a/include/Mw/Core.h b/include/Mw/Core.h index 80fbb97..61cb085 100644 --- a/include/Mw/Core.h +++ b/include/Mw/Core.h @@ -383,6 +383,13 @@ MWDECL void* MWAPI MwGetUser(MwWidget handle); */ MWDECL void MWAPI MwFreeWidget(MwWidget handle); +/*! + * @brief Gets the frame of the widget + * @param handle Widget + * @param rect Frame + */ +MWDECL void MWAPI MwGetFrame(MwWidget handle, MwRect* rect); + #ifdef _MILSKO MWDECL void MwForceRender_Internal(MwWidget handle); MWDECL void MwForceRender2_Internal(MwWidget handle, void* ptr); diff --git a/include/Mw/Milsko.h b/include/Mw/Milsko.h index d2e29bc..f7b4b49 100644 --- a/include/Mw/Milsko.h +++ b/include/Mw/Milsko.h @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Mw/StringDefs.h b/include/Mw/StringDefs.h index e33f2d3..3a019e0 100644 --- a/include/Mw/StringDefs.h +++ b/include/Mw/StringDefs.h @@ -49,6 +49,9 @@ #define MwNday "Iday" #define MwNwaitLayout "IwaitLayout" #define MwNscale "Iscale" +#define MwNcolumns "Icolumns" +#define MwNcolumnSpan "IcolumnSpan" +#define MwNrowSpan "IrowSpan" #define MwNtitle "Stitle" #define MwNtext "Stext" diff --git a/include/Mw/TypeDefs.h b/include/Mw/TypeDefs.h index 6d1776a..8bfa8c1 100644 --- a/include/Mw/TypeDefs.h +++ b/include/Mw/TypeDefs.h @@ -29,6 +29,7 @@ typedef struct _MwDirectoryEntry MwDirectoryEntry; typedef struct _MwBox* MwBox; typedef struct _MwSubWindow* MwSubWindow; typedef struct _MwTab* MwTab; +typedef struct _MwTable* MwTable; typedef struct _MwMouse MwMouse; #ifdef _MILSKO typedef struct _MwWidget* MwWidget; @@ -39,6 +40,7 @@ typedef void (*MwHandler)(MwWidget handle); typedef int (*MwHandlerWithStatus)(MwWidget handle); typedef void (*MwHandlerProps)(MwWidget handle, char** key); typedef void (*MwHandlerProp)(MwWidget handle, const char* key); +typedef void (*MwHandlerChildrenUpdate)(MwWidget handle, MwWidget child, int new); typedef void (*MwHandlerChildrenProp)(MwWidget handle, MwWidget child, const char* key); typedef void (*MwHandlerKey)(MwWidget handle, int key); typedef void (*MwHandlerMouse)(MwWidget handle, void* ptr); @@ -212,6 +214,11 @@ struct _MwBox { int layout; }; +struct _MwTable { + int layout; + MwWidget* widgets; +}; + struct _MwSubWindow { MwWidget frame; MwWidget minimize; @@ -251,7 +258,7 @@ struct _MwClass { MwHandlerExecute execute; MwHandler tick; MwHandler resize; - MwHandler children_update; + MwHandlerChildrenUpdate children_update; MwHandlerChildrenProp children_prop_change; MwHandlerClipboardReceived clipboard; MwHandlerProps props_change; diff --git a/include/Mw/Widget/Table.h b/include/Mw/Widget/Table.h new file mode 100644 index 0000000..1d39ec3 --- /dev/null +++ b/include/Mw/Widget/Table.h @@ -0,0 +1,24 @@ +/*! + * @file Mw/Widget/Table.h + * @brief Table widget + */ +#ifndef __MW_WIDGET_TABLE_H__ +#define __MW_WIDGET_TABLE_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * @brief Table widget class + */ +MWDECL MwClass MwTableClass; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/milsko.xml b/milsko.xml index 8518a27..837934e 100644 --- a/milsko.xml +++ b/milsko.xml @@ -103,6 +103,9 @@ + + + @@ -924,6 +927,16 @@ + + + + + + + + + + diff --git a/pl/rules.pl b/pl/rules.pl index 0c5ae4a..52f3c2a 100644 --- a/pl/rules.pl +++ b/pl/rules.pl @@ -160,6 +160,7 @@ new_object("src/widget/separator.c"); new_object("src/widget/submenu.c"); new_object("src/widget/subwindow.c"); new_object("src/widget/tab.c"); +new_object("src/widget/table.c"); new_object("src/widget/treeview.c"); new_object("src/widget/viewport.c"); new_object("src/widget/window.c"); diff --git a/src/core.c b/src/core.c index 4fd2645..f1a6c3d 100644 --- a/src/core.c +++ b/src/core.c @@ -286,7 +286,7 @@ static MwWidget MwCreateWidget_Internal(MwClass widget_class, const char* name, h->root_boldmonofont = NULL; } - if(h->parent != NULL) MwDispatch(h->parent, children_update); + if(h->parent != NULL) MwDispatch4(h->parent, children_update, h, 1); return h; } @@ -415,7 +415,7 @@ void MwDestroyWidget(MwWidget handle) { arrput(handle->parent->destroy_queue, handle); } - MwDispatch(handle->parent, children_update); + MwDispatch4(handle->parent, children_update, handle, 0); } destroy_children(handle); handle->destroyed = 1; @@ -1042,14 +1042,14 @@ void MwReparent(MwWidget handle, MwWidget new_parent) { } } - MwDispatch(handle->parent, children_update); + MwDispatch4(handle->parent, children_update, handle, 0); } handle->parent = new_parent; if(new_parent != NULL) { arrput(new_parent->children, handle); - MwDispatch(handle->parent, children_update); + MwDispatch4(handle->parent, children_update, handle, 1); } if(new_parent == NULL) { @@ -1109,4 +1109,11 @@ void* MwGetUser(MwWidget handle) { return handle->user; } +void MwGetFrame(MwWidget handle, MwRect* rect) { + rect->x = MwGetInteger(handle, MwNx); + rect->y = MwGetInteger(handle, MwNy); + rect->width = MwGetInteger(handle, MwNwidth); + rect->height = MwGetInteger(handle, MwNheight); +} + const char* MwVersionString = MwVERSION; diff --git a/src/widget/box.c b/src/widget/box.c index e6afea6..c49febb 100644 --- a/src/widget/box.c +++ b/src/widget/box.c @@ -95,18 +95,7 @@ static void draw(MwWidget handle) { } static void prop_change(MwWidget handle, const char* key) { - if(strcmp(key, MwNorientation) == 0) { - MwBox b = handle->internal; - b->layout = 1; - - MwForceRender(handle); - } -} - -static void children_prop_change(MwWidget handle, MwWidget child, const char* key) { - (void)child; - - if(strcmp(key, MwNratio) == 0 || strcmp(key, MwNfixedSize) == 0) { + if(strcmp(key, MwNorientation) == 0 || strcmp(key, MwNpadding) == 0 || strcmp(key, MwNmargin) == 0) { MwBox b = handle->internal; b->layout = 1; @@ -131,13 +120,28 @@ static void resize(MwWidget handle) { MwForceRender(handle); } -static void children_update(MwWidget handle) { - MwBox b = handle->internal; +static void children_update(MwWidget handle, MwWidget child, int new) { + MwBox b = handle->internal; + + (void)child; + (void)new; + b->layout = 1; MwForceRender(handle); } +static void children_prop_change(MwWidget handle, MwWidget child, const char* key) { + (void)child; + + if(strcmp(key, MwNratio) == 0 || strcmp(key, MwNfixedSize) == 0) { + MwBox b = handle->internal; + b->layout = 1; + + MwForceRender(handle); + } +} + MwClassRec MwBoxClassRec = { wcreate, /* create */ destroy, /* destroy */ diff --git a/src/widget/table.c b/src/widget/table.c new file mode 100644 index 0000000..e28612d --- /dev/null +++ b/src/widget/table.c @@ -0,0 +1,228 @@ +#include + +#include "../../external/stb_ds.h" + +static int wcreate(MwWidget handle) { + MwTable t = malloc(sizeof(*t)); + memset(t, 0, sizeof(*t)); + + handle->internal = t; + + MwSetDefault(handle); + + MwSetInteger(handle, MwNcolumns, 1); + MwSetInteger(handle, MwNmargin, 0); + MwSetInteger(handle, MwNpadding, 0); + MwSetInteger(handle, MwNhasBorder, 0); + MwSetInteger(handle, MwNinverted, 1); + MwSetInteger(handle, MwNwaitLayout, 0); + + t->layout = 0; + + return 0; +} + +static void destroy(MwWidget handle) { + MwTable t = handle->internal; + + arrfree(t->widgets); + + free(t); +} + +typedef struct intkv { + int key; + int value; +} intkv_t; + +static void put_intkv(intkv_t** kv, int x, int y, int c, int r) { + int k = (x << 16) | y; + int v = (c << 16) | r; + + hmput(*kv, k, v); +} + +static int get_intkv(intkv_t* kv, int x, int y) { + int k = (x << 16) | y; + + return hmget(kv, k); +} + +#define LAYOUT_BEGIN \ + hmfree(kv); \ + hmdefault(kv, 0); \ + n = 0; \ + x = bw; \ + y = bw; \ + for(i = 0; i < arrlen(handle->children); i++) { \ + MwRect rc; \ + MwWidget hc = handle->children[i]; \ + int cs = MwGetInteger(hc, MwNcolumnSpan); \ + int rs = MwGetInteger(hc, MwNrowSpan); \ + int w, h; \ + int cy; \ + int s = MwGetInteger(handle, MwNmargin); \ + int v = n; \ +\ + if(cs == MwDEFAULT) cs = 1; \ + if(rs == MwDEFAULT) rs = 1; \ +\ + w = cs * cw + (cs - 1) * s; \ + h = rs * ch + (rs - 1) * s; \ +\ + MwGetFrame(hc, &rc); \ +\ + if(hc == child || rc.x != x || rc.y != y || rc.width != w || rc.height != h) { + +#define LAYOUT_END(label) \ + } \ +\ + put_intkv(&kv, n % c, n / c, cs, rs); \ +\ + n += cs; \ + repeat_##label :; \ + if((n % c) == 0) { \ + x = bw; \ + y += ch + s; \ + } else { \ + for(; v < n; v++) { \ + x += cw + s; \ + } \ + } \ +\ + for(cy = (n / c); cy >= 0; cy--) { \ + int v = get_intkv(kv, n % c, cy) & 0xffff; \ +\ + if(cy <= (n / c) && (n / c) <= (cy + v - 1)) { \ + v = n; \ + n += get_intkv(kv, n % c, cy) >> 16; \ + goto repeat_##label; \ + } \ + } \ + } + +static void layout_widget(MwWidget handle, MwWidget child) { + int i; + int c = MwGetInteger(handle, MwNcolumns); + int bw = (MwGetInteger(handle, MwNhasBorder) ? MwDefaultBorderWidth(handle) : 0) + MwGetInteger(handle, MwNpadding); + int m = MwGetInteger(handle, MwNmargin); + int n, x, y, cw = 0, ch = 0; + int gcs, grs; + intkv_t* kv = NULL; + + LAYOUT_BEGIN; + LAYOUT_END(1); + + gcs = (n + c - 1) / c; + grs = n % c; + cw = (MwGetInteger(handle, MwNwidth) - bw * 2 - (m * (c - 1))) / c; + ch = (MwGetInteger(handle, MwNheight) - m * (gcs - 1) - bw * 2) / gcs; + + LAYOUT_BEGIN; + MwVaApply(hc, + MwNx, x, + MwNy, y, + MwNwidth, w, + MwNheight, h, + NULL); + LAYOUT_END(2); + + hmfree(kv); +} + +static void layout(MwWidget handle) { + MwTable t = handle->internal; + int i; + + if(arrlen(t->widgets) > 0) { + for(i = 0; i < arrlen(t->widgets); i++) layout_widget(handle, t->widgets[i]); + } else { + for(i = 0; i < arrlen(handle->children[i]); i++) layout_widget(handle, handle->children[i]); + } +} + +static void draw(MwWidget handle) { + MwBoxClass->draw(handle); +} + +static void tick(MwWidget handle) { + MwTable t = handle->internal; + + if(t->layout && !MwGetInteger(handle, MwNwaitLayout)) { + layout(handle); + + t->layout = 0; + } +} + +static void prop_change(MwWidget handle, const char* key) { + if(strcmp(key, MwNcolumns) == 0 || strcmp(key, MwNpadding) == 0 || strcmp(key, MwNmargin) == 0) { + MwTable t = handle->internal; + t->layout = 1; + + MwForceRender(handle); + } +} + +static void resize(MwWidget handle) { + MwTable t = handle->internal; + t->layout = 1; + + MwForceRender(handle); +} + +static void children_update(MwWidget handle, MwWidget child, int new) { + MwTable t = handle->internal; + t->layout = 1; + + if(new) { + arrput(t->widgets, child); + } else { + int i; + + for(i = 0; i < arrlen(t->widgets); i++) { + if(t->widgets[i] == child) { + arrdel(t->widgets, i); + i = -1; + } + } + } + + MwForceRender(handle); +} + +static void children_prop_change(MwWidget handle, MwWidget child, const char* key) { + (void)child; + + if(strcmp(key, MwNcolumnSpan) == 0 || strcmp(key, MwNrowSpan) == 0) { + MwTable t = handle->internal; + t->layout = 1; + + arrput(t->widgets, child); + + MwForceRender(handle); + } +} + +MwClassRec MwTableClassRec = { + wcreate, /* create */ + destroy, /* destroy */ + draw, /* draw */ + NULL, /* click */ + NULL, /* parent_resize */ + prop_change, /* prop_change */ + NULL, /* mouse_move */ + NULL, /* mouse_up */ + NULL, /* mouse_down */ + NULL, /* key */ + NULL, /* execute */ + tick, /* tick */ + resize, /* resize */ + children_update, /* children_update */ + children_prop_change, /* children_prop_change */ + NULL, /* clipboard */ + NULL, /* props_change */ + NULL, + NULL, + NULL}; +MwClass MwTableClass = &MwTableClassRec;