Files
milsko/src/widget/combobox.c
2025-12-15 14:09:30 +09:00

213 lines
5.6 KiB
C

#include <Mw/Milsko.h>
#include "../../external/stb_ds.h"
static int create(MwWidget handle) {
MwComboBox cb = malloc(sizeof(*cb));
cb->list = NULL;
cb->opened = 0;
cb->listbox = NULL;
handle->internal = cb;
MwSetDefault(handle);
MwSetInteger(handle, MwNareaShown, 6);
MwSetInteger(handle, MwNvalue, 0);
return 0;
}
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);
free(handle->internal);
}
static void draw(MwWidget handle) {
MwRect r, rc;
MwLLColor base = MwParseColor(handle, MwGetText(handle, MwNbackground));
MwLLColor text = MwParseColor(handle, MwGetText(handle, MwNforeground));
MwComboBox cb = handle->internal;
r.x = 0;
r.y = 0;
r.width = MwGetInteger(handle, MwNwidth);
r.height = MwGetInteger(handle, MwNheight);
MwDrawWidgetBack(handle, &r, base, 0, 1);
rc = r;
/* draw text */
if(arrlen(cb->list) > MwGetInteger(handle, MwNvalue)) {
MwPoint p;
p.x = MwDefaultBorderWidth(handle) * 2 + 4;
p.y = MwGetInteger(handle, MwNheight) / 2;
MwDrawText(handle, &p, cb->list[MwGetInteger(handle, MwNvalue)], 0, MwALIGNMENT_BEGINNING, text);
}
r = rc;
r.height /= 5;
r.width = r.height * 3;
r.x = MwGetInteger(handle, MwNwidth) - MwDefaultBorderWidth(handle) - r.width - MwDefaultBorderWidth(handle) * 2;
r.y = MwDefaultBorderWidth(handle);
r.height = rc.height;
r.width += MwDefaultBorderWidth(handle) * 2;
MwDrawWidgetBack(handle, &r, base, 0, 0);
r = rc;
r.width = r.height * 3 / 5;
r.height = r.width - MwDefaultBorderWidth(handle) * 2;
r.x = MwGetInteger(handle, MwNwidth) - MwDefaultBorderWidth(handle) - r.width - MwDefaultBorderWidth(handle) * 2;
r.y = (MwGetInteger(handle, MwNheight) - (r.height + MwDefaultBorderWidth(handle) * 2)) / 2;
MwDrawTriangle(handle, &r, base, cb->opened, MwSOUTH);
r.y += r.height;
r.height = MwDefaultBorderWidth(handle) * 2;
MwDrawFrame(handle, &r, base, 0);
MwLLFreeColor(text);
MwLLFreeColor(base);
}
static void listbox_activate(MwWidget handle, void* user, void* client) {
MwComboBox cb = handle->parent->internal;
(void)user;
MwSetInteger(handle->parent, MwNvalue, *(int*)client);
cb->opened = 0;
cb->listbox = NULL;
MwForceRender(handle->parent);
MwDispatchUserHandler(handle->parent, MwNchangedHandler, client);
MwDestroyWidget(handle);
}
static void click(MwWidget handle) {
MwComboBox cb = handle->internal;
cb->opened = cb->opened ? 0 : 1;
if(cb->opened) {
MwPoint p;
int i;
void* packet;
int width = MwGetInteger(handle, MwNwidth);
int ent = MwGetInteger(handle, MwNareaShown);
if(arrlen(cb->list) == 0) {
cb->opened = 0;
return;
}
if(ent > arrlen(cb->list)) ent = arrlen(cb->list);
MwLLSetCursor(handle->lowlevel, &MwCursorArrow, &MwCursorArrowMask);
for(i = 0; i < arrlen(cb->list); i++) {
int l = MwTextWidth(handle, cb->list[i]) + MwDefaultBorderWidth(handle) * 2;
if(l > width) width = l;
}
cb->listbox = MwVaCreateWidget(MwListBoxClass, "listbox", handle, 0, MwGetInteger(handle, MwNheight), width, MwTextHeight(handle, "M") * ent + MwDefaultBorderWidth(handle) * 2 + MwTextHeight(handle, "M") / 4,
MwNsingleClickSelectable, 1,
NULL);
MwLLSetCursor(((MwListBox)cb->listbox->internal)->frame->lowlevel, &MwCursorArrow, &MwCursorArrowMask);
packet = MwListBoxCreatePacket();
for(i = 0; i < arrlen(cb->list); i++) {
int index = MwListBoxPacketInsert(packet, -1);
MwListBoxPacketSet(packet, index, 0, cb->list[i]);
}
MwListBoxInsert(cb->listbox, -1, packet);
MwListBoxDestroyPacket(packet);
MwAddUserHandler(cb->listbox, MwNactivateHandler, listbox_activate, NULL);
p.x = 0;
p.y = MwGetInteger(handle, MwNheight);
MwLLBeginStateChange(cb->listbox->lowlevel);
MwLLDetach(cb->listbox->lowlevel, &p);
MwLLMakeToolWindow(cb->listbox->lowlevel);
MwLLEndStateChange(cb->listbox->lowlevel);
} else {
MwLLSetCursor(handle->lowlevel, &MwCursorDefault, &MwCursorDefaultMask);
if(cb->listbox != NULL) {
MwDestroyWidget(cb->listbox);
cb->listbox = NULL;
}
}
MwForceRender(handle);
}
static void prop_change(MwWidget handle, const char* prop) {
if(strcmp(prop, MwNvalue) == 0) {
MwForceRender(handle);
}
}
static void mwComboBoxAddImpl(MwWidget handle, int index, const char* text) {
MwComboBox cb = handle->internal;
char* t = MwStringDuplicate(text);
if(index == -1) index = arrlen(cb->list);
arrins(cb->list, index, t);
if(index <= MwGetInteger(handle, MwNvalue)) MwForceRender(handle);
}
static const char* mwComboBoxGetImpl(MwWidget handle, int index) {
MwComboBox cb = handle->internal;
return cb->list[index];
}
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);
const char* text = va_arg(va, const char*);
mwComboBoxAddImpl(handle, index, text);
}
if(strcmp(name, "mwComboBoxGet") == 0) {
int index = va_arg(va, int);
*(const char**)out = mwComboBoxGetImpl(handle, index);
}
}
MwClassRec MwComboBoxClassRec = {
create, /* create */
destroy, /* destroy */
draw, /* draw */
click, /* click */
NULL, /* parent_resize */
prop_change, /* prop_change */
NULL, /* mouse_move */
MwForceRender2, /* mouse_up */
MwForceRender2, /* mouse_down */
NULL, /* key */
func_handler, /* execute */
NULL, /* tick */
NULL, /* resize */
NULL, /* children_update */
NULL, /* children_prop_change */
NULL,
NULL,
NULL,
NULL,
NULL};
MwClass MwComboBoxClass = &MwComboBoxClassRec;