From 120eb5baaf5124243203b9a8f84481bd71a3eab2 Mon Sep 17 00:00:00 2001 From: Nishi Date: Mon, 14 Sep 2026 01:09:57 +0900 Subject: [PATCH] bar chart --- examples/basic/periodic.c | 5 + include/Mw/Constants.h | 4 + include/Mw/Draw.h | 8 ++ include/Mw/StringDefs.h | 1 + include/Mw/TypeDefs.h | 12 +++ include/Mw/Widget/Chart.h | 33 ++++++ include/Mw/Widget/ComboBox.h | 17 +++ milsko.xml | 51 ++++++++- src/core.c | 2 +- src/draw.c | 61 +++++++++-- src/widget/chart.c | 203 +++++++++++++++++++++++++++++++---- src/widget/combobox.c | 50 ++++++--- src/widget/treeview.c | 1 + src/widget/window.c | 1 + 14 files changed, 405 insertions(+), 44 deletions(-) diff --git a/examples/basic/periodic.c b/examples/basic/periodic.c index 5b65265..db833c6 100644 --- a/examples/basic/periodic.c +++ b/examples/basic/periodic.c @@ -270,6 +270,11 @@ int main() { f = frame("Chart", -PaddingContent, -PaddingContent, MwChartClass, NULL); + w = child(f); + MwChartAdd(w, -1, "A", -100, NULL); + MwChartAdd(w, -1, "B", 100, NULL); + MwChartAdd(w, -1, "C", 200, NULL); + MwChartAdd(w, -1, "D", 300, NULL); f = frame("ComboBox", -PaddingContent, 24, MwComboBoxClass, NULL); w = child(f); diff --git a/include/Mw/Constants.h b/include/Mw/Constants.h index 15853ff..ad29792 100644 --- a/include/Mw/Constants.h +++ b/include/Mw/Constants.h @@ -113,6 +113,10 @@ enum MwCLIPBOARD { MwCLIPBOARD_PRIMARY, }; +enum MwCHART { + MwCHART_BAR = 0 +}; + enum MwMOUSE { MwMOUSE_LEFT = 1, MwMOUSE_MIDDLE, diff --git a/include/Mw/Draw.h b/include/Mw/Draw.h index 542aac5..e3c6991 100644 --- a/include/Mw/Draw.h +++ b/include/Mw/Draw.h @@ -60,6 +60,14 @@ MWDECL MwLLColor MWAPI MwLightenColor(MwWidget handle, MwLLColor color, int r, i */ MWDECL void MWAPI MwDrawRect(MwWidget handle, MwRect* rect, MwLLColor color); +/*! + * @brief Draws a rectangle + * @param handle Widget + * @param rect Rectangle area + * @param color Color + */ +MWDECL void MWAPI MwDrawRectLine(MwWidget handle, MwRect* rect, MwLLColor color); + /*! * @brief Draws a filled rectangle that fades to a darker color * @param handle Widget diff --git a/include/Mw/StringDefs.h b/include/Mw/StringDefs.h index 39730db..d820ea4 100644 --- a/include/Mw/StringDefs.h +++ b/include/Mw/StringDefs.h @@ -57,6 +57,7 @@ #define MwNhour "Ihour" #define MwNminute "Iminute" #define MwNsecond "Isecond" +#define MwNtype "Itype" #define MwNtitle "Stitle" #define MwNtext "Stext" diff --git a/include/Mw/TypeDefs.h b/include/Mw/TypeDefs.h index a9bc3cf..0be60e8 100644 --- a/include/Mw/TypeDefs.h +++ b/include/Mw/TypeDefs.h @@ -30,6 +30,8 @@ typedef struct _MwBox* MwBox; typedef struct _MwSubWindow* MwSubWindow; typedef struct _MwTab* MwTab; typedef struct _MwTable* MwTable; +typedef struct _MwChartEntry MwChartEntry; +typedef struct _MwChart* MwChart; typedef struct _MwMouse MwMouse; typedef struct _MwTTFInfo* MwTTFInfo; #ifdef _MILSKO @@ -247,6 +249,16 @@ struct _MwTab { char** names; }; +struct _MwChartEntry { + char* name; + char* color; + double value; +}; + +struct _MwChart { + MwChartEntry* entries; +}; + struct _MwMouse { MwPoint point; int button; diff --git a/include/Mw/Widget/Chart.h b/include/Mw/Widget/Chart.h index 6ab404b..d0957a2 100644 --- a/include/Mw/Widget/Chart.h +++ b/include/Mw/Widget/Chart.h @@ -17,6 +17,39 @@ extern "C" { */ MWDECL MwClass MwChartClass; +/*! + * @brief Adds the entry to the chart + * @param handle Widget + * @param index Index + * @param text Text + * @param value Value + * @return Index + */ +MwInline int MwChartAdd(MwWidget handle, int index, const char* text, double value, const char* color) { + int out; + + MwVaWidgetExecute(handle, "mwChartAdd", (void*)&out, index, text, value, color); + + return out; +} + +/*! + * @brief Deletes item from the chart + * @param handle Widget + * @param index Index + */ +MwInline void MwChartDelete(MwWidget handle, int index) { + MwVaWidgetExecute(handle, "mwChartDelete", NULL, index); +} + +/*! + * @brief Resets the chart + * @param handle Widget + */ +MwInline void MwChartReset(MwWidget handle) { + MwVaWidgetExecute(handle, "mwChartReset", NULL); +} + #ifdef __cplusplus } #endif diff --git a/include/Mw/Widget/ComboBox.h b/include/Mw/Widget/ComboBox.h index 7b2dc2f..fd5c343 100644 --- a/include/Mw/Widget/ComboBox.h +++ b/include/Mw/Widget/ComboBox.h @@ -41,6 +41,23 @@ MwInline const char* MwComboBoxGet(MwWidget handle, int index) { return text; } +/*! + * @brief Deletes the entry from combobox + * @param handle Widget + * @param index Index + */ +MwInline void MwComboBoxDelete(MwWidget handle, int index) { + MwVaWidgetExecute(handle, "mwComboBoxDelete", NULL, index); +} + +/*! + * @brief Resets the combobox + * @param handle Widget + */ +MwInline void MwComboBoxReset(MwWidget handle) { + MwVaWidgetExecute(handle, "mwComboBoxReset", NULL); +} + #ifdef __cplusplus } #endif diff --git a/milsko.xml b/milsko.xml index 573b84d..30efce1 100644 --- a/milsko.xml +++ b/milsko.xml @@ -1,10 +1,11 @@ @@ -228,6 +230,9 @@ 0 + + 0 + 1 @@ -649,6 +654,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -879,9 +907,26 @@ - - + + + + + + + + + + + + + + + + + + + diff --git a/src/core.c b/src/core.c index 481fa34..2982c1e 100644 --- a/src/core.c +++ b/src/core.c @@ -5,7 +5,7 @@ #ifdef USE_COCOA #define PERIODIC #endif -// #define USE_CLASSIC_THEME +#define USE_CLASSIC_THEME #define RESIZE_ON_TICK #define DRAW(handle) \ diff --git a/src/draw.c b/src/draw.c index bbd0264..5cbfd22 100644 --- a/src/draw.c +++ b/src/draw.c @@ -101,22 +101,67 @@ MwLLColor MwLightenColor(MwWidget handle, MwLLColor color, int r, int g, int b) void MwDrawRect(MwWidget handle, MwRect* rect, MwLLColor color) { MwPoint p[4]; + MwRect r = *rect; - p[0].x = rect->x; - p[0].y = rect->y; + if(r.width < 0) { + r.width *= -1; + r.x -= r.width; + } - p[1].x = rect->x + rect->width; - p[1].y = rect->y; + if(r.height < 0) { + r.height *= -1; + r.y -= r.height; + } - p[2].x = rect->x + rect->width; - p[2].y = rect->y + rect->height; + p[0].x = r.x; + p[0].y = r.y; - p[3].x = rect->x; - p[3].y = rect->y + rect->height; + p[1].x = r.x + r.width; + p[1].y = r.y; + + p[2].x = r.x + r.width; + p[2].y = r.y + r.height; + + p[3].x = r.x; + p[3].y = r.y + r.height; MwLLPolygon(handle->lowlevel, p, 4, color); } +void MwDrawRectLine(MwWidget handle, MwRect* rect, MwLLColor color) { + MwPoint p[5]; + MwRect r = *rect; + + if(r.width < 0) { + r.width *= -1; + r.x -= r.width; + } + + if(r.height < 0) { + r.height *= -1; + r.y -= r.height; + } + + p[0].x = r.x; + p[0].y = r.y; + + p[1].x = r.x + r.width; + p[1].y = r.y; + + p[2].x = r.x + r.width; + p[2].y = r.y + r.height; + + p[3].x = r.x; + p[3].y = r.y + r.height; + + p[4] = p[0]; + + MwLLLine(handle->lowlevel, &p[0], color); + MwLLLine(handle->lowlevel, &p[1], color); + MwLLLine(handle->lowlevel, &p[2], color); + MwLLLine(handle->lowlevel, &p[3], color); +} + void MwDrawRectFading(MwWidget handle, MwRect* rect, MwLLColor color) { MwLLPixmap pixmap; int y; diff --git a/src/widget/chart.c b/src/widget/chart.c index 46f248e..b014513 100644 --- a/src/widget/chart.c +++ b/src/widget/chart.c @@ -1,12 +1,62 @@ #include +#include "../../external/stb_ds.h" + +static const char* palette0[] = { + "#800", + "#080", + "#880", + "#008", + "#808", + "#088", + "#888", + "#f00", + "#0f0", + "#ff0", + "#00f", + "#f0f", + "#0ff", + "#fff", + NULL}; + static int wcreate(MwWidget handle) { + MwChart c = malloc(sizeof(*c)); + memset(c, 0, sizeof(*c)); + + handle->internal = c; + + MwSetDefault(handle); + return 0; } +static void destroy(MwWidget handle) { + MwChartReset(handle); + free(handle->internal); +} + static void draw(MwWidget handle) { - MwLLColor base = MwParseColor(handle, MwGetText(handle, MwNbackground)); - MwRect r; + MwLLColor base = MwParseColor(handle, MwGetText(handle, MwNbackground)); + MwLLColor border = MwParseColor(handle, MwGetText(handle, MwNforeground)); + MwRect r; + MwChart c = handle->internal; + int i; + const char** colors = palette0; + int n; + int gap; + int width; + MwRect node; + double min = 0xffffffff; + double max = -0xffffffff; + int space = MwTextHeight(handle, NULL, "M"); + MwPoint p[2]; + + for(n = 0; colors[n] != NULL; n++); + + for(i = 0; i < arrlen(c->entries); i++) { + if(min > c->entries[i].value) min = c->entries[i].value; + if(max < c->entries[i].value) max = c->entries[i].value; + } r.x = 0; r.y = 0; @@ -14,31 +64,144 @@ static void draw(MwWidget handle) { r.height = MwGetInteger(handle, MwNheight); MwDrawRect(handle, &r, base); + if(min > 0) { + min = 0; + } else if(min < 0) { + min -= r.height / 10; + } + + node.x = 16; + node.y = 0; + + width = (r.width - node.x) * 2 / (arrlen(c->entries) * 3 + 1); + gap = width / 2; + + p[0].x = node.x; + p[0].y = r.height - space; + + p[1].x = node.x + (width + gap) * arrlen(c->entries) + gap; + p[1].y = p[0].y; + MwLLLine(handle->lowlevel, &p[0], border); + + p[0].y = r.height - space - abs(min / (max - min) * (r.height - space)); + p[1].y = p[0].y; + MwLLLine(handle->lowlevel, &p[0], border); + MwDrawText(handle, NULL, p, "0", MwALIGNMENT_END, border); + + p[0].x = node.x; + p[0].y = 0; + + p[1].x = node.x; + p[1].y = r.height - space; + MwLLLine(handle->lowlevel, &p[0], border); + + node.width = width; + for(i = 0; i < arrlen(c->entries); i++) { + MwLLColor color = MwParseColor(handle, c->entries[i].color == NULL ? colors[i % n] : c->entries[i].color); + + node.x += gap; + + node.height = c->entries[i].value / (max - min) * (r.height - space); + node.y = r.height - space - node.height - abs(min / (max - min) * (r.height - space)); + + MwDrawRect(handle, &node, color); + MwDrawRectLine(handle, &node, border); + + p[0].x = node.x + node.width / 2; + p[0].y = r.height - space / 2; + + MwDrawText(handle, NULL, p, c->entries[i].name, MwALIGNMENT_CENTER, border); + + node.x += node.width; + + MwLLFreeColor(color); + } + + MwLLFreeColor(border); MwLLFreeColor(base); } static void prop_change(MwWidget handle, const char* key) { - if(strcmp(key, MwNhour) == 0 || strcmp(key, MwNminute) == 0 || strcmp(key, MwNsecond) == 0) MwForceRender(handle); + if(strcmp(key, MwNtype) == 0) MwForceRender(handle); +} + +static int mwChartAddImpl(MwWidget handle, int index, const char* text, double value, const char* color) { + MwChart c = handle->internal; + MwChartEntry e; + + if(index == -1) index = arrlen(c->entries); + + e.name = MwStringDuplicate(text); + e.color = color == NULL ? NULL : MwStringDuplicate(color); + e.value = value; + + arrins(c->entries, index, e); + + MwForceRender(handle); + + return index; +} + +static void mwChartDeleteImpl(MwWidget handle, int index) { + MwChart c = handle->internal; + + if(index == -1) index = arrlen(c->entries) - 1; + + free(c->entries[index].name); + if(c->entries[index].color != NULL) free(c->entries[index].color); + arrdel(c->entries, index); + + MwForceRender(handle); +} + +static void mwChartResetImpl(MwWidget handle) { + MwChart c = handle->internal; + int i; + + for(i = 0; i < arrlen(c->entries); i++) { + free(c->entries[i].name); + if(c->entries[i].color != NULL) free(c->entries[i].color); + } + arrfree(c->entries); + + MwForceRender(handle); +} + +static void func_handler(MwWidget handle, const char* name, void* out, va_list va) { + if(strcmp(name, "mwChartAdd") == 0) { + int index = va_arg(va, int); + const char* text = va_arg(va, const char*); + double value = va_arg(va, double); + const char* color = va_arg(va, const char*); + *(int*)out = mwChartAddImpl(handle, index, text, value, color); + } + if(strcmp(name, "mwChartDelete") == 0) { + int index = va_arg(va, int); + mwChartDeleteImpl(handle, index); + } + if(strcmp(name, "mwChartReset") == 0) { + mwChartResetImpl(handle); + } } MwClassRec MwChartClassRec = { - wcreate, /* create */ - NULL, /* 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 */ - NULL, /* tick */ - NULL, /* resize */ - NULL, /* children_update */ - NULL, /* children_prop_change */ - NULL, /* clipboard */ - NULL, /* props_change */ + 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 */ + func_handler, /* execute */ + NULL, /* tick */ + MwForceRender, /* resize */ + NULL, /* children_update */ + NULL, /* children_prop_change */ + NULL, /* clipboard */ + NULL, /* props_change */ NULL, NULL, NULL}; diff --git a/src/widget/combobox.c b/src/widget/combobox.c index e53647d..5502a07 100644 --- a/src/widget/combobox.c +++ b/src/widget/combobox.c @@ -20,12 +20,8 @@ static int wcreate(MwWidget handle) { static void destroy(MwWidget handle) { MwComboBox cb = handle->internal; - int i; - for(i = 0; i < arrlen(cb->list); i++) { - free(cb->list[i]); - } - arrfree(cb->list); + MwComboBoxReset(handle); free(handle->internal); } @@ -145,6 +141,13 @@ static void prop_change(MwWidget handle, const char* prop) { } } +static void parent_resize(MwWidget handle) { + MwComboBox cb = handle->internal; + if(cb->opened) { + click(handle); + } +} + static void mwComboBoxAddImpl(MwWidget handle, int index, const char* text) { MwComboBox cb = handle->internal; char* t = MwStringDuplicate(text); @@ -153,7 +156,7 @@ static void mwComboBoxAddImpl(MwWidget handle, int index, const char* text) { arrins(cb->list, index, t); - if(index <= MwGetInteger(handle, MwNvalue)) MwForceRender(handle); + MwForceRender(handle); } static const char* mwComboBoxGetImpl(MwWidget handle, int index) { @@ -162,6 +165,29 @@ static const char* mwComboBoxGetImpl(MwWidget handle, int index) { return cb->list[index]; } +static void mwComboBoxDeleteImpl(MwWidget handle, int index) { + MwComboBox cb = handle->internal; + int n = MwGetInteger(handle, MwNvalue); + + free(cb->list[index]); + arrdel(cb->list, index); + + if(index < n && n > 0) { + MwSetInteger(handle, MwNvalue, n - 1); + } + + MwForceRender(handle); +} + +static void mwComboBoxResetImpl(MwWidget handle) { + MwComboBox cb = handle->internal; + + arrfree(cb->list); + MwSetInteger(handle, MwNvalue, 0); + + MwForceRender(handle); +} + static void func_handler(MwWidget handle, const char* name, void* out, va_list va) { if(strcmp(name, "mwComboBoxAdd") == 0) { int index = va_arg(va, int); @@ -172,12 +198,12 @@ static void func_handler(MwWidget handle, const char* name, void* out, va_list v int index = va_arg(va, int); *(const char**)out = mwComboBoxGetImpl(handle, index); } -} - -static void parent_resize(MwWidget handle) { - MwComboBox cb = handle->internal; - if(cb->opened) { - click(handle); + if(strcmp(name, "mwComboBoxDelete") == 0) { + int index = va_arg(va, int); + mwComboBoxDeleteImpl(handle, index); + } + if(strcmp(name, "mwComboBoxReset") == 0) { + mwComboBoxResetImpl(handle); } } diff --git a/src/widget/treeview.c b/src/widget/treeview.c index 3fba309..d07d355 100644 --- a/src/widget/treeview.c +++ b/src/widget/treeview.c @@ -317,6 +317,7 @@ static int wcreate(MwWidget handle) { } static void destroy(MwWidget handle) { + MwTreeViewReset(handle); free(handle->internal); } diff --git a/src/widget/window.c b/src/widget/window.c index 3d0afe2..9ea16a3 100644 --- a/src/widget/window.c +++ b/src/widget/window.c @@ -51,6 +51,7 @@ static void func_handler(MwWidget handle, const char* name, void* out, va_list v mwWindowShouldCloseImpl(handle, out); } } + MwClassRec MwWindowClassRec = { wcreate, /* create */ NULL, /* destroy */