2026-05-07 18:30:24 +09:00
|
|
|
#include <Mw/Milsko.h>
|
|
|
|
|
|
|
|
|
|
#include "../../external/stb_ds.h"
|
|
|
|
|
|
|
|
|
|
static void resize(MwWidget handle);
|
|
|
|
|
|
|
|
|
|
static int wcreate(MwWidget handle) {
|
|
|
|
|
MwTab t = malloc(sizeof(*t));
|
|
|
|
|
memset(t, 0, sizeof(*t));
|
|
|
|
|
|
|
|
|
|
handle->internal = t;
|
|
|
|
|
|
|
|
|
|
MwSetDefault(handle);
|
|
|
|
|
|
|
|
|
|
MwSetInteger(handle, MwNvalue, -1);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void destroy(MwWidget handle) {
|
|
|
|
|
MwTab t = handle->internal;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < arrlen(t->names); i++) {
|
|
|
|
|
free(t->names[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arrfree(t->names);
|
|
|
|
|
arrfree(t->frames);
|
|
|
|
|
|
|
|
|
|
free(handle->internal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int tab_width(MwWidget handle, const char* text) {
|
|
|
|
|
return MwTextWidth(handle, NULL, text) + 20;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int tab_height(MwWidget handle) {
|
|
|
|
|
return MwTextHeight(handle, NULL, "M") + 10;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-23 03:16:33 +09:00
|
|
|
static int icon_width(MwWidget handle, const char* text) {
|
|
|
|
|
MwTab t = handle->internal;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < arrlen(t->names); i++) {
|
|
|
|
|
if(strcmp(t->names[i], text) == 0) {
|
|
|
|
|
MwLLPixmap px = MwGetVoid(t->frames[i], MwNiconPixmap);
|
|
|
|
|
|
|
|
|
|
if(px == NULL) return 0;
|
|
|
|
|
|
|
|
|
|
return px->common.width * tab_height(handle) / px->common.height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 18:30:24 +09:00
|
|
|
static void draw(MwWidget handle) {
|
|
|
|
|
MwTab t = handle->internal;
|
|
|
|
|
MwLLColor c = MwParseColor(handle, MwGetText(handle, MwNbackground));
|
|
|
|
|
MwLLColor ct = MwParseColor(handle, MwGetText(handle, MwNforeground));
|
|
|
|
|
MwRect r, r2;
|
|
|
|
|
int h = tab_height(handle);
|
|
|
|
|
int i;
|
|
|
|
|
int n;
|
|
|
|
|
int x = 0;
|
|
|
|
|
|
|
|
|
|
r.x = 0;
|
|
|
|
|
r.y = 0;
|
|
|
|
|
r.width = MwGetInteger(handle, MwNwidth);
|
|
|
|
|
r.height = MwGetInteger(handle, MwNheight);
|
|
|
|
|
|
|
|
|
|
MwDrawRect(handle, &r, c);
|
|
|
|
|
|
|
|
|
|
for(n = 0; n < 2; n++) {
|
|
|
|
|
x = 0;
|
|
|
|
|
for(i = 0; i < arrlen(t->names); i++) {
|
|
|
|
|
int render = 0;
|
2026-05-23 03:16:33 +09:00
|
|
|
int iw = icon_width(handle, t->names[i]);
|
2026-05-07 18:30:24 +09:00
|
|
|
|
|
|
|
|
if(i == MwGetInteger(handle, MwNvalue) && n == 1) {
|
|
|
|
|
r.y += h;
|
|
|
|
|
r.height -= h;
|
|
|
|
|
MwDrawFrame(handle, &r, c, 0);
|
|
|
|
|
|
|
|
|
|
render = 1;
|
|
|
|
|
} else if(n == 0) {
|
|
|
|
|
render = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r2.x = x;
|
|
|
|
|
r2.y = 0;
|
2026-05-23 03:16:33 +09:00
|
|
|
r2.width = tab_width(handle, t->names[i]) + iw;
|
2026-05-07 18:30:24 +09:00
|
|
|
r2.height = tab_height(handle) + MwDefaultBorderWidth(handle);
|
|
|
|
|
|
|
|
|
|
x += r2.width;
|
|
|
|
|
|
2026-05-23 03:16:33 +09:00
|
|
|
if(iw > 0) {
|
|
|
|
|
MwLLPixmap px = MwGetVoid(t->frames[i], MwNiconPixmap);
|
|
|
|
|
MwRect rp;
|
|
|
|
|
|
|
|
|
|
rp = r2;
|
|
|
|
|
|
|
|
|
|
rp.x += 5;
|
|
|
|
|
rp.y += 5;
|
|
|
|
|
|
|
|
|
|
rp.width = iw - 10;
|
|
|
|
|
rp.height = rp.height * (iw - 10) / iw;
|
|
|
|
|
|
|
|
|
|
MwLLDrawPixmap(handle->lowlevel, &rp, px);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 18:30:24 +09:00
|
|
|
if(render) {
|
|
|
|
|
MwPoint p;
|
|
|
|
|
|
2026-05-07 18:40:14 +09:00
|
|
|
if(n == 1 && i > 0) {
|
2026-05-07 18:30:24 +09:00
|
|
|
MwRect r3 = r2;
|
|
|
|
|
|
|
|
|
|
r3.y = tab_height(handle);
|
|
|
|
|
r3.width = r3.height = MwDefaultBorderWidth(handle);
|
|
|
|
|
|
|
|
|
|
MwLLClip(handle->lowlevel, &r3);
|
|
|
|
|
|
|
|
|
|
r3.x -= r3.width;
|
|
|
|
|
r3.y -= r3.height;
|
|
|
|
|
r3.width *= 2;
|
|
|
|
|
r3.height *= 2;
|
|
|
|
|
MwDrawFrame(handle, &r3, c, 1);
|
|
|
|
|
|
2026-05-08 00:35:34 +09:00
|
|
|
MwLLClip(handle->lowlevel, NULL);
|
2026-05-12 09:49:06 +09:00
|
|
|
} else if(n == 1 && i == 0) {
|
2026-05-08 00:35:34 +09:00
|
|
|
MwRect r3 = r2;
|
|
|
|
|
|
|
|
|
|
r3.y = tab_height(handle);
|
|
|
|
|
r3.width = r3.height = MwDefaultBorderWidth(handle);
|
|
|
|
|
|
|
|
|
|
MwLLClip(handle->lowlevel, &r3);
|
|
|
|
|
|
|
|
|
|
r3.x -= r3.width;
|
|
|
|
|
r3.y -= r3.height;
|
|
|
|
|
r3.width *= 2;
|
|
|
|
|
r3.height *= 3;
|
|
|
|
|
MwDrawFrame(handle, &r3, c, 1);
|
|
|
|
|
|
2026-05-07 18:30:24 +09:00
|
|
|
MwLLClip(handle->lowlevel, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 18:40:14 +09:00
|
|
|
if(n == 1 && i < (arrlen(t->names) - 1)) {
|
2026-05-07 18:30:24 +09:00
|
|
|
MwRect r3 = r2;
|
|
|
|
|
|
|
|
|
|
r3.x += r2.width - MwDefaultBorderWidth(handle);
|
|
|
|
|
r3.y = tab_height(handle);
|
|
|
|
|
r3.width = r3.height = MwDefaultBorderWidth(handle);
|
|
|
|
|
|
|
|
|
|
MwLLClip(handle->lowlevel, &r3);
|
|
|
|
|
|
|
|
|
|
r3.y -= r3.height;
|
|
|
|
|
r3.width *= 2;
|
|
|
|
|
r3.height *= 2;
|
|
|
|
|
MwDrawFrame(handle, &r3, c, 1);
|
|
|
|
|
|
|
|
|
|
MwLLClip(handle->lowlevel, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(n == 1) {
|
|
|
|
|
MwRect r3 = r2;
|
|
|
|
|
|
|
|
|
|
r3.height -= MwDefaultBorderWidth(handle);
|
|
|
|
|
|
|
|
|
|
MwLLClip(handle->lowlevel, &r3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MwDrawFrame(handle, &r2, c, 0);
|
|
|
|
|
|
|
|
|
|
MwLLClip(handle->lowlevel, NULL);
|
|
|
|
|
|
|
|
|
|
if(n == 1) {
|
|
|
|
|
MwRect r3 = r2;
|
|
|
|
|
|
|
|
|
|
r3.y += r3.height;
|
|
|
|
|
r3.height = MwDefaultBorderWidth(handle);
|
|
|
|
|
|
|
|
|
|
MwDrawRect(handle, &r3, c);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-23 03:16:33 +09:00
|
|
|
p.x = r2.x + (r2.width + iw / 2) / 2;
|
2026-05-07 18:30:24 +09:00
|
|
|
p.y = r2.y + r2.height / 2;
|
|
|
|
|
MwDrawText(handle, NULL, &p, t->names[i], MwALIGNMENT_CENTER, ct);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MwLLFreeColor(ct);
|
|
|
|
|
MwLLFreeColor(c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void show_frame(MwWidget handle) {
|
|
|
|
|
resize(handle);
|
|
|
|
|
|
|
|
|
|
MwForceRender(handle);
|
|
|
|
|
|
|
|
|
|
MwDispatchUserHandler(handle, MwNchangedHandler, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void click(MwWidget handle) {
|
|
|
|
|
MwTab t = handle->internal;
|
|
|
|
|
int i;
|
|
|
|
|
int x = 0;
|
|
|
|
|
int h = tab_height(handle);
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < arrlen(t->names); i++) {
|
2026-05-23 03:16:33 +09:00
|
|
|
int w = tab_width(handle, t->names[i]);
|
|
|
|
|
int iw = icon_width(handle, t->names[i]);
|
|
|
|
|
|
|
|
|
|
w += iw;
|
2026-05-07 18:30:24 +09:00
|
|
|
|
|
|
|
|
if(MwGetInteger(handle, MwNvalue) != i && x <= handle->mouse_point.x && handle->mouse_point.x <= (x + w) && 0 <= handle->mouse_point.y && handle->mouse_point.y <= h) {
|
|
|
|
|
MwSetInteger(handle, MwNvalue, i);
|
|
|
|
|
show_frame(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x += w;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-23 03:16:33 +09:00
|
|
|
static void prop_change(MwWidget handle, const char* key) {
|
|
|
|
|
if(strcmp(key, MwNvalue) == 0) {
|
|
|
|
|
show_frame(handle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 18:30:24 +09:00
|
|
|
static MwWidget mwTabAddImpl(MwWidget handle, const char* name) {
|
|
|
|
|
MwTab t = handle->internal;
|
2026-05-22 09:19:19 +09:00
|
|
|
MwWidget f = MwCreateWidget(MwFrameClass, "tabframe", handle, MwDefaultBorderWidth(handle), tab_height(handle) + MwDefaultBorderWidth(handle), MwGetInteger(handle, MwNwidth) - MwDefaultBorderWidth(handle) * 2, MwGetInteger(handle, MwNheight) - tab_height(handle) - MwDefaultBorderWidth(handle) * 2);
|
2026-05-07 18:30:24 +09:00
|
|
|
char* n = MwStringDuplicate(name);
|
|
|
|
|
|
|
|
|
|
arrput(t->frames, f);
|
|
|
|
|
arrput(t->names, n);
|
|
|
|
|
|
|
|
|
|
MwForceRender(handle);
|
|
|
|
|
|
|
|
|
|
if(MwGetInteger(handle, MwNvalue) == -1) {
|
|
|
|
|
MwSetInteger(handle, MwNvalue, arrlen(t->frames) - 1);
|
|
|
|
|
show_frame(handle);
|
2026-05-21 18:21:50 -07:00
|
|
|
} else {
|
2026-05-22 09:19:19 +09:00
|
|
|
MwLLShow(f->lowlevel, 0);
|
|
|
|
|
}
|
2026-05-07 18:30:24 +09:00
|
|
|
|
|
|
|
|
return f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static MwWidget mwTabGetFrameImpl(MwWidget handle, const char* name) {
|
|
|
|
|
MwTab t = handle->internal;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < arrlen(t->names); i++) {
|
|
|
|
|
if(strcmp(t->names[i], name) == 0) return t->frames[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-23 03:16:33 +09:00
|
|
|
static void mwTabFocusImpl(MwWidget handle, const char* name) {
|
|
|
|
|
MwTab t = handle->internal;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < arrlen(t->names); i++) {
|
|
|
|
|
if(strcmp(t->names[i], name) == 0) {
|
|
|
|
|
MwSetInteger(handle, MwNvalue, i);
|
|
|
|
|
show_frame(handle);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 18:30:24 +09:00
|
|
|
static void func_handler(MwWidget handle, const char* name, void* out, va_list va) {
|
|
|
|
|
if(strcmp(name, "mwTabAdd") == 0) {
|
|
|
|
|
const char* name = va_arg(va, const char*);
|
|
|
|
|
|
|
|
|
|
*(MwWidget*)out = mwTabAddImpl(handle, name);
|
|
|
|
|
} else if(strcmp(name, "mwTabGetFrame") == 0) {
|
|
|
|
|
const char* name = va_arg(va, const char*);
|
|
|
|
|
|
|
|
|
|
*(MwWidget*)out = mwTabGetFrameImpl(handle, name);
|
2026-05-23 03:16:33 +09:00
|
|
|
} else if(strcmp(name, "mwTabFocus") == 0) {
|
|
|
|
|
const char* name = va_arg(va, const char*);
|
|
|
|
|
|
|
|
|
|
mwTabFocusImpl(handle, name);
|
2026-05-07 18:30:24 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void resize(MwWidget handle) {
|
|
|
|
|
MwTab t = handle->internal;
|
|
|
|
|
int v = MwGetInteger(handle, MwNvalue);
|
2026-05-07 22:59:54 +09:00
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < arrlen(t->frames); i++) {
|
|
|
|
|
int y = MwGetInteger(handle, MwNheight);
|
2026-05-07 18:30:24 +09:00
|
|
|
|
2026-05-22 09:19:19 +09:00
|
|
|
MwLLShow(t->frames[i]->lowlevel, i == v);
|
2026-05-08 00:35:34 +09:00
|
|
|
if(i == v) y = tab_height(handle) + MwDefaultBorderWidth(handle);
|
2026-05-07 18:30:24 +09:00
|
|
|
|
2026-05-07 22:59:54 +09:00
|
|
|
MwVaApply(t->frames[i],
|
|
|
|
|
MwNx, MwDefaultBorderWidth(handle),
|
|
|
|
|
MwNy, y,
|
|
|
|
|
MwNwidth, MwGetInteger(handle, MwNwidth) - MwDefaultBorderWidth(handle) * 2,
|
|
|
|
|
MwNheight, MwGetInteger(handle, MwNheight) - tab_height(handle) - MwDefaultBorderWidth(handle) * 2,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
2026-05-07 18:30:24 +09:00
|
|
|
}
|
|
|
|
|
|
2026-05-23 03:16:33 +09:00
|
|
|
static void children_update(MwWidget handle, MwWidget child, int new_child) {
|
|
|
|
|
MwTab t = handle->internal;
|
|
|
|
|
int v = MwGetInteger(handle, MwNvalue);
|
|
|
|
|
int c = v;
|
|
|
|
|
int n;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if(new_child) return;
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < arrlen(t->frames); i++) {
|
|
|
|
|
if(t->frames[i] == child) {
|
|
|
|
|
n = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(t->names[n]);
|
|
|
|
|
|
|
|
|
|
arrdel(t->frames, n);
|
|
|
|
|
arrdel(t->names, n);
|
|
|
|
|
|
|
|
|
|
if(n <= v) {
|
|
|
|
|
if(v > 0) v--;
|
|
|
|
|
|
|
|
|
|
MwSetInteger(handle, MwNvalue, v);
|
|
|
|
|
show_frame(handle);
|
|
|
|
|
} else {
|
|
|
|
|
MwSetInteger(handle, MwNvalue, v);
|
|
|
|
|
show_frame(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MwForceRender(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void children_prop_change(MwWidget handle, MwWidget child, const char* key) {
|
|
|
|
|
if(strcmp(key, MwNiconPixmap) == 0) {
|
|
|
|
|
MwForceRender(handle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 18:30:24 +09:00
|
|
|
MwClassRec MwTabClassRec = {
|
2026-05-23 03:16:33 +09:00
|
|
|
wcreate, /* create */
|
|
|
|
|
destroy, /* destroy */
|
|
|
|
|
draw, /* draw */
|
|
|
|
|
click, /* click */
|
|
|
|
|
NULL, /* parent_resize */
|
|
|
|
|
prop_change, /* prop_change */
|
|
|
|
|
NULL, /* mouse_move */
|
|
|
|
|
NULL, /* mouse_up */
|
|
|
|
|
NULL, /* mouse_down */
|
|
|
|
|
NULL, /* key */
|
|
|
|
|
func_handler, /* execute */
|
|
|
|
|
NULL, /* tick */
|
|
|
|
|
resize, /* resize */
|
|
|
|
|
children_update, /* children_update */
|
|
|
|
|
children_prop_change, /* children_prop_change */
|
|
|
|
|
NULL, /* clipboard */
|
|
|
|
|
NULL, /* props_change */
|
2026-05-07 18:30:24 +09:00
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL};
|
|
|
|
|
MwClass MwTabClass = &MwTabClassRec;
|