More events added and wired to BASIC.
This commit is contained in:
parent
1fb8e2a387
commit
4d4aedbc43
8 changed files with 724 additions and 23 deletions
|
|
@ -73,6 +73,12 @@ static void onWidgetChange(WidgetT *w);
|
|||
static void onWidgetClick(WidgetT *w);
|
||||
static void onWidgetDblClick(WidgetT *w);
|
||||
static void onWidgetFocus(WidgetT *w);
|
||||
static void onWidgetKeyPress(WidgetT *w, int32_t keyAscii);
|
||||
static void onWidgetKeyDown(WidgetT *w, int32_t keyCode, int32_t shift);
|
||||
static void onWidgetMouseDown(WidgetT *w, int32_t button, int32_t x, int32_t y);
|
||||
static void onWidgetMouseUp(WidgetT *w, int32_t button, int32_t x, int32_t y);
|
||||
static void onWidgetMouseMove(WidgetT *w, int32_t button, int32_t x, int32_t y);
|
||||
static void onWidgetScroll(WidgetT *w, int32_t delta);
|
||||
static void parseFrmLine(const char *line, char *key, char *value);
|
||||
static void rebuildListBoxItems(BasControlT *ctrl);
|
||||
static const char *resolveTypeName(const char *typeName);
|
||||
|
|
@ -341,6 +347,10 @@ void *basFormRtFindCtrl(void *ctx, void *formRef, const char *ctrlName) {
|
|||
// ============================================================
|
||||
|
||||
bool basFormRtFireEvent(BasFormRtT *rt, BasFormT *form, const char *ctrlName, const char *eventName) {
|
||||
return basFormRtFireEventArgs(rt, form, ctrlName, eventName, NULL, 0);
|
||||
}
|
||||
|
||||
bool basFormRtFireEventArgs(BasFormRtT *rt, BasFormT *form, const char *ctrlName, const char *eventName, const BasValueT *args, int32_t argCount) {
|
||||
if (!rt || !form || !rt->vm || !rt->module) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -354,7 +364,13 @@ bool basFormRtFireEvent(BasFormRtT *rt, BasFormT *form, const char *ctrlName, co
|
|||
return false;
|
||||
}
|
||||
|
||||
if (proc->isFunction || proc->paramCount > 0) {
|
||||
if (proc->isFunction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Strict parameter matching: the sub must declare exactly the
|
||||
// number of parameters the event provides, or zero (no params).
|
||||
if (proc->paramCount != 0 && proc->paramCount != argCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -362,7 +378,13 @@ bool basFormRtFireEvent(BasFormRtT *rt, BasFormT *form, const char *ctrlName, co
|
|||
rt->currentForm = form;
|
||||
basVmSetCurrentForm(rt->vm, form);
|
||||
|
||||
bool ok = basVmCallSub(rt->vm, proc->codeAddr);
|
||||
bool ok;
|
||||
|
||||
if (argCount > 0 && args) {
|
||||
ok = basVmCallSubWithArgs(rt->vm, proc->codeAddr, args, argCount);
|
||||
} else {
|
||||
ok = basVmCallSub(rt->vm, proc->codeAddr);
|
||||
}
|
||||
|
||||
rt->currentForm = prevForm;
|
||||
basVmSetCurrentForm(rt->vm, prevForm);
|
||||
|
|
@ -647,11 +669,17 @@ BasFormT *basFormRtLoadFrm(BasFormRtT *rt, const char *source, int32_t sourceLen
|
|||
// Re-derive pointer after arrput (may realloc)
|
||||
current = &form->controls[form->controlCount - 1];
|
||||
widget->userData = current;
|
||||
widget->onClick = onWidgetClick;
|
||||
widget->onDblClick = onWidgetDblClick;
|
||||
widget->onChange = onWidgetChange;
|
||||
widget->onFocus = onWidgetFocus;
|
||||
widget->onBlur = onWidgetBlur;
|
||||
widget->onClick = onWidgetClick;
|
||||
widget->onDblClick = onWidgetDblClick;
|
||||
widget->onChange = onWidgetChange;
|
||||
widget->onFocus = onWidgetFocus;
|
||||
widget->onBlur = onWidgetBlur;
|
||||
widget->onKeyPress = onWidgetKeyPress;
|
||||
widget->onKeyDown = onWidgetKeyDown;
|
||||
widget->onMouseDown = onWidgetMouseDown;
|
||||
widget->onMouseUp = onWidgetMouseUp;
|
||||
widget->onMouseMove = onWidgetMouseMove;
|
||||
widget->onScroll = onWidgetScroll;
|
||||
}
|
||||
|
||||
// Track block type for End handling
|
||||
|
|
@ -1447,6 +1475,127 @@ static void onWidgetFocus(WidgetT *w) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetKeyPress / onWidgetKeyDown
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetKeyPress(WidgetT *w, int32_t keyAscii) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
if (!ctrl || !ctrl->form || !ctrl->form->vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasFormRtT *rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
|
||||
if (rt) {
|
||||
BasValueT args[1];
|
||||
args[0] = basValLong(keyAscii);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "KeyPress", args, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void onWidgetKeyDown(WidgetT *w, int32_t keyCode, int32_t shift) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
if (!ctrl || !ctrl->form || !ctrl->form->vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasFormRtT *rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
|
||||
if (rt) {
|
||||
BasValueT args[2];
|
||||
args[0] = basValLong(keyCode);
|
||||
args[1] = basValLong(shift);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "KeyDown", args, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetMouseDown / onWidgetMouseUp / onWidgetMouseMove
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetMouseDown(WidgetT *w, int32_t button, int32_t x, int32_t y) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
if (!ctrl || !ctrl->form || !ctrl->form->vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasFormRtT *rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
|
||||
if (rt) {
|
||||
BasValueT args[3];
|
||||
args[0] = basValLong(button);
|
||||
args[1] = basValLong(x);
|
||||
args[2] = basValLong(y);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "MouseDown", args, 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void onWidgetMouseUp(WidgetT *w, int32_t button, int32_t x, int32_t y) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
if (!ctrl || !ctrl->form || !ctrl->form->vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasFormRtT *rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
|
||||
if (rt) {
|
||||
BasValueT args[3];
|
||||
args[0] = basValLong(button);
|
||||
args[1] = basValLong(x);
|
||||
args[2] = basValLong(y);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "MouseUp", args, 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void onWidgetMouseMove(WidgetT *w, int32_t button, int32_t x, int32_t y) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
if (!ctrl || !ctrl->form || !ctrl->form->vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasFormRtT *rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
|
||||
if (rt) {
|
||||
BasValueT args[3];
|
||||
args[0] = basValLong(button);
|
||||
args[1] = basValLong(x);
|
||||
args[2] = basValLong(y);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "MouseMove", args, 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onWidgetScroll
|
||||
// ============================================================
|
||||
|
||||
static void onWidgetScroll(WidgetT *w, int32_t delta) {
|
||||
BasControlT *ctrl = (BasControlT *)w->userData;
|
||||
|
||||
if (!ctrl || !ctrl->form || !ctrl->form->vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasFormRtT *rt = (BasFormRtT *)ctrl->form->vm->ui.ctx;
|
||||
|
||||
if (rt) {
|
||||
BasValueT args[1];
|
||||
args[0] = basValLong(delta);
|
||||
basFormRtFireEventArgs(rt, ctrl->form, ctrl->name, "Scroll", args, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// parseFrmLine
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ int32_t basFormRtMsgBox(void *ctx, const char *message, int32_t flags);
|
|||
// ---- Event dispatch ----
|
||||
|
||||
bool basFormRtFireEvent(BasFormRtT *rt, BasFormT *form, const char *ctrlName, const char *eventName);
|
||||
bool basFormRtFireEventArgs(BasFormRtT *rt, BasFormT *form, const char *ctrlName, const char *eventName, const BasValueT *args, int32_t argCount);
|
||||
|
||||
// ---- Form file loading ----
|
||||
|
||||
|
|
|
|||
|
|
@ -2432,24 +2432,64 @@ static void onEvtDropdownChange(WidgetT *w) {
|
|||
int32_t objIdx = wgtDropdownGetSelected(sObjDropdown);
|
||||
int32_t evtIdx = wgtDropdownGetSelected(sEvtDropdown);
|
||||
|
||||
if (objIdx < 0 || objIdx >= (int32_t)arrlen(sObjItems) || evtIdx < 0) {
|
||||
if (objIdx < 0 || objIdx >= (int32_t)arrlen(sObjItems) ||
|
||||
evtIdx < 0 || evtIdx >= (int32_t)arrlen(sEvtItems)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *selObj = sObjItems[objIdx];
|
||||
const char *selEvt = sEvtItems[evtIdx];
|
||||
|
||||
// Find the proc matching the selected object + event
|
||||
int32_t matchIdx = 0;
|
||||
int32_t procCount = (int32_t)arrlen(sProcTable);
|
||||
// Strip brackets if present (unimplemented event)
|
||||
char evtName[64];
|
||||
if (selEvt[0] == '[') {
|
||||
snprintf(evtName, sizeof(evtName), "%s", selEvt + 1);
|
||||
int32_t len = (int32_t)strlen(evtName);
|
||||
|
||||
if (len > 0 && evtName[len - 1] == ']') {
|
||||
evtName[len - 1] = '\0';
|
||||
}
|
||||
} else {
|
||||
snprintf(evtName, sizeof(evtName), "%s", selEvt);
|
||||
}
|
||||
|
||||
// Search for an existing proc matching object + event
|
||||
int32_t procCount = (int32_t)arrlen(sProcTable);
|
||||
|
||||
for (int32_t i = 0; i < procCount; i++) {
|
||||
if (strcasecmp(sProcTable[i].objName, selObj) == 0) {
|
||||
if (matchIdx == evtIdx) {
|
||||
showProc(i);
|
||||
return;
|
||||
}
|
||||
if (strcasecmp(sProcTable[i].objName, selObj) == 0 &&
|
||||
strcasecmp(sProcTable[i].evtName, evtName) == 0) {
|
||||
showProc(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
matchIdx++;
|
||||
// Not found -- create a new sub skeleton
|
||||
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);
|
||||
|
||||
arrput(sProcBufs, strdup(skeleton));
|
||||
|
||||
// Update form code if editing a form
|
||||
if (sDesigner.form) {
|
||||
free(sDesigner.form->code);
|
||||
sDesigner.form->code = strdup(getFullSource());
|
||||
sDesigner.form->dirty = true;
|
||||
}
|
||||
|
||||
updateDropdowns();
|
||||
|
||||
// Show the new proc (last in the list)
|
||||
procCount = (int32_t)arrlen(sProcTable);
|
||||
|
||||
for (int32_t i = 0; i < procCount; i++) {
|
||||
if (strcasecmp(sProcTable[i].objName, selObj) == 0 &&
|
||||
strcasecmp(sProcTable[i].evtName, evtName) == 0) {
|
||||
showProc(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2513,6 +2553,25 @@ static void onImmediateChange(WidgetT *w) {
|
|||
//
|
||||
// Update the Event dropdown when the Object selection changes.
|
||||
|
||||
// Common events available on all controls
|
||||
static const char *sCommonEvents[] = {
|
||||
"Click", "DblClick", "Change", "GotFocus", "LostFocus",
|
||||
"KeyPress", "KeyDown",
|
||||
"MouseDown", "MouseUp", "MouseMove", "Scroll",
|
||||
NULL
|
||||
};
|
||||
|
||||
// Form-specific events
|
||||
static const char *sFormEvents[] = {
|
||||
"Load", "Unload", "Resize", "Activate", "Deactivate",
|
||||
"KeyPress", "KeyDown",
|
||||
"MouseDown", "MouseUp", "MouseMove",
|
||||
NULL
|
||||
};
|
||||
|
||||
// Buffer for event dropdown labels (with [] for unimplemented)
|
||||
static char sEvtLabelBufs[64][32];
|
||||
|
||||
static void onObjDropdownChange(WidgetT *w) {
|
||||
(void)w;
|
||||
|
||||
|
|
@ -2528,17 +2587,157 @@ static void onObjDropdownChange(WidgetT *w) {
|
|||
|
||||
const char *selObj = sObjItems[objIdx];
|
||||
|
||||
// Build event list for the selected object
|
||||
// Collect which events already have code
|
||||
arrsetlen(sEvtItems, 0);
|
||||
|
||||
int32_t procCount = (int32_t)arrlen(sProcTable);
|
||||
const char **existingEvts = NULL; // stb_ds temp array
|
||||
|
||||
for (int32_t i = 0; i < procCount; i++) {
|
||||
if (strcasecmp(sProcTable[i].objName, selObj) == 0) {
|
||||
arrput(sEvtItems, sProcTable[i].evtName);
|
||||
arrput(existingEvts, sProcTable[i].evtName);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which event list to use
|
||||
const char **availEvents = sCommonEvents;
|
||||
|
||||
if (strcasecmp(selObj, "(General)") == 0) {
|
||||
// (General) has no standard events -- just show existing procs
|
||||
for (int32_t i = 0; i < (int32_t)arrlen(existingEvts); i++) {
|
||||
arrput(sEvtItems, existingEvts[i]);
|
||||
}
|
||||
|
||||
arrfree(existingEvts);
|
||||
|
||||
int32_t evtCount = (int32_t)arrlen(sEvtItems);
|
||||
wgtDropdownSetItems(sEvtDropdown, sEvtItems, evtCount);
|
||||
|
||||
if (evtCount > 0) {
|
||||
wgtDropdownSetSelected(sEvtDropdown, 0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a form name
|
||||
bool isForm = false;
|
||||
|
||||
if (sDesigner.form && strcasecmp(selObj, sDesigner.form->name) == 0) {
|
||||
isForm = true;
|
||||
availEvents = sFormEvents;
|
||||
}
|
||||
|
||||
// Get widget-specific events from the interface
|
||||
const WgtIfaceT *iface = NULL;
|
||||
|
||||
if (!isForm && sDesigner.form) {
|
||||
for (int32_t i = 0; i < (int32_t)arrlen(sDesigner.form->controls); i++) {
|
||||
if (strcasecmp(sDesigner.form->controls[i].name, selObj) == 0) {
|
||||
const char *wgtName = wgtFindByBasName(sDesigner.form->controls[i].typeName);
|
||||
|
||||
if (wgtName) {
|
||||
iface = wgtGetIface(wgtName);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the event list: standard events + widget-specific events
|
||||
int32_t labelIdx = 0;
|
||||
|
||||
// Add standard events (common or form)
|
||||
for (int32_t i = 0; availEvents[i]; i++) {
|
||||
bool hasCode = false;
|
||||
|
||||
for (int32_t j = 0; j < (int32_t)arrlen(existingEvts); j++) {
|
||||
if (strcasecmp(existingEvts[j], availEvents[i]) == 0) {
|
||||
hasCode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (labelIdx < 64) {
|
||||
if (hasCode) {
|
||||
snprintf(sEvtLabelBufs[labelIdx], 32, "%s", availEvents[i]);
|
||||
} else {
|
||||
snprintf(sEvtLabelBufs[labelIdx], 32, "[%s]", availEvents[i]);
|
||||
}
|
||||
|
||||
arrput(sEvtItems, sEvtLabelBufs[labelIdx]);
|
||||
labelIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
// Add widget-specific events
|
||||
if (iface) {
|
||||
for (int32_t i = 0; i < iface->eventCount; i++) {
|
||||
const char *evtName = iface->events[i].name;
|
||||
|
||||
// Skip if already in the standard list
|
||||
bool alreadyListed = false;
|
||||
|
||||
for (int32_t j = 0; availEvents[j]; j++) {
|
||||
if (strcasecmp(availEvents[j], evtName) == 0) {
|
||||
alreadyListed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (alreadyListed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool hasCode = false;
|
||||
|
||||
for (int32_t j = 0; j < (int32_t)arrlen(existingEvts); j++) {
|
||||
if (strcasecmp(existingEvts[j], evtName) == 0) {
|
||||
hasCode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (labelIdx < 64) {
|
||||
if (hasCode) {
|
||||
snprintf(sEvtLabelBufs[labelIdx], 32, "%s", evtName);
|
||||
} else {
|
||||
snprintf(sEvtLabelBufs[labelIdx], 32, "[%s]", evtName);
|
||||
}
|
||||
|
||||
arrput(sEvtItems, sEvtLabelBufs[labelIdx]);
|
||||
labelIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add any existing events not in the standard/widget list (custom subs)
|
||||
for (int32_t i = 0; i < (int32_t)arrlen(existingEvts); i++) {
|
||||
bool alreadyListed = false;
|
||||
int32_t evtCount = (int32_t)arrlen(sEvtItems);
|
||||
|
||||
for (int32_t j = 0; j < evtCount; j++) {
|
||||
const char *label = sEvtItems[j];
|
||||
|
||||
// Strip brackets for comparison
|
||||
if (label[0] == '[') { label++; }
|
||||
|
||||
if (strncasecmp(label, existingEvts[i], strlen(existingEvts[i])) == 0) {
|
||||
alreadyListed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyListed && labelIdx < 64) {
|
||||
snprintf(sEvtLabelBufs[labelIdx], 32, "%s", existingEvts[i]);
|
||||
arrput(sEvtItems, sEvtLabelBufs[labelIdx]);
|
||||
labelIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
arrfree(existingEvts);
|
||||
|
||||
int32_t evtCount = (int32_t)arrlen(sEvtItems);
|
||||
wgtDropdownSetItems(sEvtDropdown, sEvtItems, evtCount);
|
||||
|
||||
|
|
@ -3320,7 +3519,178 @@ static void parseProcs(const char *source) {
|
|||
}
|
||||
|
||||
|
||||
// saveCurProc -- save editor contents back to the current buffer
|
||||
// extractNewProcs -- scan a buffer for Sub/Function declarations that
|
||||
// don't belong (e.g. user typed a new Sub in the General section).
|
||||
// Extracts them into new sProcBufs entries and removes them from the
|
||||
// source buffer. Returns a new buffer (caller frees) or NULL if no
|
||||
// extraction was needed.
|
||||
|
||||
static char *extractNewProcs(const char *buf) {
|
||||
if (!buf || !buf[0]) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Scan for Sub/Function at the start of a line
|
||||
bool found = false;
|
||||
const char *pos = buf;
|
||||
|
||||
while (*pos) {
|
||||
const char *trimmed = pos;
|
||||
|
||||
while (*trimmed == ' ' || *trimmed == '\t') {
|
||||
trimmed++;
|
||||
}
|
||||
|
||||
bool isSub = (strncasecmp(trimmed, "SUB ", 4) == 0);
|
||||
bool isFunc = (strncasecmp(trimmed, "FUNCTION ", 9) == 0);
|
||||
|
||||
if (isSub || isFunc) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
while (*pos && *pos != '\n') { pos++; }
|
||||
if (*pos == '\n') { pos++; }
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Build the remaining text (before the first proc) and extract procs
|
||||
int32_t bufLen = (int32_t)strlen(buf);
|
||||
char *remaining = (char *)malloc(bufLen + 1);
|
||||
|
||||
if (!remaining) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int32_t remPos = 0;
|
||||
pos = buf;
|
||||
|
||||
while (*pos) {
|
||||
const char *lineStart = pos;
|
||||
const char *trimmed = pos;
|
||||
|
||||
while (*trimmed == ' ' || *trimmed == '\t') {
|
||||
trimmed++;
|
||||
}
|
||||
|
||||
bool isSub = (strncasecmp(trimmed, "SUB ", 4) == 0);
|
||||
bool isFunc = (strncasecmp(trimmed, "FUNCTION ", 9) == 0);
|
||||
|
||||
if (isSub || isFunc) {
|
||||
// Extract procedure name for duplicate check
|
||||
const char *np = trimmed + (isSub ? 4 : 9);
|
||||
|
||||
while (*np == ' ' || *np == '\t') { np++; }
|
||||
|
||||
char newName[128];
|
||||
int32_t nn = 0;
|
||||
|
||||
while (*np && *np != '(' && *np != ' ' && *np != '\t' && *np != '\n' && nn < 127) {
|
||||
newName[nn++] = *np++;
|
||||
}
|
||||
|
||||
newName[nn] = '\0';
|
||||
|
||||
// Check for duplicate against existing proc buffers
|
||||
bool isDuplicate = false;
|
||||
|
||||
for (int32_t p = 0; p < (int32_t)arrlen(sProcBufs); p++) {
|
||||
if (!sProcBufs[p]) { continue; }
|
||||
|
||||
const char *ep = sProcBufs[p];
|
||||
|
||||
while (*ep == ' ' || *ep == '\t') { ep++; }
|
||||
|
||||
if (strncasecmp(ep, "SUB ", 4) == 0) { ep += 4; }
|
||||
else if (strncasecmp(ep, "FUNCTION ", 9) == 0) { ep += 9; }
|
||||
|
||||
while (*ep == ' ' || *ep == '\t') { ep++; }
|
||||
|
||||
char existName[128];
|
||||
int32_t en = 0;
|
||||
|
||||
while (*ep && *ep != '(' && *ep != ' ' && *ep != '\t' && *ep != '\n' && en < 127) {
|
||||
existName[en++] = *ep++;
|
||||
}
|
||||
|
||||
existName[en] = '\0';
|
||||
|
||||
if (strcasecmp(newName, existName) == 0) {
|
||||
isDuplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find End Sub / End Function
|
||||
const char *endTag = isSub ? "END SUB" : "END FUNCTION";
|
||||
int32_t endTagLen = isSub ? 7 : 12;
|
||||
const char *scan = pos;
|
||||
|
||||
while (*scan && *scan != '\n') { scan++; }
|
||||
if (*scan == '\n') { scan++; }
|
||||
|
||||
while (*scan) {
|
||||
const char *sl = scan;
|
||||
|
||||
while (*sl == ' ' || *sl == '\t') { sl++; }
|
||||
|
||||
if (strncasecmp(sl, endTag, endTagLen) == 0) {
|
||||
while (*scan && *scan != '\n') { scan++; }
|
||||
if (*scan == '\n') { scan++; }
|
||||
break;
|
||||
}
|
||||
|
||||
while (*scan && *scan != '\n') { scan++; }
|
||||
if (*scan == '\n') { scan++; }
|
||||
}
|
||||
|
||||
if (isDuplicate) {
|
||||
// Leave it in the General section -- compiler will report the error
|
||||
while (*pos && *pos != '\n') {
|
||||
remaining[remPos++] = *pos++;
|
||||
}
|
||||
|
||||
if (*pos == '\n') {
|
||||
remaining[remPos++] = *pos++;
|
||||
}
|
||||
} else {
|
||||
// Extract this procedure into a new buffer
|
||||
int32_t procLen = (int32_t)(scan - lineStart);
|
||||
char *procBuf = (char *)malloc(procLen + 1);
|
||||
|
||||
if (procBuf) {
|
||||
memcpy(procBuf, lineStart, procLen);
|
||||
procBuf[procLen] = '\0';
|
||||
arrput(sProcBufs, procBuf);
|
||||
}
|
||||
|
||||
pos = scan;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy non-proc lines to remaining
|
||||
while (*pos && *pos != '\n') {
|
||||
remaining[remPos++] = *pos++;
|
||||
}
|
||||
|
||||
if (*pos == '\n') {
|
||||
remaining[remPos++] = *pos++;
|
||||
}
|
||||
}
|
||||
|
||||
remaining[remPos] = '\0';
|
||||
return remaining;
|
||||
}
|
||||
|
||||
|
||||
// saveCurProc -- save editor contents back to the current buffer.
|
||||
// If the user typed a new Sub/Function in the General section,
|
||||
// it's automatically extracted into its own procedure buffer.
|
||||
|
||||
static void saveCurProc(void) {
|
||||
if (!sEditor) {
|
||||
|
|
@ -3334,8 +3704,17 @@ static void saveCurProc(void) {
|
|||
}
|
||||
|
||||
if (sCurProcIdx == -1) {
|
||||
// General section -- check for embedded proc declarations
|
||||
char *cleaned = extractNewProcs(edText);
|
||||
|
||||
free(sGeneralBuf);
|
||||
sGeneralBuf = strdup(edText);
|
||||
sGeneralBuf = cleaned ? cleaned : strdup(edText);
|
||||
|
||||
if (cleaned) {
|
||||
// Update editor to show the cleaned General section
|
||||
wgtSetText(sEditor, sGeneralBuf);
|
||||
updateDropdowns();
|
||||
}
|
||||
} else if (sCurProcIdx >= 0 && sCurProcIdx < (int32_t)arrlen(sProcBufs)) {
|
||||
free(sProcBufs[sCurProcIdx]);
|
||||
sProcBufs[sCurProcIdx] = strdup(edText);
|
||||
|
|
@ -3560,7 +3939,20 @@ static void updateDropdowns(void) {
|
|||
lineNum++;
|
||||
}
|
||||
|
||||
// Build unique object names for the Object dropdown
|
||||
// Build object names for the Object dropdown.
|
||||
// Always include "(General)" and the form name (if editing a form).
|
||||
// Then add control names from the designer, plus any from existing procs.
|
||||
arrput(sObjItems, "(General)");
|
||||
|
||||
if (sDesigner.form) {
|
||||
arrput(sObjItems, sDesigner.form->name);
|
||||
|
||||
for (int32_t i = 0; i < (int32_t)arrlen(sDesigner.form->controls); i++) {
|
||||
arrput(sObjItems, sDesigner.form->controls[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
// Add any objects from existing procs not already in the list
|
||||
int32_t procCount = (int32_t)arrlen(sProcTable);
|
||||
|
||||
for (int32_t i = 0; i < procCount; i++) {
|
||||
|
|
|
|||
|
|
@ -105,6 +105,71 @@ bool basVmCallSub(BasVmT *vm, int32_t codeAddr) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// basVmCallSubWithArgs
|
||||
// ============================================================
|
||||
|
||||
bool basVmCallSubWithArgs(BasVmT *vm, int32_t codeAddr, const BasValueT *args, int32_t argCount) {
|
||||
if (!vm || !vm->module) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (codeAddr < 0 || codeAddr >= vm->module->codeLen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vm->callDepth >= BAS_VM_CALL_STACK_SIZE - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t savedPc = vm->pc;
|
||||
int32_t savedCallDepth = vm->callDepth;
|
||||
bool savedRunning = vm->running;
|
||||
|
||||
BasCallFrameT *frame = &vm->callStack[vm->callDepth++];
|
||||
frame->returnPc = savedPc;
|
||||
frame->localCount = BAS_VM_MAX_LOCALS;
|
||||
memset(frame->locals, 0, sizeof(frame->locals));
|
||||
|
||||
// Set arguments as locals (parameter 0 = local 0, etc.)
|
||||
for (int32_t i = 0; i < argCount && i < BAS_VM_MAX_LOCALS; i++) {
|
||||
frame->locals[i] = basValCopy(args[i]);
|
||||
}
|
||||
|
||||
vm->pc = codeAddr;
|
||||
vm->running = true;
|
||||
|
||||
int32_t steps = 0;
|
||||
|
||||
while (vm->running && vm->callDepth > savedCallDepth) {
|
||||
if (vm->stepLimit > 0 && steps >= vm->stepLimit) {
|
||||
vm->callDepth = savedCallDepth;
|
||||
vm->pc = savedPc;
|
||||
vm->running = savedRunning;
|
||||
return false;
|
||||
}
|
||||
|
||||
BasVmResultE result = basVmStep(vm);
|
||||
steps++;
|
||||
|
||||
if (result == BAS_VM_HALTED) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (result != BAS_VM_OK) {
|
||||
vm->pc = savedPc;
|
||||
vm->callDepth = savedCallDepth;
|
||||
vm->running = savedRunning;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
vm->pc = savedPc;
|
||||
vm->running = savedRunning;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// basVmCreate
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -361,5 +361,6 @@ const char *basVmGetError(const BasVmT *vm);
|
|||
// the previous execution state. Returns true if the SUB was called
|
||||
// and returned normally, false on error or if the VM was not idle.
|
||||
bool basVmCallSub(BasVmT *vm, int32_t codeAddr);
|
||||
bool basVmCallSubWithArgs(BasVmT *vm, int32_t codeAddr, const BasValueT *args, int32_t argCount);
|
||||
|
||||
#endif // DVXBASIC_VM_H
|
||||
|
|
|
|||
|
|
@ -1297,6 +1297,14 @@ static void dispatchEvents(AppContextT *ctx) {
|
|||
win = ctx->stack.windows[hitIdx];
|
||||
}
|
||||
|
||||
// Dispatch right-click to the widget system for MouseDown events
|
||||
if (win->onMouse) {
|
||||
int32_t relX = mx - win->x - win->contentX;
|
||||
int32_t relY = my - win->y - win->contentY;
|
||||
win->onMouse(win, relX, relY, buttons);
|
||||
}
|
||||
|
||||
// Then check for context menus
|
||||
MenuT *ctxMenu = NULL;
|
||||
|
||||
if (win->widgetRoot) {
|
||||
|
|
@ -1338,7 +1346,7 @@ static void dispatchEvents(AppContextT *ctx) {
|
|||
|
||||
// Mouse movement in content area -- send to focused window
|
||||
if ((mx != ctx->prevMouseX || my != ctx->prevMouseY) &&
|
||||
ctx->stack.focusedIdx >= 0 && (buttons & MOUSE_LEFT)) {
|
||||
ctx->stack.focusedIdx >= 0) {
|
||||
WindowT *win = ctx->stack.windows[ctx->stack.focusedIdx];
|
||||
|
||||
if (win->onMouse) {
|
||||
|
|
@ -1396,6 +1404,15 @@ static void dispatchEvents(AppContextT *ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
// Fire onScroll callback on the focused widget
|
||||
if (win->widgetRoot) {
|
||||
WidgetT *focus = wgtGetFocused();
|
||||
|
||||
if (focus && focus->onScroll) {
|
||||
focus->onScroll(focus, ctx->mouseWheel * ctx->wheelDirection);
|
||||
}
|
||||
}
|
||||
|
||||
ScrollbarT *sb = !consumed ? (win->vScroll ? win->vScroll : win->hScroll) : NULL;
|
||||
|
||||
if (sb) {
|
||||
|
|
|
|||
|
|
@ -264,6 +264,13 @@ typedef struct WidgetT {
|
|||
void (*onChange)(struct WidgetT *w);
|
||||
void (*onFocus)(struct WidgetT *w);
|
||||
void (*onBlur)(struct WidgetT *w);
|
||||
void (*onKeyPress)(struct WidgetT *w, int32_t keyAscii);
|
||||
void (*onKeyDown)(struct WidgetT *w, int32_t keyCode, int32_t shift);
|
||||
void (*onKeyUp)(struct WidgetT *w, int32_t keyCode, int32_t shift);
|
||||
void (*onMouseDown)(struct WidgetT *w, int32_t button, int32_t x, int32_t y);
|
||||
void (*onMouseUp)(struct WidgetT *w, int32_t button, int32_t x, int32_t y);
|
||||
void (*onMouseMove)(struct WidgetT *w, int32_t button, int32_t x, int32_t y);
|
||||
void (*onScroll)(struct WidgetT *w, int32_t delta);
|
||||
|
||||
} WidgetT;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@
|
|||
// button again and re-open the popup in the same event.
|
||||
WidgetT *sClosedPopup = NULL;
|
||||
|
||||
// Mouse state for tracking button transitions and movement
|
||||
static int32_t sPrevMouseButtons = 0;
|
||||
static int32_t sPrevMouseX = -1;
|
||||
static int32_t sPrevMouseY = -1;
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetManageScrollbars
|
||||
|
|
@ -179,6 +184,15 @@ void widgetOnKey(WindowT *win, int32_t key, int32_t mod) {
|
|||
|
||||
wclsOnKey(focus, key, mod);
|
||||
|
||||
// Fire user callbacks after the widget's internal handler
|
||||
if (key >= 32 && key < 127 && focus->onKeyPress) {
|
||||
focus->onKeyPress(focus, key);
|
||||
}
|
||||
|
||||
if (focus->onKeyDown) {
|
||||
focus->onKeyDown(focus, key, mod);
|
||||
}
|
||||
|
||||
ctx->currentAppId = prevAppId;
|
||||
}
|
||||
|
||||
|
|
@ -351,6 +365,61 @@ static void widgetOnMouseInner(WindowT *win, WidgetT *root, int32_t x, int32_t y
|
|||
}
|
||||
}
|
||||
|
||||
// Fire mouse event callbacks
|
||||
if (hit->enabled) {
|
||||
int32_t relX = vx - hit->x;
|
||||
int32_t relY = vy - hit->y;
|
||||
|
||||
// MouseDown: button just pressed
|
||||
if ((buttons & MOUSE_LEFT) && !(sPrevMouseButtons & MOUSE_LEFT)) {
|
||||
if (hit->onMouseDown) {
|
||||
hit->onMouseDown(hit, 1, relX, relY);
|
||||
}
|
||||
}
|
||||
|
||||
if ((buttons & MOUSE_RIGHT) && !(sPrevMouseButtons & MOUSE_RIGHT)) {
|
||||
if (hit->onMouseDown) {
|
||||
hit->onMouseDown(hit, 2, relX, relY);
|
||||
}
|
||||
}
|
||||
|
||||
if ((buttons & MOUSE_MIDDLE) && !(sPrevMouseButtons & MOUSE_MIDDLE)) {
|
||||
if (hit->onMouseDown) {
|
||||
hit->onMouseDown(hit, 3, relX, relY);
|
||||
}
|
||||
}
|
||||
|
||||
// MouseUp: button just released
|
||||
if (!(buttons & MOUSE_LEFT) && (sPrevMouseButtons & MOUSE_LEFT)) {
|
||||
if (hit->onMouseUp) {
|
||||
hit->onMouseUp(hit, 1, relX, relY);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(buttons & MOUSE_RIGHT) && (sPrevMouseButtons & MOUSE_RIGHT)) {
|
||||
if (hit->onMouseUp) {
|
||||
hit->onMouseUp(hit, 2, relX, relY);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(buttons & MOUSE_MIDDLE) && (sPrevMouseButtons & MOUSE_MIDDLE)) {
|
||||
if (hit->onMouseUp) {
|
||||
hit->onMouseUp(hit, 3, relX, relY);
|
||||
}
|
||||
}
|
||||
|
||||
// MouseMove: position changed
|
||||
if (vx != sPrevMouseX || vy != sPrevMouseY) {
|
||||
if (hit->onMouseMove) {
|
||||
hit->onMouseMove(hit, buttons, relX, relY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sPrevMouseButtons = buttons;
|
||||
sPrevMouseX = vx;
|
||||
sPrevMouseY = vy;
|
||||
|
||||
// Update the cached focus pointer for O(1) access in widgetOnKey
|
||||
if (hit->focused) {
|
||||
sFocusedWidget = hit;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue