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 onWidgetClick(WidgetT *w);
|
||||||
static void onWidgetDblClick(WidgetT *w);
|
static void onWidgetDblClick(WidgetT *w);
|
||||||
static void onWidgetFocus(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 parseFrmLine(const char *line, char *key, char *value);
|
||||||
static void rebuildListBoxItems(BasControlT *ctrl);
|
static void rebuildListBoxItems(BasControlT *ctrl);
|
||||||
static const char *resolveTypeName(const char *typeName);
|
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) {
|
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) {
|
if (!rt || !form || !rt->vm || !rt->module) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -354,7 +364,13 @@ bool basFormRtFireEvent(BasFormRtT *rt, BasFormT *form, const char *ctrlName, co
|
||||||
return false;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,7 +378,13 @@ bool basFormRtFireEvent(BasFormRtT *rt, BasFormT *form, const char *ctrlName, co
|
||||||
rt->currentForm = form;
|
rt->currentForm = form;
|
||||||
basVmSetCurrentForm(rt->vm, 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;
|
rt->currentForm = prevForm;
|
||||||
basVmSetCurrentForm(rt->vm, prevForm);
|
basVmSetCurrentForm(rt->vm, prevForm);
|
||||||
|
|
@ -652,6 +674,12 @@ BasFormT *basFormRtLoadFrm(BasFormRtT *rt, const char *source, int32_t sourceLen
|
||||||
widget->onChange = onWidgetChange;
|
widget->onChange = onWidgetChange;
|
||||||
widget->onFocus = onWidgetFocus;
|
widget->onFocus = onWidgetFocus;
|
||||||
widget->onBlur = onWidgetBlur;
|
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
|
// 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
|
// parseFrmLine
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ int32_t basFormRtMsgBox(void *ctx, const char *message, int32_t flags);
|
||||||
// ---- Event dispatch ----
|
// ---- Event dispatch ----
|
||||||
|
|
||||||
bool basFormRtFireEvent(BasFormRtT *rt, BasFormT *form, const char *ctrlName, const char *eventName);
|
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 ----
|
// ---- Form file loading ----
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2432,24 +2432,64 @@ static void onEvtDropdownChange(WidgetT *w) {
|
||||||
int32_t objIdx = wgtDropdownGetSelected(sObjDropdown);
|
int32_t objIdx = wgtDropdownGetSelected(sObjDropdown);
|
||||||
int32_t evtIdx = wgtDropdownGetSelected(sEvtDropdown);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *selObj = sObjItems[objIdx];
|
const char *selObj = sObjItems[objIdx];
|
||||||
|
const char *selEvt = sEvtItems[evtIdx];
|
||||||
|
|
||||||
// Find the proc matching the selected object + event
|
// Strip brackets if present (unimplemented event)
|
||||||
int32_t matchIdx = 0;
|
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);
|
int32_t procCount = (int32_t)arrlen(sProcTable);
|
||||||
|
|
||||||
for (int32_t i = 0; i < procCount; i++) {
|
for (int32_t i = 0; i < procCount; i++) {
|
||||||
if (strcasecmp(sProcTable[i].objName, selObj) == 0) {
|
if (strcasecmp(sProcTable[i].objName, selObj) == 0 &&
|
||||||
if (matchIdx == evtIdx) {
|
strcasecmp(sProcTable[i].evtName, evtName) == 0) {
|
||||||
showProc(i);
|
showProc(i);
|
||||||
return;
|
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.
|
// 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) {
|
static void onObjDropdownChange(WidgetT *w) {
|
||||||
(void)w;
|
(void)w;
|
||||||
|
|
||||||
|
|
@ -2528,17 +2587,157 @@ static void onObjDropdownChange(WidgetT *w) {
|
||||||
|
|
||||||
const char *selObj = sObjItems[objIdx];
|
const char *selObj = sObjItems[objIdx];
|
||||||
|
|
||||||
// Build event list for the selected object
|
// Collect which events already have code
|
||||||
arrsetlen(sEvtItems, 0);
|
arrsetlen(sEvtItems, 0);
|
||||||
|
|
||||||
int32_t procCount = (int32_t)arrlen(sProcTable);
|
int32_t procCount = (int32_t)arrlen(sProcTable);
|
||||||
|
const char **existingEvts = NULL; // stb_ds temp array
|
||||||
|
|
||||||
for (int32_t i = 0; i < procCount; i++) {
|
for (int32_t i = 0; i < procCount; i++) {
|
||||||
if (strcasecmp(sProcTable[i].objName, selObj) == 0) {
|
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);
|
int32_t evtCount = (int32_t)arrlen(sEvtItems);
|
||||||
wgtDropdownSetItems(sEvtDropdown, sEvtItems, evtCount);
|
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) {
|
static void saveCurProc(void) {
|
||||||
if (!sEditor) {
|
if (!sEditor) {
|
||||||
|
|
@ -3334,8 +3704,17 @@ static void saveCurProc(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sCurProcIdx == -1) {
|
if (sCurProcIdx == -1) {
|
||||||
|
// General section -- check for embedded proc declarations
|
||||||
|
char *cleaned = extractNewProcs(edText);
|
||||||
|
|
||||||
free(sGeneralBuf);
|
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)) {
|
} else if (sCurProcIdx >= 0 && sCurProcIdx < (int32_t)arrlen(sProcBufs)) {
|
||||||
free(sProcBufs[sCurProcIdx]);
|
free(sProcBufs[sCurProcIdx]);
|
||||||
sProcBufs[sCurProcIdx] = strdup(edText);
|
sProcBufs[sCurProcIdx] = strdup(edText);
|
||||||
|
|
@ -3560,7 +3939,20 @@ static void updateDropdowns(void) {
|
||||||
lineNum++;
|
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);
|
int32_t procCount = (int32_t)arrlen(sProcTable);
|
||||||
|
|
||||||
for (int32_t i = 0; i < procCount; i++) {
|
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
|
// basVmCreate
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -361,5 +361,6 @@ const char *basVmGetError(const BasVmT *vm);
|
||||||
// the previous execution state. Returns true if the SUB was called
|
// the previous execution state. Returns true if the SUB was called
|
||||||
// and returned normally, false on error or if the VM was not idle.
|
// and returned normally, false on error or if the VM was not idle.
|
||||||
bool basVmCallSub(BasVmT *vm, int32_t codeAddr);
|
bool basVmCallSub(BasVmT *vm, int32_t codeAddr);
|
||||||
|
bool basVmCallSubWithArgs(BasVmT *vm, int32_t codeAddr, const BasValueT *args, int32_t argCount);
|
||||||
|
|
||||||
#endif // DVXBASIC_VM_H
|
#endif // DVXBASIC_VM_H
|
||||||
|
|
|
||||||
|
|
@ -1297,6 +1297,14 @@ static void dispatchEvents(AppContextT *ctx) {
|
||||||
win = ctx->stack.windows[hitIdx];
|
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;
|
MenuT *ctxMenu = NULL;
|
||||||
|
|
||||||
if (win->widgetRoot) {
|
if (win->widgetRoot) {
|
||||||
|
|
@ -1338,7 +1346,7 @@ static void dispatchEvents(AppContextT *ctx) {
|
||||||
|
|
||||||
// Mouse movement in content area -- send to focused window
|
// Mouse movement in content area -- send to focused window
|
||||||
if ((mx != ctx->prevMouseX || my != ctx->prevMouseY) &&
|
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];
|
WindowT *win = ctx->stack.windows[ctx->stack.focusedIdx];
|
||||||
|
|
||||||
if (win->onMouse) {
|
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;
|
ScrollbarT *sb = !consumed ? (win->vScroll ? win->vScroll : win->hScroll) : NULL;
|
||||||
|
|
||||||
if (sb) {
|
if (sb) {
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,13 @@ typedef struct WidgetT {
|
||||||
void (*onChange)(struct WidgetT *w);
|
void (*onChange)(struct WidgetT *w);
|
||||||
void (*onFocus)(struct WidgetT *w);
|
void (*onFocus)(struct WidgetT *w);
|
||||||
void (*onBlur)(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;
|
} WidgetT;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,11 @@
|
||||||
// button again and re-open the popup in the same event.
|
// button again and re-open the popup in the same event.
|
||||||
WidgetT *sClosedPopup = NULL;
|
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
|
// widgetManageScrollbars
|
||||||
|
|
@ -179,6 +184,15 @@ void widgetOnKey(WindowT *win, int32_t key, int32_t mod) {
|
||||||
|
|
||||||
wclsOnKey(focus, key, 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;
|
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
|
// Update the cached focus pointer for O(1) access in widgetOnKey
|
||||||
if (hit->focused) {
|
if (hit->focused) {
|
||||||
sFocusedWidget = hit;
|
sFocusedWidget = hit;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue