Control arrays added.
This commit is contained in:
parent
657b44eb25
commit
dd68a19b5b
14 changed files with 1106 additions and 108 deletions
|
|
@ -193,6 +193,7 @@
|
|||
#define OP_CREATE_CTRL 0x8B // pop name, pop typeName, pop formRef, push controlRef
|
||||
#define OP_FIND_CTRL 0x8C // pop ctrlName, pop formRef, push controlRef
|
||||
#define OP_CTRL_REF 0x8D // [uint16 nameConstIdx] push named control on current form
|
||||
#define OP_FIND_CTRL_IDX 0x8E // pop index, pop ctrlName, pop formRef, push ctrlRef
|
||||
|
||||
// ============================================================
|
||||
// Array / misc
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ static const BuiltinFuncT builtinFuncs[] = {
|
|||
static void addPredefConst(BasParserT *p, const char *name, int32_t val);
|
||||
static void addPredefConsts(BasParserT *p);
|
||||
static void advance(BasParserT *p);
|
||||
static bool checkCtrlArrayAccess(BasParserT *p);
|
||||
static bool check(BasParserT *p, BasTokenTypeE type);
|
||||
static bool checkKeyword(BasParserT *p, const char *kw);
|
||||
static bool checkKeywordText(const char *text, const char *kw);
|
||||
|
|
@ -247,6 +248,45 @@ static void addPredefConsts(BasParserT *p) {
|
|||
}
|
||||
|
||||
|
||||
// Check if current token '(' is followed by a matching ')' then '.'.
|
||||
// This disambiguates control array access Name(idx).Property from
|
||||
// function calls Name(args). Saves and restores lexer state.
|
||||
// Must be called when current token is TOK_LPAREN.
|
||||
static bool checkCtrlArrayAccess(BasParserT *p) {
|
||||
BasLexerT savedLex = p->lex;
|
||||
bool savedErr = p->hasError;
|
||||
|
||||
basLexerNext(&p->lex); // consume (
|
||||
|
||||
int32_t depth = 1;
|
||||
|
||||
while (depth > 0 && p->lex.token.type != TOK_EOF && !p->hasError) {
|
||||
if (p->lex.token.type == TOK_LPAREN) {
|
||||
depth++;
|
||||
} else if (p->lex.token.type == TOK_RPAREN) {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
basLexerNext(&p->lex);
|
||||
}
|
||||
|
||||
// Advance past the closing )
|
||||
if (p->lex.token.type == TOK_RPAREN) {
|
||||
basLexerNext(&p->lex);
|
||||
}
|
||||
|
||||
bool dotFollows = (p->lex.token.type == TOK_DOT);
|
||||
|
||||
// Restore lexer state
|
||||
p->lex = savedLex;
|
||||
p->hasError = savedErr;
|
||||
|
||||
return dotFollows;
|
||||
}
|
||||
|
||||
|
||||
static void advance(BasParserT *p) {
|
||||
if (p->hasError) {
|
||||
return;
|
||||
|
|
@ -1361,8 +1401,41 @@ static void parsePrimary(BasParserT *p) {
|
|||
}
|
||||
return;
|
||||
}
|
||||
// Unknown function -- forward reference, assume it's a function
|
||||
// Unknown identifier + '(' -- could be forward-ref function or
|
||||
// control array access: Name(index).Property
|
||||
if (sym == NULL) {
|
||||
if (checkCtrlArrayAccess(p)) {
|
||||
// Control array read: Name(idx).Property
|
||||
expect(p, TOK_LPAREN);
|
||||
|
||||
basEmit8(&p->cg, OP_PUSH_INT16);
|
||||
basEmit16(&p->cg, 0); // NULL form ref = current form
|
||||
uint16_t ctrlNameIdx = basAddConstant(&p->cg, name, (int32_t)strlen(name));
|
||||
basEmit8(&p->cg, OP_PUSH_STR);
|
||||
basEmitU16(&p->cg, ctrlNameIdx);
|
||||
|
||||
parseExpression(p); // index expression
|
||||
expect(p, TOK_RPAREN);
|
||||
basEmit8(&p->cg, OP_FIND_CTRL_IDX);
|
||||
|
||||
expect(p, TOK_DOT);
|
||||
if (!check(p, TOK_IDENT)) {
|
||||
errorExpected(p, "property name");
|
||||
return;
|
||||
}
|
||||
char memberName[BAS_MAX_TOKEN_LEN];
|
||||
strncpy(memberName, p->lex.token.text, BAS_MAX_TOKEN_LEN - 1);
|
||||
memberName[BAS_MAX_TOKEN_LEN - 1] = '\0';
|
||||
advance(p);
|
||||
|
||||
uint16_t propNameIdx = basAddConstant(&p->cg, memberName, (int32_t)strlen(memberName));
|
||||
basEmit8(&p->cg, OP_PUSH_STR);
|
||||
basEmitU16(&p->cg, propNameIdx);
|
||||
basEmit8(&p->cg, OP_LOAD_PROP);
|
||||
return;
|
||||
}
|
||||
|
||||
// Not a control array -- forward-ref function call
|
||||
sym = basSymTabAdd(&p->sym, name, SYM_FUNCTION, suffixToType(name));
|
||||
if (sym == NULL) {
|
||||
error(p, "Symbol table full");
|
||||
|
|
@ -1673,7 +1746,7 @@ static void parseAssignOrCall(BasParserT *p) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Array assignment: var(index) = expr
|
||||
// Array assignment, sub/function call, or control array access: var(index)
|
||||
if (check(p, TOK_LPAREN)) {
|
||||
// Could be a function call as a statement (discard result)
|
||||
// or array assignment
|
||||
|
|
@ -1685,6 +1758,60 @@ static void parseAssignOrCall(BasParserT *p) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Control array property/method: Name(idx).Prop = expr OR Name(idx).Method args
|
||||
if (sym == NULL && checkCtrlArrayAccess(p)) {
|
||||
expect(p, TOK_LPAREN);
|
||||
|
||||
basEmit8(&p->cg, OP_PUSH_INT16);
|
||||
basEmit16(&p->cg, 0); // NULL form ref = current form
|
||||
uint16_t ctrlNameIdx = basAddConstant(&p->cg, name, (int32_t)strlen(name));
|
||||
basEmit8(&p->cg, OP_PUSH_STR);
|
||||
basEmitU16(&p->cg, ctrlNameIdx);
|
||||
|
||||
parseExpression(p); // index expression
|
||||
expect(p, TOK_RPAREN);
|
||||
basEmit8(&p->cg, OP_FIND_CTRL_IDX);
|
||||
|
||||
expect(p, TOK_DOT);
|
||||
if (!check(p, TOK_IDENT) && !check(p, TOK_SHOW) && !check(p, TOK_HIDE)) {
|
||||
errorExpected(p, "property or method name");
|
||||
return;
|
||||
}
|
||||
char memberName[BAS_MAX_TOKEN_LEN];
|
||||
strncpy(memberName, p->lex.token.text, BAS_MAX_TOKEN_LEN - 1);
|
||||
memberName[BAS_MAX_TOKEN_LEN - 1] = '\0';
|
||||
advance(p);
|
||||
|
||||
if (check(p, TOK_EQ)) {
|
||||
// Property assignment: Name(idx).Prop = expr
|
||||
advance(p);
|
||||
uint16_t propNameIdx = basAddConstant(&p->cg, memberName, (int32_t)strlen(memberName));
|
||||
basEmit8(&p->cg, OP_PUSH_STR);
|
||||
basEmitU16(&p->cg, propNameIdx);
|
||||
parseExpression(p);
|
||||
basEmit8(&p->cg, OP_STORE_PROP);
|
||||
} else {
|
||||
// Method call: Name(idx).Method args
|
||||
uint16_t methodNameIdx = basAddConstant(&p->cg, memberName, (int32_t)strlen(memberName));
|
||||
basEmit8(&p->cg, OP_PUSH_STR);
|
||||
basEmitU16(&p->cg, methodNameIdx);
|
||||
|
||||
int32_t argc = 0;
|
||||
while (!check(p, TOK_NEWLINE) && !check(p, TOK_COLON) && !check(p, TOK_EOF) && !check(p, TOK_ELSE)) {
|
||||
if (argc > 0 && check(p, TOK_COMMA)) {
|
||||
advance(p);
|
||||
}
|
||||
parseExpression(p);
|
||||
argc++;
|
||||
}
|
||||
|
||||
basEmit8(&p->cg, OP_CALL_METHOD);
|
||||
basEmit8(&p->cg, (uint8_t)argc);
|
||||
basEmit8(&p->cg, OP_POP); // discard return value
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Array element assignment
|
||||
if (sym == NULL) {
|
||||
sym = ensureVariable(p, name);
|
||||
|
|
@ -4509,9 +4636,27 @@ static void parseStatement(BasParserT *p) {
|
|||
// Me.Hide
|
||||
basEmit8(&p->cg, OP_ME_REF);
|
||||
basEmit8(&p->cg, OP_HIDE_FORM);
|
||||
} else if (check(p, TOK_DOT)) {
|
||||
// Me.CtrlName.Property = expr (control access via Me)
|
||||
advance(p); // consume second DOT
|
||||
} else if (check(p, TOK_LPAREN) || check(p, TOK_DOT)) {
|
||||
// Me.CtrlName(idx).Property OR Me.CtrlName.Property
|
||||
bool hasIndex = check(p, TOK_LPAREN);
|
||||
|
||||
// Push form ref (Me), ctrl name
|
||||
basEmit8(&p->cg, OP_ME_REF);
|
||||
uint16_t ctrlIdx = basAddConstant(&p->cg, meMember, (int32_t)strlen(meMember));
|
||||
basEmit8(&p->cg, OP_PUSH_STR);
|
||||
basEmitU16(&p->cg, ctrlIdx);
|
||||
|
||||
if (hasIndex) {
|
||||
// Me.CtrlName(idx) -- parse index, use FIND_CTRL_IDX
|
||||
expect(p, TOK_LPAREN);
|
||||
parseExpression(p);
|
||||
expect(p, TOK_RPAREN);
|
||||
basEmit8(&p->cg, OP_FIND_CTRL_IDX);
|
||||
} else {
|
||||
basEmit8(&p->cg, OP_FIND_CTRL);
|
||||
}
|
||||
|
||||
expect(p, TOK_DOT);
|
||||
if (!check(p, TOK_IDENT)) {
|
||||
errorExpected(p, "property name");
|
||||
break;
|
||||
|
|
@ -4521,13 +4666,6 @@ static void parseStatement(BasParserT *p) {
|
|||
propName[BAS_MAX_TOKEN_LEN - 1] = '\0';
|
||||
advance(p);
|
||||
|
||||
// Push form ref (Me), ctrl name, FIND_CTRL
|
||||
basEmit8(&p->cg, OP_ME_REF);
|
||||
uint16_t ctrlIdx = basAddConstant(&p->cg, meMember, (int32_t)strlen(meMember));
|
||||
basEmit8(&p->cg, OP_PUSH_STR);
|
||||
basEmitU16(&p->cg, ctrlIdx);
|
||||
basEmit8(&p->cg, OP_FIND_CTRL);
|
||||
|
||||
if (check(p, TOK_EQ)) {
|
||||
// Property assignment
|
||||
advance(p);
|
||||
|
|
|
|||
|
|
@ -101,8 +101,9 @@ void basFormRtBindVm(BasFormRtT *rt) {
|
|||
ui.setProp = basFormRtSetProp;
|
||||
ui.callMethod = basFormRtCallMethod;
|
||||
ui.createCtrl = basFormRtCreateCtrl;
|
||||
ui.findCtrl = basFormRtFindCtrl;
|
||||
ui.loadForm = basFormRtLoadForm;
|
||||
ui.findCtrl = basFormRtFindCtrl;
|
||||
ui.findCtrlIdx = basFormRtFindCtrlIdx;
|
||||
ui.loadForm = basFormRtLoadForm;
|
||||
ui.unloadForm = basFormRtUnloadForm;
|
||||
ui.showForm = basFormRtShowForm;
|
||||
ui.hideForm = basFormRtHideForm;
|
||||
|
|
@ -269,6 +270,7 @@ void *basFormRtCreateCtrl(void *ctx, void *formRef, const char *typeName, const
|
|||
// Initialize control entry
|
||||
BasControlT entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.index = -1;
|
||||
snprintf(entry.name, BAS_MAX_CTRL_NAME, "%s", ctrlName);
|
||||
arrput(form->controls, entry);
|
||||
form->controlCount = (int32_t)arrlen(form->controls);
|
||||
|
|
@ -345,6 +347,28 @@ void *basFormRtFindCtrl(void *ctx, void *formRef, const char *ctrlName) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// basFormRtFindCtrlIdx
|
||||
// ============================================================
|
||||
|
||||
void *basFormRtFindCtrlIdx(void *ctx, void *formRef, const char *ctrlName, int32_t index) {
|
||||
(void)ctx;
|
||||
BasFormT *form = (BasFormT *)formRef;
|
||||
|
||||
if (!form) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < form->controlCount; i++) {
|
||||
if (form->controls[i].index == index && strcasecmp(form->controls[i].name, ctrlName) == 0) {
|
||||
return &form->controls[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// basFormRtFireEvent
|
||||
// ============================================================
|
||||
|
|
@ -719,6 +743,7 @@ BasFormT *basFormRtLoadFrm(BasFormRtT *rt, const char *source, int32_t sourceLen
|
|||
memset(&ctrlEntry, 0, sizeof(ctrlEntry));
|
||||
snprintf(ctrlEntry.name, BAS_MAX_CTRL_NAME, "%s", ctrlName);
|
||||
snprintf(ctrlEntry.typeName, BAS_MAX_CTRL_NAME, "%s", typeName);
|
||||
ctrlEntry.index = -1;
|
||||
ctrlEntry.widget = widget;
|
||||
ctrlEntry.form = form;
|
||||
ctrlEntry.iface = wgtGetIface(wgtTypeName);
|
||||
|
|
@ -787,6 +812,12 @@ BasFormT *basFormRtLoadFrm(BasFormRtT *rt, const char *source, int32_t sourceLen
|
|||
}
|
||||
|
||||
if (current) {
|
||||
// Control array index is stored on the struct, not as a widget property
|
||||
if (strcasecmp(key, "Index") == 0) {
|
||||
current->index = atoi(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
BasValueT val;
|
||||
|
||||
if (value[0] == '"') {
|
||||
|
|
@ -1463,126 +1494,111 @@ static void onFormDeactivate(WindowT *win) {
|
|||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetBlur
|
||||
// fireCtrlEvent -- fire event, prepending Index for array members
|
||||
// ============================================================
|
||||
|
||||
#define MAX_FIRE_ARGS 8
|
||||
|
||||
static void fireCtrlEvent(BasFormRtT *rt, BasControlT *ctrl, const char *eventName, const BasValueT *args, int32_t argCount) {
|
||||
if (ctrl->index >= 0) {
|
||||
// Control array element: prepend Index as first argument
|
||||
BasValueT allArgs[MAX_FIRE_ARGS];
|
||||
allArgs[0] = basValLong(ctrl->index);
|
||||
|
||||
for (int32_t i = 0; i < argCount && i < MAX_FIRE_ARGS - 1; i++) {
|
||||
allArgs[i + 1] = args[i];
|
||||
}
|
||||
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, eventName, allArgs, argCount + 1);
|
||||
} else if (argCount > 0 && args) {
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, eventName, args, argCount);
|
||||
} else {
|
||||
basFormRtFireEvent(rt, ctrl->form, ctrl->name, eventName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Widget event callbacks
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetBlur(WidgetT *w) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
if (!ctrl || !ctrl->form) {
|
||||
if (!ctrl || !ctrl->form || !ctrl->form->vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasFormRtT *rt = NULL;
|
||||
|
||||
if (ctrl->form->vm) {
|
||||
rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
}
|
||||
BasFormRtT *rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
|
||||
if (rt) {
|
||||
basFormRtFireEvent(rt, ctrl->form, ctrl->name, "LostFocus");
|
||||
fireCtrlEvent(rt, ctrl, "LostFocus", NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetChange
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetChange(WidgetT *w) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
if (!ctrl || !ctrl->form) {
|
||||
if (!ctrl || !ctrl->form || !ctrl->form->vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasFormRtT *rt = NULL;
|
||||
|
||||
if (ctrl->form->vm) {
|
||||
rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
}
|
||||
BasFormRtT *rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
|
||||
if (rt) {
|
||||
// Timer widgets fire "Timer" event, everything else fires "Change"
|
||||
const char *evtName = (strcasecmp(ctrl->typeName, "Timer") == 0) ? "Timer" : "Change";
|
||||
basFormRtFireEvent(rt, ctrl->form, ctrl->name, evtName);
|
||||
fireCtrlEvent(rt, ctrl, evtName, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetClick
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetClick(WidgetT *w) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
if (!ctrl || !ctrl->form) {
|
||||
if (!ctrl || !ctrl->form || !ctrl->form->vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasFormRtT *rt = NULL;
|
||||
|
||||
if (ctrl->form->vm) {
|
||||
rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
}
|
||||
BasFormRtT *rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
|
||||
if (rt) {
|
||||
basFormRtFireEvent(rt, ctrl->form, ctrl->name, "Click");
|
||||
fireCtrlEvent(rt, ctrl, "Click", NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetDblClick
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetDblClick(WidgetT *w) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
if (!ctrl || !ctrl->form) {
|
||||
if (!ctrl || !ctrl->form || !ctrl->form->vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasFormRtT *rt = NULL;
|
||||
|
||||
if (ctrl->form->vm) {
|
||||
rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
}
|
||||
BasFormRtT *rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
|
||||
if (rt) {
|
||||
basFormRtFireEvent(rt, ctrl->form, ctrl->name, "DblClick");
|
||||
fireCtrlEvent(rt, ctrl, "DblClick", NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetFocus
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetFocus(WidgetT *w) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
if (!ctrl || !ctrl->form) {
|
||||
if (!ctrl || !ctrl->form || !ctrl->form->vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasFormRtT *rt = NULL;
|
||||
|
||||
if (ctrl->form->vm) {
|
||||
rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
}
|
||||
BasFormRtT *rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
|
||||
if (rt) {
|
||||
basFormRtFireEvent(rt, ctrl->form, ctrl->name, "GotFocus");
|
||||
fireCtrlEvent(rt, ctrl, "GotFocus", NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetKeyPress / onWidgetKeyDown
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetKeyPress(WidgetT *w, int32_t keyAscii) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
|
|
@ -1595,7 +1611,7 @@ static void onWidgetKeyPress(WidgetT *w, int32_t keyAscii) {
|
|||
if (rt) {
|
||||
BasValueT args[1];
|
||||
args[0] = basValLong(keyAscii);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "KeyPress", args, 1);
|
||||
fireCtrlEvent(rt, ctrl, "KeyPress", args, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1613,15 +1629,11 @@ static void onWidgetKeyDown(WidgetT *w, int32_t keyCode, int32_t shift) {
|
|||
BasValueT args[2];
|
||||
args[0] = basValLong(keyCode);
|
||||
args[1] = basValLong(shift);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "KeyDown", args, 2);
|
||||
fireCtrlEvent(rt, ctrl, "KeyDown", args, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetKeyUp
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetKeyUp(WidgetT *w, int32_t keyCode, int32_t shift) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
|
|
@ -1635,15 +1647,11 @@ static void onWidgetKeyUp(WidgetT *w, int32_t keyCode, int32_t shift) {
|
|||
BasValueT args[2];
|
||||
args[0] = basValLong(keyCode);
|
||||
args[1] = basValLong(shift);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "KeyUp", args, 2);
|
||||
fireCtrlEvent(rt, ctrl, "KeyUp", args, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetMouseDown / onWidgetMouseUp / onWidgetMouseMove
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetMouseDown(WidgetT *w, int32_t button, int32_t x, int32_t y) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
|
|
@ -1658,7 +1666,7 @@ static void onWidgetMouseDown(WidgetT *w, int32_t button, int32_t x, int32_t y)
|
|||
args[0] = basValLong(button);
|
||||
args[1] = basValLong(x);
|
||||
args[2] = basValLong(y);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "MouseDown", args, 3);
|
||||
fireCtrlEvent(rt, ctrl, "MouseDown", args, 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1677,7 +1685,7 @@ static void onWidgetMouseUp(WidgetT *w, int32_t button, int32_t x, int32_t y) {
|
|||
args[0] = basValLong(button);
|
||||
args[1] = basValLong(x);
|
||||
args[2] = basValLong(y);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "MouseUp", args, 3);
|
||||
fireCtrlEvent(rt, ctrl, "MouseUp", args, 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1696,15 +1704,11 @@ static void onWidgetMouseMove(WidgetT *w, int32_t button, int32_t x, int32_t y)
|
|||
args[0] = basValLong(button);
|
||||
args[1] = basValLong(x);
|
||||
args[2] = basValLong(y);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "MouseMove", args, 3);
|
||||
fireCtrlEvent(rt, ctrl, "MouseMove", args, 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetScroll
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetScroll(WidgetT *w, int32_t delta) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
|
|
@ -1717,7 +1721,7 @@ static void onWidgetScroll(WidgetT *w, int32_t delta) {
|
|||
if (rt) {
|
||||
BasValueT args[1];
|
||||
args[0] = basValLong(delta);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "Scroll", args, 1);
|
||||
fireCtrlEvent(rt, ctrl, "Scroll", args, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ typedef struct BasControlT BasControlT;
|
|||
typedef struct BasControlT {
|
||||
char name[BAS_MAX_CTRL_NAME]; // VB control name (e.g. "Command1")
|
||||
char typeName[BAS_MAX_CTRL_NAME]; // VB type name (e.g. "CommandButton")
|
||||
int32_t index; // control array index (-1 = not in array)
|
||||
WidgetT *widget; // the DVX widget
|
||||
BasFormT *form; // owning form
|
||||
const WgtIfaceT *iface; // interface descriptor (from .wgt)
|
||||
|
|
@ -102,6 +103,7 @@ void basFormRtSetProp(void *ctx, void *ctrlRef, const char *propName, BasVa
|
|||
BasValueT basFormRtCallMethod(void *ctx, void *ctrlRef, const char *methodName, BasValueT *args, int32_t argc);
|
||||
void *basFormRtCreateCtrl(void *ctx, void *formRef, const char *typeName, const char *ctrlName);
|
||||
void *basFormRtFindCtrl(void *ctx, void *formRef, const char *ctrlName);
|
||||
void *basFormRtFindCtrlIdx(void *ctx, void *formRef, const char *ctrlName, int32_t index);
|
||||
void *basFormRtLoadForm(void *ctx, const char *formName);
|
||||
void basFormRtUnloadForm(void *ctx, void *formRef);
|
||||
void basFormRtShowForm(void *ctx, void *formRef, bool modal);
|
||||
|
|
|
|||
|
|
@ -436,6 +436,7 @@ bool dsgnLoadFrm(DsgnStateT *ds, const char *source, int32_t sourceLen) {
|
|||
} else if (inForm) {
|
||||
DsgnControlT ctrl;
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
ctrl.index = -1;
|
||||
snprintf(ctrl.name, DSGN_MAX_NAME, "%s", ctrlName);
|
||||
snprintf(ctrl.typeName, DSGN_MAX_NAME, "%s", typeName);
|
||||
|
||||
|
|
@ -545,6 +546,7 @@ bool dsgnLoadFrm(DsgnStateT *ds, const char *source, int32_t sourceLen) {
|
|||
else if (strcasecmp(key, "MaxWidth") == 0) { curCtrl->maxWidth = atoi(val); }
|
||||
else if (strcasecmp(key, "MaxHeight") == 0) { curCtrl->maxHeight = atoi(val); }
|
||||
else if (strcasecmp(key, "Weight") == 0) { curCtrl->weight = atoi(val); }
|
||||
else if (strcasecmp(key, "Index") == 0) { curCtrl->index = atoi(val); }
|
||||
else if (strcasecmp(key, "TabIndex") == 0) { /* ignored -- DVX has no tab order */ }
|
||||
else { setPropValue(curCtrl, key, val); }
|
||||
} else {
|
||||
|
|
@ -747,6 +749,7 @@ void dsgnOnMouse(DsgnStateT *ds, int32_t x, int32_t y, bool drag) {
|
|||
const char *typeName = ds->activeTool;
|
||||
DsgnControlT ctrl;
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
ctrl.index = -1;
|
||||
dsgnAutoName(ds, typeName, ctrl.name, DSGN_MAX_NAME);
|
||||
snprintf(ctrl.typeName, DSGN_MAX_NAME, "%s", typeName);
|
||||
ctrl.width = DEFAULT_CTRL_W;
|
||||
|
|
@ -906,6 +909,10 @@ static int32_t saveControls(const DsgnFormT *form, char *buf, int32_t bufSize, i
|
|||
|
||||
pos += snprintf(buf + pos, bufSize - pos, "%sBegin %s %s\n", pad, ctrl->typeName, ctrl->name);
|
||||
|
||||
if (ctrl->index >= 0) {
|
||||
pos += snprintf(buf + pos, bufSize - pos, "%s Index = %d\n", pad, (int)ctrl->index);
|
||||
}
|
||||
|
||||
const char *caption = getPropValue(ctrl, "Caption");
|
||||
const char *text = getPropValue(ctrl, "Text");
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ typedef struct {
|
|||
char name[DSGN_MAX_NAME];
|
||||
char typeName[DSGN_MAX_NAME];
|
||||
char parentName[DSGN_MAX_NAME]; // empty = top-level (child of form)
|
||||
int32_t index; // control array index (-1 = not in array)
|
||||
int32_t left;
|
||||
int32_t top;
|
||||
int32_t width;
|
||||
|
|
|
|||
|
|
@ -3105,6 +3105,45 @@ static void onMenu(WindowT *win, int32_t menuId) {
|
|||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// isCtrlArrayInDesigner -- check if a control name is a control
|
||||
// array member in the current designer form.
|
||||
// ============================================================
|
||||
|
||||
static bool isCtrlArrayInDesigner(const char *ctrlName) {
|
||||
if (!sDesigner.form) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t count = (int32_t)arrlen(sDesigner.form->controls);
|
||||
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
if (strcasecmp(sDesigner.form->controls[i].name, ctrlName) == 0 && sDesigner.form->controls[i].index >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// getEventExtraParams -- return the extra parameters for a
|
||||
// known event type (the part after "Index As Integer").
|
||||
// ============================================================
|
||||
|
||||
static const char *getEventExtraParams(const char *evtName) {
|
||||
if (strcasecmp(evtName, "KeyPress") == 0) { return ", KeyAscii As Integer"; }
|
||||
if (strcasecmp(evtName, "KeyDown") == 0) { return ", KeyCode As Integer, Shift As Integer"; }
|
||||
if (strcasecmp(evtName, "KeyUp") == 0) { return ", KeyCode As Integer, Shift As Integer"; }
|
||||
if (strcasecmp(evtName, "MouseDown") == 0) { return ", Button As Integer, X As Integer, Y As Integer"; }
|
||||
if (strcasecmp(evtName, "MouseUp") == 0) { return ", Button As Integer, X As Integer, Y As Integer"; }
|
||||
if (strcasecmp(evtName, "MouseMove") == 0) { return ", Button As Integer, X As Integer, Y As Integer"; }
|
||||
if (strcasecmp(evtName, "Scroll") == 0) { return ", Delta As Integer"; }
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onEvtDropdownChange
|
||||
// ============================================================
|
||||
|
|
@ -3169,8 +3208,13 @@ static void onEvtDropdownChange(WidgetT *w) {
|
|||
char subName[128];
|
||||
snprintf(subName, sizeof(subName), "%s_%s", selObj, evtName);
|
||||
|
||||
char skeleton[256];
|
||||
snprintf(skeleton, sizeof(skeleton), "Sub %s ()\n\nEnd Sub\n", subName);
|
||||
char skeleton[512];
|
||||
|
||||
if (isCtrlArrayInDesigner(selObj)) {
|
||||
snprintf(skeleton, sizeof(skeleton), "Sub %s (Index As Integer%s)\n\nEnd Sub\n", subName, getEventExtraParams(evtName));
|
||||
} else {
|
||||
snprintf(skeleton, sizeof(skeleton), "Sub %s ()\n\nEnd Sub\n", subName);
|
||||
}
|
||||
|
||||
arrput(sProcBufs, strdup(skeleton));
|
||||
showProc((int32_t)arrlen(sProcBufs) - 1);
|
||||
|
|
@ -3552,6 +3596,11 @@ static void dsgnCopySelected(void) {
|
|||
DsgnControlT *ctrl = &sDesigner.form->controls[sDesigner.selectedIdx];
|
||||
|
||||
pos += snprintf(buf + pos, sizeof(buf) - pos, "Begin %s %s\n", ctrl->typeName, ctrl->name);
|
||||
|
||||
if (ctrl->index >= 0) {
|
||||
pos += snprintf(buf + pos, sizeof(buf) - pos, " Index = %d\n", (int)ctrl->index);
|
||||
}
|
||||
|
||||
pos += snprintf(buf + pos, sizeof(buf) - pos, " Caption = \"%s\"\n", wgtGetText(ctrl->widget) ? wgtGetText(ctrl->widget) : "");
|
||||
|
||||
if (ctrl->width > 0) {
|
||||
|
|
@ -3673,13 +3722,58 @@ static void dsgnPasteControl(void) {
|
|||
|
||||
ctrlName[ci] = '\0';
|
||||
|
||||
// Auto-generate a unique name to avoid duplicates
|
||||
// Check if a control with the same name exists -- create control array
|
||||
char newName[DSGN_MAX_NAME];
|
||||
dsgnAutoName(&sDesigner, typeName, newName, DSGN_MAX_NAME);
|
||||
int32_t newIndex = -1;
|
||||
bool nameExists = false;
|
||||
int32_t highIdx = -1;
|
||||
int32_t existCount = (int32_t)arrlen(sDesigner.form->controls);
|
||||
|
||||
for (int32_t i = 0; i < existCount; i++) {
|
||||
if (strcasecmp(sDesigner.form->controls[i].name, ctrlName) == 0) {
|
||||
nameExists = true;
|
||||
if (sDesigner.form->controls[i].index > highIdx) {
|
||||
highIdx = sDesigner.form->controls[i].index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nameExists) {
|
||||
// Already a control array -- just add the next element
|
||||
if (highIdx >= 0) {
|
||||
snprintf(newName, DSGN_MAX_NAME, "%s", ctrlName);
|
||||
newIndex = highIdx + 1;
|
||||
} else {
|
||||
// Not yet an array -- ask the user
|
||||
int32_t result = dvxMessageBox(sAc, "Paste",
|
||||
"A control with this name already exists.\n"
|
||||
"Create a control array?",
|
||||
MB_YESNO | MB_ICONQUESTION);
|
||||
|
||||
if (result == ID_YES) {
|
||||
// Convert existing control to index 0
|
||||
for (int32_t i = 0; i < existCount; i++) {
|
||||
if (strcasecmp(sDesigner.form->controls[i].name, ctrlName) == 0) {
|
||||
sDesigner.form->controls[i].index = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(newName, DSGN_MAX_NAME, "%s", ctrlName);
|
||||
newIndex = 1;
|
||||
} else {
|
||||
// Rename to a unique name
|
||||
dsgnAutoName(&sDesigner, typeName, newName, DSGN_MAX_NAME);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dsgnAutoName(&sDesigner, typeName, newName, DSGN_MAX_NAME);
|
||||
}
|
||||
|
||||
// Create the control
|
||||
DsgnControlT ctrl;
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
ctrl.index = newIndex;
|
||||
snprintf(ctrl.name, DSGN_MAX_NAME, "%s", newName);
|
||||
snprintf(ctrl.typeName, DSGN_MAX_NAME, "%s", typeName);
|
||||
|
||||
|
|
@ -4065,8 +4159,13 @@ static void navigateToEventSub(void) {
|
|||
// Not found -- create a new sub skeleton for editing.
|
||||
// Don't mark dirty yet; saveCurProc will discard it if the
|
||||
// user doesn't add any code.
|
||||
char skeleton[256];
|
||||
snprintf(skeleton, sizeof(skeleton), "Sub %s ()\n\nEnd Sub\n", subName);
|
||||
char skeleton[512];
|
||||
|
||||
if (isCtrlArrayInDesigner(ctrlName)) {
|
||||
snprintf(skeleton, sizeof(skeleton), "Sub %s (Index As Integer%s)\n\nEnd Sub\n", subName, getEventExtraParams(eventName));
|
||||
} else {
|
||||
snprintf(skeleton, sizeof(skeleton), "Sub %s ()\n\nEnd Sub\n", subName);
|
||||
}
|
||||
|
||||
arrput(sProcBufs, strdup(skeleton));
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ static void onTreeReorder(WidgetT *w);
|
|||
// ============================================================
|
||||
|
||||
static void onPrpClose(WindowT *win) {
|
||||
(void)win;
|
||||
win->visible = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -107,7 +107,8 @@ static void onPrpClose(WindowT *win) {
|
|||
|
||||
static uint8_t getPropType(const char *propName, const char *typeName) {
|
||||
// Read-only properties
|
||||
if (strcasecmp(propName, "Type") == 0) { return PROP_TYPE_READONLY; }
|
||||
if (strcasecmp(propName, "Type") == 0) { return PROP_TYPE_READONLY; }
|
||||
if (strcasecmp(propName, "Index") == 0) { return PROP_TYPE_READONLY; }
|
||||
|
||||
// Known built-in types
|
||||
if (strcasecmp(propName, "Name") == 0) { return PROP_TYPE_STRING; }
|
||||
|
|
@ -343,13 +344,21 @@ static void onPropDblClick(WidgetT *w) {
|
|||
if (strcasecmp(propName, "Name") == 0) {
|
||||
char oldName[DSGN_MAX_NAME];
|
||||
snprintf(oldName, sizeof(oldName), "%s", ctrl->name);
|
||||
snprintf(ctrl->name, DSGN_MAX_NAME, "%.31s", newValue);
|
||||
|
||||
if (ctrl->widget) {
|
||||
wgtSetName(ctrl->widget, ctrl->name);
|
||||
// Rename all members of a control array, not just the selected one
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
DsgnControlT *c = &sDs->form->controls[i];
|
||||
|
||||
if (strcasecmp(c->name, oldName) == 0) {
|
||||
snprintf(c->name, DSGN_MAX_NAME, "%.31s", newValue);
|
||||
|
||||
if (c->widget) {
|
||||
wgtSetName(c->widget, c->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ideRenameInCode(oldName, ctrl->name);
|
||||
ideRenameInCode(oldName, newValue);
|
||||
prpRebuildTree(sDs);
|
||||
} else if (strcasecmp(propName, "MinWidth") == 0) {
|
||||
ctrl->width = atoi(newValue);
|
||||
|
|
@ -834,7 +843,12 @@ void prpRebuildTree(DsgnStateT *ds) {
|
|||
for (int32_t i = 0; i < count; i++) {
|
||||
DsgnControlT *ctrl = &ds->form->controls[i];
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "%s (%s)", ctrl->name, ctrl->typeName);
|
||||
|
||||
if (ctrl->index >= 0) {
|
||||
snprintf(buf, sizeof(buf), "%s(%d) (%s)", ctrl->name, (int)ctrl->index, ctrl->typeName);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s (%s)", ctrl->name, ctrl->typeName);
|
||||
}
|
||||
|
||||
char *label = strdup(buf);
|
||||
arrput(sTreeLabels, label);
|
||||
|
|
@ -897,6 +911,12 @@ void prpRefresh(DsgnStateT *ds) {
|
|||
char buf[32];
|
||||
|
||||
addPropRow("Name", ctrl->name);
|
||||
|
||||
if (ctrl->index >= 0) {
|
||||
snprintf(buf, sizeof(buf), "%d", (int)ctrl->index);
|
||||
addPropRow("Index", buf);
|
||||
}
|
||||
|
||||
addPropRow("Type", ctrl->typeName);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%d", (int)ctrl->width);
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ static void onToolClick(WidgetT *w) {
|
|||
|
||||
|
||||
static void onTbxClose(WindowT *win) {
|
||||
(void)win;
|
||||
win->visible = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ WindowT *tbxCreate(AppContextT *ctx, DsgnStateT *ds) {
|
|||
win->onClose = onTbxClose;
|
||||
|
||||
WidgetT *root = wgtInitWindow(ctx, win);
|
||||
root->spacing = wgtPixels(1);
|
||||
root->spacing = 0;
|
||||
|
||||
// Enumerate all registered widget interfaces with a basName
|
||||
int32_t ifaceCount = wgtIfaceCount();
|
||||
|
|
@ -145,7 +145,7 @@ WindowT *tbxCreate(AppContextT *ctx, DsgnStateT *ds) {
|
|||
// Start a new row every TBX_COLS buttons
|
||||
if (col == 0 || !row) {
|
||||
row = wgtHBox(root);
|
||||
row->spacing = wgtPixels(1);
|
||||
row->spacing = 0;
|
||||
}
|
||||
|
||||
// Create button in the current row
|
||||
|
|
|
|||
|
|
@ -2878,6 +2878,33 @@ BasVmResultE basVmStep(BasVmT *vm) {
|
|||
break;
|
||||
}
|
||||
|
||||
case OP_FIND_CTRL_IDX: {
|
||||
// Stack: [formRef, ctrlName, index] -- index on top
|
||||
BasValueT idxVal;
|
||||
BasValueT nameVal;
|
||||
BasValueT formVal;
|
||||
|
||||
if (!pop(vm, &idxVal) || !pop(vm, &nameVal) || !pop(vm, &formVal)) {
|
||||
return BAS_VM_STACK_UNDERFLOW;
|
||||
}
|
||||
|
||||
void *ctrlRef = NULL;
|
||||
|
||||
if (vm->ui.findCtrlIdx) {
|
||||
void *formRef = (formVal.type == BAS_TYPE_OBJECT) ? formVal.objVal : vm->currentForm;
|
||||
int32_t index = (int32_t)basValToNumber(idxVal);
|
||||
BasValueT sv = basValToString(nameVal);
|
||||
ctrlRef = vm->ui.findCtrlIdx(vm->ui.ctx, formRef, sv.strVal->data, index);
|
||||
basValRelease(&sv);
|
||||
}
|
||||
|
||||
basValRelease(&idxVal);
|
||||
basValRelease(&nameVal);
|
||||
basValRelease(&formVal);
|
||||
push(vm, basValObject(ctrlRef));
|
||||
break;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// External library calls
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -100,6 +100,9 @@ typedef void *(*BasUiCreateCtrlFnT)(void *ctx, void *formRef, const char *typeNa
|
|||
// Find an existing control by name on a form. Returns NULL if not found.
|
||||
typedef void *(*BasUiFindCtrlFnT)(void *ctx, void *formRef, const char *ctrlName);
|
||||
|
||||
// Find a control array element by name and index. Returns NULL if not found.
|
||||
typedef void *(*BasUiFindCtrlIdxFnT)(void *ctx, void *formRef, const char *ctrlName, int32_t index);
|
||||
|
||||
// Load a form by name. Returns an opaque form reference.
|
||||
typedef void *(*BasUiLoadFormFnT)(void *ctx, const char *formName);
|
||||
|
||||
|
|
@ -125,6 +128,7 @@ typedef struct {
|
|||
BasUiCallMethodFnT callMethod;
|
||||
BasUiCreateCtrlFnT createCtrl;
|
||||
BasUiFindCtrlFnT findCtrl;
|
||||
BasUiFindCtrlIdxFnT findCtrlIdx;
|
||||
BasUiLoadFormFnT loadForm;
|
||||
BasUiUnloadFormFnT unloadForm;
|
||||
BasUiShowFormFnT showForm;
|
||||
|
|
|
|||
|
|
@ -2501,6 +2501,135 @@ int main(void) {
|
|||
"End If\n"
|
||||
);
|
||||
|
||||
// ============================================================
|
||||
// Coverage: Control array read -- Name(0).Caption
|
||||
// ============================================================
|
||||
|
||||
{
|
||||
printf("=== Control array read ===\n");
|
||||
BasParserT parser;
|
||||
basParserInit(&parser,
|
||||
"DIM x AS STRING\n"
|
||||
"x = Command1(0).Caption\n"
|
||||
"PRINT x\n",
|
||||
-1);
|
||||
|
||||
if (!basParse(&parser)) {
|
||||
printf("COMPILE ERROR: %s\n", parser.error);
|
||||
} else {
|
||||
printf("OK\n");
|
||||
}
|
||||
|
||||
basParserFree(&parser);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Coverage: Control array write -- Name(idx).Caption = "text"
|
||||
// ============================================================
|
||||
|
||||
{
|
||||
printf("=== Control array write ===\n");
|
||||
BasParserT parser;
|
||||
basParserInit(&parser,
|
||||
"DIM idx AS INTEGER\n"
|
||||
"idx = 1\n"
|
||||
"Command1(idx).Caption = \"Hello\"\n",
|
||||
-1);
|
||||
|
||||
if (!basParse(&parser)) {
|
||||
printf("COMPILE ERROR: %s\n", parser.error);
|
||||
} else {
|
||||
printf("OK\n");
|
||||
}
|
||||
|
||||
basParserFree(&parser);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Coverage: Control array complex index -- Name(X + 1).Caption
|
||||
// ============================================================
|
||||
|
||||
{
|
||||
printf("=== Control array complex index ===\n");
|
||||
BasParserT parser;
|
||||
basParserInit(&parser,
|
||||
"DIM i AS INTEGER\n"
|
||||
"DIM s AS STRING\n"
|
||||
"i = 2\n"
|
||||
"s = Btn(i + 1).Text\n"
|
||||
"PRINT s\n",
|
||||
-1);
|
||||
|
||||
if (!basParse(&parser)) {
|
||||
printf("COMPILE ERROR: %s\n", parser.error);
|
||||
} else {
|
||||
printf("OK\n");
|
||||
}
|
||||
|
||||
basParserFree(&parser);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Coverage: Forward-ref function still works (no dot after paren)
|
||||
// ============================================================
|
||||
|
||||
runProgram("Forward-ref function still works",
|
||||
"Function GetValue () As Integer\n"
|
||||
" GetValue = 42\n"
|
||||
"End Function\n"
|
||||
"\n"
|
||||
"PRINT GetValue()\n"
|
||||
);
|
||||
|
||||
// ============================================================
|
||||
// Coverage: Control array in event handler
|
||||
// ============================================================
|
||||
|
||||
{
|
||||
printf("=== Control array event handler ===\n");
|
||||
BasParserT parser;
|
||||
basParserInit(&parser,
|
||||
"Sub Cmd_Click (Index As Integer)\n"
|
||||
" Cmd(Index).Caption = \"Clicked\"\n"
|
||||
"End Sub\n",
|
||||
-1);
|
||||
|
||||
if (!basParse(&parser)) {
|
||||
printf("COMPILE ERROR: %s\n", parser.error);
|
||||
} else {
|
||||
printf("OK\n");
|
||||
}
|
||||
|
||||
basParserFree(&parser);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Coverage: Me.Control(idx).Property
|
||||
// ============================================================
|
||||
|
||||
{
|
||||
printf("=== Me.Control(idx).Property ===\n");
|
||||
BasParserT parser;
|
||||
basParserInit(&parser,
|
||||
"Sub Cmd_Click (Index As Integer)\n"
|
||||
" Me.Cmd(Index).Caption = \"OK\"\n"
|
||||
"End Sub\n",
|
||||
-1);
|
||||
|
||||
if (!basParse(&parser)) {
|
||||
printf("COMPILE ERROR: %s\n", parser.error);
|
||||
} else {
|
||||
printf("OK\n");
|
||||
}
|
||||
|
||||
basParserFree(&parser);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("All tests complete.\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ static int32_t sAppCount = 0;
|
|||
int32_t appMain(DxeAppContextT *ctx);
|
||||
static void buildPmWindow(void);
|
||||
static void desktopUpdate(void);
|
||||
static void onAppButtonDblClick(WidgetT *w);
|
||||
static void onAppButtonClick(WidgetT *w);
|
||||
static void onPmClose(WindowT *win);
|
||||
static void onPmMenu(WindowT *win, int32_t menuId);
|
||||
static void scanAppsDir(void);
|
||||
|
|
@ -233,7 +233,7 @@ static void buildPmWindow(void) {
|
|||
}
|
||||
|
||||
btn->userData = &sAppFiles[i];
|
||||
btn->onDblClick = onAppButtonDblClick;
|
||||
btn->onClick = onAppButtonClick;
|
||||
|
||||
if (sAppFiles[i].tooltip[0]) {
|
||||
wgtSetTooltip(btn, sAppFiles[i].tooltip);
|
||||
|
|
@ -276,7 +276,7 @@ static void desktopUpdate(void) {
|
|||
|
||||
// Widget click handler for app grid buttons. userData was set to the
|
||||
// AppEntryT pointer during window construction, giving us the .app path.
|
||||
static void onAppButtonDblClick(WidgetT *w) {
|
||||
static void onAppButtonClick(WidgetT *w) {
|
||||
AppEntryT *entry = (AppEntryT *)w->userData;
|
||||
|
||||
if (!entry) {
|
||||
|
|
|
|||
566
tools/mkwgticon.c
Normal file
566
tools/mkwgticon.c
Normal file
|
|
@ -0,0 +1,566 @@
|
|||
// mkwgticon.c -- Generate 24x24 BMP widget icons for the DVX BASIC toolbox
|
||||
//
|
||||
// Usage: mkwgticon <output.bmp> <type>
|
||||
// Types: button, label, textbox, checkbox, radio, dropdown, combobox,
|
||||
// listbox, listview, treeview, image, imgbtn, canvas, slider,
|
||||
// spinner, progress, timer, frame, hbox, vbox, splitter,
|
||||
// scrollpane, tabctrl, toolbar, statusbar, separator, spacer,
|
||||
// terminal
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define W 24
|
||||
#define H 24
|
||||
|
||||
static uint8_t pixels[H][W][3]; // BGR
|
||||
|
||||
static void clear(uint8_t r, uint8_t g, uint8_t b) {
|
||||
for (int y = 0; y < H; y++) {
|
||||
for (int x = 0; x < W; x++) {
|
||||
pixels[y][x][0] = b;
|
||||
pixels[y][x][1] = g;
|
||||
pixels[y][x][2] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void px(int x, int y, uint8_t r, uint8_t g, uint8_t b) {
|
||||
if (x >= 0 && x < W && y >= 0 && y < H) {
|
||||
pixels[y][x][0] = b;
|
||||
pixels[y][x][1] = g;
|
||||
pixels[y][x][2] = r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void rect(int x0, int y0, int w, int h, uint8_t r, uint8_t g, uint8_t b) {
|
||||
for (int y = y0; y < y0 + h && y < H; y++) {
|
||||
for (int x = x0; x < x0 + w && x < W; x++) {
|
||||
px(x, y, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void hline(int x, int y, int len, uint8_t r, uint8_t g, uint8_t b) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
px(x + i, y, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void vline(int x, int y, int len, uint8_t r, uint8_t g, uint8_t b) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
px(x, y + i, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void box(int x0, int y0, int w, int h, uint8_t r, uint8_t g, uint8_t b) {
|
||||
hline(x0, y0, w, r, g, b);
|
||||
hline(x0, y0 + h - 1, w, r, g, b);
|
||||
vline(x0, y0, h, r, g, b);
|
||||
vline(x0 + w - 1, y0, h, r, g, b);
|
||||
}
|
||||
|
||||
|
||||
// 3x5 mini font for labeling icons
|
||||
static void miniChar(int x, int y, char ch, uint8_t r, uint8_t g, uint8_t b) {
|
||||
// Simplified: just draw recognizable glyphs for A-Z, 0-9
|
||||
static const uint16_t font[128] = {
|
||||
['A'] = 0x7D6F, ['B'] = 0xFD7F, ['C'] = 0x7C9F, ['D'] = 0xF56F,
|
||||
['E'] = 0xFC9F, ['F'] = 0xFC90, ['G'] = 0x7CBF, ['H'] = 0xB7ED,
|
||||
['I'] = 0xE92E, ['K'] = 0xB6AD, ['L'] = 0x924F, ['M'] = 0xBFED,
|
||||
['N'] = 0xBDED, ['O'] = 0x756E, ['P'] = 0xFD20, ['R'] = 0xFD6D,
|
||||
['S'] = 0x7C1F, ['T'] = 0xE924, ['U'] = 0xB6DE, ['V'] = 0xB6A4,
|
||||
['W'] = 0xB7FA, ['X'] = 0xB52D, ['Y'] = 0xB524, ['Z'] = 0xE54F,
|
||||
};
|
||||
|
||||
uint16_t bits = (ch >= 'A' && ch <= 'Z') ? font[(int)ch] : 0;
|
||||
|
||||
for (int row = 0; row < 5; row++) {
|
||||
for (int col = 0; col < 3; col++) {
|
||||
if (bits & (1 << (14 - row * 3 - col))) {
|
||||
px(x + col, y + row, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void miniText(int x, int y, const char *text, uint8_t r, uint8_t g, uint8_t b) {
|
||||
while (*text) {
|
||||
char ch = *text;
|
||||
|
||||
if (ch >= 'a' && ch <= 'z') {
|
||||
ch -= 32;
|
||||
}
|
||||
|
||||
miniChar(x, y, ch, r, g, b);
|
||||
x += 4;
|
||||
text++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void bevel(int x0, int y0, int w, int h, int bw) {
|
||||
// Raised bevel: white top-left, dark bottom-right
|
||||
for (int i = 0; i < bw; i++) {
|
||||
hline(x0 + i, y0 + i, w - i * 2, 255, 255, 255);
|
||||
vline(x0 + i, y0 + i, h - i * 2, 255, 255, 255);
|
||||
hline(x0 + i, y0 + h - 1 - i, w - i * 2, 128, 128, 128);
|
||||
vline(x0 + w - 1 - i, y0 + i, h - i * 2, 128, 128, 128);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void drawButton(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(3, 5, 18, 14, 192, 192, 192);
|
||||
bevel(3, 5, 18, 14, 2);
|
||||
miniText(7, 9, "OK", 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawLabel(void) {
|
||||
clear(192, 192, 192);
|
||||
miniText(3, 9, "LABEL", 0, 0, 128);
|
||||
}
|
||||
|
||||
|
||||
static void drawTextbox(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(2, 6, 20, 12, 255, 255, 255);
|
||||
box(2, 6, 20, 12, 128, 128, 128);
|
||||
miniText(4, 9, "TEXT", 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawCheckbox(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(4, 7, 10, 10, 255, 255, 255);
|
||||
box(4, 7, 10, 10, 128, 128, 128);
|
||||
// Checkmark
|
||||
px(6, 12, 0, 0, 0); px(7, 13, 0, 0, 0); px(8, 14, 0, 0, 0);
|
||||
px(9, 13, 0, 0, 0); px(10, 12, 0, 0, 0); px(11, 11, 0, 0, 0);
|
||||
px(12, 10, 0, 0, 0);
|
||||
miniText(16, 9, "AB", 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawRadio(void) {
|
||||
clear(192, 192, 192);
|
||||
// Circle outline
|
||||
for (int i = 5; i <= 9; i++) { px(i, 6, 128, 128, 128); px(i, 16, 128, 128, 128); }
|
||||
for (int i = 7; i <= 15; i++) { px(4, i, 128, 128, 128); px(10, i, 128, 128, 128); }
|
||||
// Filled dot
|
||||
rect(6, 9, 3, 3, 0, 0, 0);
|
||||
miniText(14, 9, "AB", 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawDropdown(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(2, 7, 20, 11, 255, 255, 255);
|
||||
box(2, 7, 20, 11, 128, 128, 128);
|
||||
// Down arrow button
|
||||
rect(16, 8, 5, 9, 192, 192, 192);
|
||||
bevel(16, 8, 5, 9, 1);
|
||||
px(17, 11, 0, 0, 0); px(18, 12, 0, 0, 0); px(19, 11, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawCombobox(void) {
|
||||
clear(192, 192, 192);
|
||||
drawDropdown();
|
||||
// Add text to distinguish from dropdown
|
||||
miniText(4, 9, "AB", 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawListbox(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(3, 3, 18, 18, 255, 255, 255);
|
||||
box(3, 3, 18, 18, 128, 128, 128);
|
||||
// List items
|
||||
hline(5, 6, 10, 0, 0, 0);
|
||||
rect(5, 9, 14, 3, 0, 0, 128); // selected
|
||||
hline(5, 10, 10, 255, 255, 255);
|
||||
hline(5, 14, 10, 0, 0, 0);
|
||||
hline(5, 17, 10, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawListview(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(2, 2, 20, 20, 255, 255, 255);
|
||||
box(2, 2, 20, 20, 128, 128, 128);
|
||||
// Column headers
|
||||
rect(3, 3, 18, 4, 192, 192, 192);
|
||||
hline(3, 6, 18, 128, 128, 128);
|
||||
vline(10, 3, 18, 128, 128, 128);
|
||||
// Grid lines
|
||||
hline(3, 10, 18, 192, 192, 192);
|
||||
hline(3, 14, 18, 192, 192, 192);
|
||||
}
|
||||
|
||||
|
||||
static void drawTreeview(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(2, 2, 20, 20, 255, 255, 255);
|
||||
box(2, 2, 20, 20, 128, 128, 128);
|
||||
// Tree lines
|
||||
vline(6, 5, 14, 128, 128, 128);
|
||||
hline(6, 7, 5, 128, 128, 128);
|
||||
hline(6, 12, 5, 128, 128, 128);
|
||||
vline(10, 12, 5, 128, 128, 128);
|
||||
hline(10, 16, 5, 128, 128, 128);
|
||||
// Expand box
|
||||
rect(4, 4, 5, 5, 255, 255, 255);
|
||||
box(4, 4, 5, 5, 0, 0, 0);
|
||||
hline(5, 6, 3, 0, 0, 0); // minus
|
||||
px(6, 5, 0, 0, 0); // plus vertical
|
||||
px(6, 7, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawImage(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(3, 3, 18, 18, 255, 255, 255);
|
||||
box(3, 3, 18, 18, 128, 128, 128);
|
||||
// Mountain landscape
|
||||
px(8, 8, 255, 255, 0); // sun
|
||||
px(9, 8, 255, 255, 0);
|
||||
px(8, 7, 255, 255, 0);
|
||||
px(9, 7, 255, 255, 0);
|
||||
// Mountain
|
||||
for (int i = 0; i < 7; i++) {
|
||||
hline(10 - i, 17 - i, 1 + i * 2, 0, 128, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void drawImgbtn(void) {
|
||||
clear(192, 192, 192);
|
||||
bevel(2, 3, 20, 18, 2);
|
||||
// Small image inside
|
||||
rect(7, 7, 10, 10, 200, 200, 255);
|
||||
box(7, 7, 10, 10, 0, 0, 128);
|
||||
}
|
||||
|
||||
|
||||
static void drawCanvas(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(3, 3, 18, 18, 255, 255, 255);
|
||||
box(3, 3, 18, 18, 128, 128, 128);
|
||||
// Diagonal line
|
||||
for (int i = 0; i < 14; i++) {
|
||||
px(4 + i, 4 + i, 255, 0, 0);
|
||||
}
|
||||
// Circle
|
||||
for (int i = 0; i < 8; i++) {
|
||||
px(14 + i/2, 6, 0, 0, 255);
|
||||
px(14 + i/2, 12, 0, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void drawSlider(void) {
|
||||
clear(192, 192, 192);
|
||||
// Track
|
||||
hline(3, 12, 18, 128, 128, 128);
|
||||
hline(3, 13, 18, 255, 255, 255);
|
||||
// Thumb
|
||||
rect(10, 8, 4, 8, 192, 192, 192);
|
||||
bevel(10, 8, 4, 8, 1);
|
||||
}
|
||||
|
||||
|
||||
static void drawSpinner(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(3, 6, 14, 12, 255, 255, 255);
|
||||
box(3, 6, 14, 12, 128, 128, 128);
|
||||
miniText(5, 9, "42", 0, 0, 0);
|
||||
// Up/down buttons
|
||||
rect(17, 6, 5, 6, 192, 192, 192);
|
||||
bevel(17, 6, 5, 6, 1);
|
||||
rect(17, 12, 5, 6, 192, 192, 192);
|
||||
bevel(17, 12, 5, 6, 1);
|
||||
// Arrows
|
||||
px(19, 8, 0, 0, 0); px(18, 9, 0, 0, 0); px(20, 9, 0, 0, 0);
|
||||
px(18, 14, 0, 0, 0); px(20, 14, 0, 0, 0); px(19, 15, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawProgress(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(2, 8, 20, 8, 255, 255, 255);
|
||||
box(2, 8, 20, 8, 128, 128, 128);
|
||||
// Fill
|
||||
rect(3, 9, 12, 6, 0, 0, 128);
|
||||
}
|
||||
|
||||
|
||||
static void drawTimer(void) {
|
||||
clear(192, 192, 192);
|
||||
// Clock face
|
||||
for (int a = 0; a < 12; a++) {
|
||||
int cx = 12, cy = 12, r = 8;
|
||||
// Approximate circle points
|
||||
static const int dx[] = {0, 4, 7, 8, 7, 4, 0, -4, -7, -8, -7, -4};
|
||||
static const int dy[] = {-8, -7, -4, 0, 4, 7, 8, 7, 4, 0, -4, -7};
|
||||
px(cx + dx[a], cy + dy[a], 0, 0, 0);
|
||||
}
|
||||
// Hands
|
||||
vline(12, 5, 7, 0, 0, 0);
|
||||
hline(12, 12, 5, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawFrame(void) {
|
||||
clear(192, 192, 192);
|
||||
box(3, 6, 18, 15, 128, 128, 128);
|
||||
rect(5, 4, 14, 5, 192, 192, 192);
|
||||
miniText(6, 4, "GRP", 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawHbox(void) {
|
||||
clear(192, 192, 192);
|
||||
box(2, 4, 20, 16, 0, 0, 128);
|
||||
vline(8, 4, 16, 0, 0, 128);
|
||||
vline(15, 4, 16, 0, 0, 128);
|
||||
// Arrow
|
||||
hline(4, 12, 16, 0, 0, 128);
|
||||
px(18, 11, 0, 0, 128); px(18, 13, 0, 0, 128);
|
||||
}
|
||||
|
||||
|
||||
static void drawVbox(void) {
|
||||
clear(192, 192, 192);
|
||||
box(2, 2, 20, 20, 0, 128, 0);
|
||||
hline(2, 8, 20, 0, 128, 0);
|
||||
hline(2, 15, 20, 0, 128, 0);
|
||||
// Arrow
|
||||
vline(12, 4, 16, 0, 128, 0);
|
||||
px(11, 18, 0, 128, 0); px(13, 18, 0, 128, 0);
|
||||
}
|
||||
|
||||
|
||||
static void drawSplitter(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(2, 2, 9, 20, 200, 200, 200);
|
||||
box(2, 2, 9, 20, 128, 128, 128);
|
||||
rect(13, 2, 9, 20, 200, 200, 200);
|
||||
box(13, 2, 9, 20, 128, 128, 128);
|
||||
// Grip dots
|
||||
for (int i = 0; i < 3; i++) {
|
||||
px(11, 9 + i * 3, 128, 128, 128);
|
||||
px(12, 9 + i * 3, 255, 255, 255);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void drawScrollpane(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(2, 2, 18, 18, 255, 255, 255);
|
||||
box(2, 2, 20, 20, 128, 128, 128);
|
||||
// Scrollbar
|
||||
rect(19, 2, 3, 20, 192, 192, 192);
|
||||
rect(19, 5, 3, 6, 160, 160, 160);
|
||||
bevel(19, 5, 3, 6, 1);
|
||||
}
|
||||
|
||||
|
||||
static void drawTabctrl(void) {
|
||||
clear(192, 192, 192);
|
||||
// Tab buttons
|
||||
rect(3, 4, 8, 5, 192, 192, 192);
|
||||
hline(3, 4, 8, 255, 255, 255);
|
||||
vline(3, 4, 5, 255, 255, 255);
|
||||
vline(10, 4, 5, 128, 128, 128);
|
||||
rect(11, 5, 8, 4, 180, 180, 180);
|
||||
hline(11, 5, 8, 255, 255, 255);
|
||||
vline(18, 5, 4, 128, 128, 128);
|
||||
// Body
|
||||
rect(2, 9, 20, 13, 192, 192, 192);
|
||||
box(2, 9, 20, 13, 128, 128, 128);
|
||||
}
|
||||
|
||||
|
||||
static void drawToolbar(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(1, 7, 22, 10, 192, 192, 192);
|
||||
bevel(1, 7, 22, 10, 1);
|
||||
// Buttons
|
||||
rect(3, 8, 5, 8, 192, 192, 192);
|
||||
bevel(3, 8, 5, 8, 1);
|
||||
rect(9, 8, 5, 8, 192, 192, 192);
|
||||
bevel(9, 8, 5, 8, 1);
|
||||
rect(15, 8, 5, 8, 192, 192, 192);
|
||||
bevel(15, 8, 5, 8, 1);
|
||||
}
|
||||
|
||||
|
||||
static void drawStatusbar(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(1, 14, 22, 8, 192, 192, 192);
|
||||
// Sunken panels
|
||||
hline(2, 15, 12, 128, 128, 128);
|
||||
hline(2, 20, 12, 255, 255, 255);
|
||||
vline(2, 15, 6, 128, 128, 128);
|
||||
vline(13, 15, 6, 255, 255, 255);
|
||||
hline(15, 15, 7, 128, 128, 128);
|
||||
hline(15, 20, 7, 255, 255, 255);
|
||||
vline(15, 15, 6, 128, 128, 128);
|
||||
vline(21, 15, 6, 255, 255, 255);
|
||||
}
|
||||
|
||||
|
||||
static void drawSeparator(void) {
|
||||
clear(192, 192, 192);
|
||||
// Horizontal separator line
|
||||
hline(3, 11, 18, 128, 128, 128);
|
||||
hline(3, 12, 18, 255, 255, 255);
|
||||
// Vertical separator line
|
||||
vline(12, 3, 18, 128, 128, 128);
|
||||
vline(13, 3, 18, 255, 255, 255);
|
||||
}
|
||||
|
||||
|
||||
static void drawSpacer(void) {
|
||||
clear(192, 192, 192);
|
||||
// Dotted rectangle outline
|
||||
for (int i = 0; i < 20; i += 2) {
|
||||
px(2 + i, 4, 128, 128, 128);
|
||||
px(2 + i, 19, 128, 128, 128);
|
||||
}
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
px(2, 4 + i, 128, 128, 128);
|
||||
px(21, 4 + i, 128, 128, 128);
|
||||
}
|
||||
// Double-headed arrow
|
||||
hline(5, 12, 14, 128, 128, 128);
|
||||
px(6, 11, 128, 128, 128); px(6, 13, 128, 128, 128);
|
||||
px(18, 11, 128, 128, 128); px(18, 13, 128, 128, 128);
|
||||
}
|
||||
|
||||
|
||||
static void drawTerminal(void) {
|
||||
clear(192, 192, 192);
|
||||
rect(2, 2, 20, 20, 0, 0, 0);
|
||||
box(2, 2, 20, 20, 128, 128, 128);
|
||||
// Green text on black
|
||||
miniText(4, 5, "C", 0, 255, 0);
|
||||
hline(4, 11, 4, 0, 255, 0);
|
||||
miniText(4, 14, "A", 0, 255, 0);
|
||||
// Cursor
|
||||
rect(8, 14, 2, 5, 0, 255, 0);
|
||||
}
|
||||
|
||||
|
||||
static void writeBmp(const char *path) {
|
||||
// 24-bit BMP, bottom-up
|
||||
int rowBytes = W * 3;
|
||||
int padding = (4 - (rowBytes % 4)) % 4;
|
||||
int stride = rowBytes + padding;
|
||||
int dataSize = stride * H;
|
||||
int fileSize = 54 + dataSize;
|
||||
|
||||
FILE *f = fopen(path, "wb");
|
||||
|
||||
if (!f) {
|
||||
fprintf(stderr, "Cannot open %s\n", path);
|
||||
return;
|
||||
}
|
||||
|
||||
// BMP header
|
||||
uint8_t hdr[54];
|
||||
memset(hdr, 0, sizeof(hdr));
|
||||
hdr[0] = 'B'; hdr[1] = 'M';
|
||||
hdr[2] = fileSize; hdr[3] = fileSize >> 8; hdr[4] = fileSize >> 16; hdr[5] = fileSize >> 24;
|
||||
hdr[10] = 54;
|
||||
hdr[14] = 40;
|
||||
hdr[18] = W; hdr[19] = W >> 8;
|
||||
hdr[22] = H; hdr[23] = H >> 8;
|
||||
hdr[26] = 1;
|
||||
hdr[28] = 24;
|
||||
|
||||
fwrite(hdr, 1, 54, f);
|
||||
|
||||
uint8_t pad[3] = {0, 0, 0};
|
||||
|
||||
for (int y = H - 1; y >= 0; y--) {
|
||||
fwrite(pixels[y], 1, rowBytes, f);
|
||||
|
||||
if (padding) {
|
||||
fwrite(pad, 1, padding, f);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: mkwgticon <output.bmp> <type>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *path = argv[1];
|
||||
const char *type = argv[2];
|
||||
|
||||
typedef struct { const char *name; void (*fn)(void); } IconT;
|
||||
IconT icons[] = {
|
||||
{"button", drawButton},
|
||||
{"label", drawLabel},
|
||||
{"textbox", drawTextbox},
|
||||
{"checkbox", drawCheckbox},
|
||||
{"radio", drawRadio},
|
||||
{"dropdown", drawDropdown},
|
||||
{"combobox", drawCombobox},
|
||||
{"listbox", drawListbox},
|
||||
{"listview", drawListview},
|
||||
{"treeview", drawTreeview},
|
||||
{"image", drawImage},
|
||||
{"imgbtn", drawImgbtn},
|
||||
{"canvas", drawCanvas},
|
||||
{"slider", drawSlider},
|
||||
{"spinner", drawSpinner},
|
||||
{"progress", drawProgress},
|
||||
{"timer", drawTimer},
|
||||
{"frame", drawFrame},
|
||||
{"hbox", drawHbox},
|
||||
{"vbox", drawVbox},
|
||||
{"splitter", drawSplitter},
|
||||
{"scrollpane", drawScrollpane},
|
||||
{"tabctrl", drawTabctrl},
|
||||
{"toolbar", drawToolbar},
|
||||
{"statusbar", drawStatusbar},
|
||||
{"separator", drawSeparator},
|
||||
{"spacer", drawSpacer},
|
||||
{"terminal", drawTerminal},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
for (int i = 0; icons[i].name; i++) {
|
||||
if (strcmp(type, icons[i].name) == 0) {
|
||||
icons[i].fn();
|
||||
writeBmp(path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Unknown type: %s\nAvailable:", type);
|
||||
|
||||
for (int i = 0; icons[i].name; i++) {
|
||||
fprintf(stderr, " %s", icons[i].name);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
return 1;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue