IDE fixes.
This commit is contained in:
parent
51ade9f119
commit
5171753250
2 changed files with 111 additions and 155 deletions
|
|
@ -13,10 +13,10 @@
|
|||
#include "dvxWm.h"
|
||||
#include "shellApp.h"
|
||||
#include "widgetBox.h"
|
||||
#include "widgetButton.h"
|
||||
#include "widgetLabel.h"
|
||||
#include "widgetTextInput.h"
|
||||
#include "widgetDropdown.h"
|
||||
#include "widgetSplitter.h"
|
||||
#include "widgetStatusBar.h"
|
||||
|
||||
#include "../compiler/parser.h"
|
||||
|
|
@ -24,6 +24,8 @@
|
|||
#include "../runtime/vm.h"
|
||||
#include "../runtime/values.h"
|
||||
|
||||
#include "stb_ds_wrap.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -35,10 +37,10 @@
|
|||
// Constants
|
||||
// ============================================================
|
||||
|
||||
#define IDE_WIN_W 560
|
||||
#define IDE_WIN_H 400
|
||||
#define IDE_BTN_W 80
|
||||
#define IDE_BTN_SPACING 8
|
||||
#define IDE_WIN_X 10
|
||||
#define IDE_WIN_Y 10
|
||||
#define IDE_WIN_W 620
|
||||
#define IDE_WIN_H 460
|
||||
#define IDE_MAX_SOURCE 65536
|
||||
#define IDE_MAX_OUTPUT 32768
|
||||
#define IDE_STEP_SLICE 10000 // VM steps per slice before yielding to DVX
|
||||
|
|
@ -50,8 +52,6 @@
|
|||
#define CMD_CLEAR 103
|
||||
#define CMD_EXIT 104
|
||||
#define CMD_RUN_NOCMP 105
|
||||
|
||||
#define IDE_MAX_PROCS 128
|
||||
#define IDE_MAX_IMM 1024
|
||||
|
||||
// ============================================================
|
||||
|
|
@ -65,10 +65,6 @@ static void compileAndRun(void);
|
|||
static void loadFile(void);
|
||||
static void onClose(WindowT *win);
|
||||
static void onMenu(WindowT *win, int32_t menuId);
|
||||
static void onOpenClick(WidgetT *w);
|
||||
static void onRunClick(WidgetT *w);
|
||||
static void onStopClick(WidgetT *w);
|
||||
static void onClearClick(WidgetT *w);
|
||||
static void basicColorize(const char *line, int32_t lineLen, uint8_t *colors, void *ctx);
|
||||
static void evaluateImmediate(const char *expr);
|
||||
static void loadFrmFiles(BasFormRtT *rt);
|
||||
|
|
@ -111,8 +107,9 @@ typedef struct {
|
|||
int32_t lineNum;
|
||||
} IdeProcEntryT;
|
||||
|
||||
static IdeProcEntryT sProcTable[IDE_MAX_PROCS];
|
||||
static int32_t sProcCount = 0;
|
||||
static IdeProcEntryT *sProcTable = NULL; // stb_ds dynamic array
|
||||
static const char **sObjItems = NULL; // stb_ds dynamic array
|
||||
static const char **sEvtItems = NULL; // stb_ds dynamic array
|
||||
|
||||
// ============================================================
|
||||
// App descriptor
|
||||
|
|
@ -151,10 +148,7 @@ int32_t appMain(DxeAppContextT *ctx) {
|
|||
// ============================================================
|
||||
|
||||
static void buildWindow(void) {
|
||||
int32_t winX = (sAc->display.width - IDE_WIN_W) / 2;
|
||||
int32_t winY = (sAc->display.height - IDE_WIN_H) / 4;
|
||||
|
||||
sWin = dvxCreateWindow(sAc, "DVX BASIC", winX, winY, IDE_WIN_W, IDE_WIN_H, true);
|
||||
sWin = dvxCreateWindow(sAc, "DVX BASIC", IDE_WIN_X, IDE_WIN_Y, IDE_WIN_W, IDE_WIN_H, true);
|
||||
|
||||
if (!sWin) {
|
||||
return;
|
||||
|
|
@ -187,9 +181,12 @@ static void buildWindow(void) {
|
|||
// Widget tree
|
||||
WidgetT *root = wgtInitWindow(sAc, sWin);
|
||||
|
||||
// Source code editor (top half)
|
||||
WidgetT *editorFrame = wgtFrame(root, "Source");
|
||||
editorFrame->weight = 100;
|
||||
// Horizontal splitter: editor on top, output+immediate on bottom
|
||||
WidgetT *splitter = wgtSplitter(root, false);
|
||||
splitter->weight = 100;
|
||||
|
||||
// Top pane: source code editor
|
||||
WidgetT *editorFrame = wgtFrame(splitter, "Source");
|
||||
|
||||
// Object/Event dropdown row
|
||||
WidgetT *dropdownRow = wgtHBox(editorFrame);
|
||||
|
|
@ -198,10 +195,12 @@ static void buildWindow(void) {
|
|||
sObjDropdown = wgtDropdown(dropdownRow);
|
||||
sObjDropdown->weight = 100;
|
||||
sObjDropdown->onChange = onObjDropdownChange;
|
||||
wgtDropdownSetItems(sObjDropdown, NULL, 0);
|
||||
|
||||
sEvtDropdown = wgtDropdown(dropdownRow);
|
||||
sEvtDropdown->weight = 100;
|
||||
sEvtDropdown->onChange = onEvtDropdownChange;
|
||||
wgtDropdownSetItems(sEvtDropdown, NULL, 0);
|
||||
|
||||
sEditor = wgtTextArea(editorFrame, IDE_MAX_SOURCE);
|
||||
sEditor->weight = 100;
|
||||
|
|
@ -209,35 +208,16 @@ static void buildWindow(void) {
|
|||
wgtTextAreaSetShowLineNumbers(sEditor, true);
|
||||
wgtTextAreaSetAutoIndent(sEditor, true);
|
||||
|
||||
// Button bar
|
||||
WidgetT *btnRow = wgtHBox(root);
|
||||
btnRow->spacing = wgtPixels(IDE_BTN_SPACING);
|
||||
// Bottom pane: output + immediate
|
||||
WidgetT *bottomPane = wgtVBox(splitter);
|
||||
|
||||
WidgetT *openBtn = wgtButton(btnRow, "&Open...");
|
||||
openBtn->onClick = onOpenClick;
|
||||
openBtn->prefW = wgtPixels(IDE_BTN_W);
|
||||
|
||||
WidgetT *runBtn = wgtButton(btnRow, "&Run");
|
||||
runBtn->onClick = onRunClick;
|
||||
runBtn->prefW = wgtPixels(IDE_BTN_W);
|
||||
|
||||
WidgetT *stopBtn = wgtButton(btnRow, "&Stop");
|
||||
stopBtn->onClick = onStopClick;
|
||||
stopBtn->prefW = wgtPixels(IDE_BTN_W);
|
||||
|
||||
WidgetT *clearBtn = wgtButton(btnRow, "&Clear");
|
||||
clearBtn->onClick = onClearClick;
|
||||
clearBtn->prefW = wgtPixels(IDE_BTN_W);
|
||||
|
||||
// Output area
|
||||
WidgetT *outputFrame = wgtFrame(root, "Output");
|
||||
outputFrame->weight = 80;
|
||||
WidgetT *outputFrame = wgtFrame(bottomPane, "Output");
|
||||
outputFrame->weight = 70;
|
||||
sOutput = wgtTextArea(outputFrame, IDE_MAX_OUTPUT);
|
||||
sOutput->weight = 100;
|
||||
sOutput->readOnly = true;
|
||||
|
||||
// Immediate window
|
||||
WidgetT *immFrame = wgtFrame(root, "Immediate");
|
||||
WidgetT *immFrame = wgtFrame(bottomPane, "Immediate");
|
||||
immFrame->weight = 30;
|
||||
sImmediate = wgtTextArea(immFrame, IDE_MAX_IMM);
|
||||
sImmediate->weight = 100;
|
||||
|
|
@ -248,7 +228,6 @@ static void buildWindow(void) {
|
|||
sStatus = wgtLabel(statusBar, "");
|
||||
sStatus->weight = 100;
|
||||
|
||||
dvxFitWindow(sAc, sWin);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
|
@ -748,6 +727,7 @@ static void loadFile(void) {
|
|||
snprintf(title, sizeof(title), "DVX BASIC - %s", path);
|
||||
dvxSetTitle(sAc, sWin, title);
|
||||
|
||||
updateDropdowns();
|
||||
setStatus("File loaded.");
|
||||
}
|
||||
}
|
||||
|
|
@ -817,15 +797,7 @@ static void loadFrmFiles(BasFormRtT *rt) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onClearClick
|
||||
// ============================================================
|
||||
|
||||
static void onClearClick(WidgetT *w) {
|
||||
(void)w;
|
||||
clearOutput();
|
||||
setStatus("Output cleared.");
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// onClose
|
||||
|
|
@ -845,6 +817,13 @@ static void onClose(WindowT *win) {
|
|||
sCachedModule = NULL;
|
||||
}
|
||||
|
||||
arrfree(sProcTable);
|
||||
arrfree(sObjItems);
|
||||
arrfree(sEvtItems);
|
||||
sProcTable = NULL;
|
||||
sObjItems = NULL;
|
||||
sEvtItems = NULL;
|
||||
|
||||
dvxDestroyWindow(sAc, win);
|
||||
}
|
||||
|
||||
|
|
@ -903,28 +882,25 @@ static void onEvtDropdownChange(WidgetT *w) {
|
|||
int32_t objIdx = wgtDropdownGetSelected(sObjDropdown);
|
||||
int32_t evtIdx = wgtDropdownGetSelected(sEvtDropdown);
|
||||
|
||||
if (objIdx < 0 || evtIdx < 0) {
|
||||
if (objIdx < 0 || objIdx >= (int32_t)arrlen(sObjItems) || evtIdx < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the object name from the dropdown
|
||||
// Dropdown items are set in updateDropdowns; find matching proc
|
||||
const char *cur = wgtGetText(sObjDropdown);
|
||||
const char *selObj = sObjItems[objIdx];
|
||||
|
||||
if (!cur) {
|
||||
return;
|
||||
}
|
||||
// Find the proc matching the selected object + event
|
||||
int32_t matchIdx = 0;
|
||||
int32_t procCount = (int32_t)arrlen(sProcTable);
|
||||
|
||||
// Find the proc entry matching the selected object + event indices
|
||||
int32_t matchIdx = 0;
|
||||
for (int32_t i = 0; i < procCount; i++) {
|
||||
if (strcasecmp(sProcTable[i].objName, selObj) == 0) {
|
||||
if (matchIdx == evtIdx) {
|
||||
wgtTextAreaGoToLine(sEditor, sProcTable[i].lineNum);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < sProcCount; i++) {
|
||||
if (matchIdx == evtIdx) {
|
||||
wgtTextAreaGoToLine(sEditor, sProcTable[i].lineNum);
|
||||
return;
|
||||
matchIdx++;
|
||||
}
|
||||
|
||||
matchIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -996,47 +972,25 @@ static void onObjDropdownChange(WidgetT *w) {
|
|||
|
||||
int32_t objIdx = wgtDropdownGetSelected(sObjDropdown);
|
||||
|
||||
if (objIdx < 0) {
|
||||
if (objIdx < 0 || objIdx >= (int32_t)arrlen(sObjItems)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Collect unique object names to find which one is selected
|
||||
char objNames[IDE_MAX_PROCS][64];
|
||||
int32_t objCount = 0;
|
||||
|
||||
for (int32_t i = 0; i < sProcCount; i++) {
|
||||
bool found = false;
|
||||
|
||||
for (int32_t j = 0; j < objCount; j++) {
|
||||
if (strcasecmp(objNames[j], sProcTable[i].objName) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && objCount < IDE_MAX_PROCS) {
|
||||
memcpy(objNames[objCount], sProcTable[i].objName, 64);
|
||||
objCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (objIdx >= objCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *selObj = objNames[objIdx];
|
||||
const char *selObj = sObjItems[objIdx];
|
||||
|
||||
// Build event list for the selected object
|
||||
const char *evtItems[IDE_MAX_PROCS];
|
||||
int32_t evtCount = 0;
|
||||
arrsetlen(sEvtItems, 0);
|
||||
|
||||
for (int32_t i = 0; i < sProcCount; i++) {
|
||||
if (strcasecmp(sProcTable[i].objName, selObj) == 0 && evtCount < IDE_MAX_PROCS) {
|
||||
evtItems[evtCount++] = sProcTable[i].evtName;
|
||||
int32_t procCount = (int32_t)arrlen(sProcTable);
|
||||
|
||||
for (int32_t i = 0; i < procCount; i++) {
|
||||
if (strcasecmp(sProcTable[i].objName, selObj) == 0) {
|
||||
arrput(sEvtItems, sProcTable[i].evtName);
|
||||
}
|
||||
}
|
||||
|
||||
wgtDropdownSetItems(sEvtDropdown, evtItems, evtCount);
|
||||
int32_t evtCount = (int32_t)arrlen(sEvtItems);
|
||||
wgtDropdownSetItems(sEvtDropdown, sEvtItems, evtCount);
|
||||
|
||||
if (evtCount > 0) {
|
||||
wgtDropdownSetSelected(sEvtDropdown, 0);
|
||||
|
|
@ -1048,35 +1002,6 @@ static void onObjDropdownChange(WidgetT *w) {
|
|||
// onOpenClick
|
||||
// ============================================================
|
||||
|
||||
static void onOpenClick(WidgetT *w) {
|
||||
(void)w;
|
||||
loadFile();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// onRunClick
|
||||
// ============================================================
|
||||
|
||||
static void onRunClick(WidgetT *w) {
|
||||
(void)w;
|
||||
compileAndRun();
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onStopClick
|
||||
// ============================================================
|
||||
|
||||
static void onStopClick(WidgetT *w) {
|
||||
(void)w;
|
||||
|
||||
if (sVm) {
|
||||
sVm->running = false;
|
||||
setStatus("Program stopped.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// printCallback
|
||||
// ============================================================
|
||||
|
|
@ -1128,7 +1053,10 @@ static void setStatus(const char *text) {
|
|||
// '_' into ObjectName and EventName (e.g. "Command1_Click").
|
||||
|
||||
static void updateDropdowns(void) {
|
||||
sProcCount = 0;
|
||||
// Reset dynamic arrays
|
||||
arrsetlen(sProcTable, 0);
|
||||
arrsetlen(sObjItems, 0);
|
||||
arrsetlen(sEvtItems, 0);
|
||||
|
||||
if (!sEditor || !sObjDropdown || !sEvtDropdown) {
|
||||
return;
|
||||
|
|
@ -1154,7 +1082,7 @@ static void updateDropdowns(void) {
|
|||
bool isSub = (strncasecmp(pos, "SUB ", 4) == 0);
|
||||
bool isFunc = (strncasecmp(pos, "FUNCTION ", 9) == 0);
|
||||
|
||||
if ((isSub || isFunc) && sProcCount < IDE_MAX_PROCS) {
|
||||
if (isSub || isFunc) {
|
||||
pos += isSub ? 4 : 9;
|
||||
|
||||
// Skip whitespace after keyword
|
||||
|
|
@ -1173,6 +1101,10 @@ static void updateDropdowns(void) {
|
|||
procName[nameLen] = '\0';
|
||||
|
||||
// Split on '_' into object + event
|
||||
IdeProcEntryT entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.lineNum = lineNum;
|
||||
|
||||
char *underscore = strchr(procName, '_');
|
||||
|
||||
if (underscore) {
|
||||
|
|
@ -1182,16 +1114,15 @@ static void updateDropdowns(void) {
|
|||
objLen = 63;
|
||||
}
|
||||
|
||||
memcpy(sProcTable[sProcCount].objName, procName, objLen);
|
||||
sProcTable[sProcCount].objName[objLen] = '\0';
|
||||
snprintf(sProcTable[sProcCount].evtName, sizeof(sProcTable[sProcCount].evtName), "%s", underscore + 1);
|
||||
memcpy(entry.objName, procName, objLen);
|
||||
entry.objName[objLen] = '\0';
|
||||
snprintf(entry.evtName, sizeof(entry.evtName), "%s", underscore + 1);
|
||||
} else {
|
||||
snprintf(sProcTable[sProcCount].objName, sizeof(sProcTable[sProcCount].objName), "%s", "(General)");
|
||||
snprintf(sProcTable[sProcCount].evtName, sizeof(sProcTable[sProcCount].evtName), "%s", procName);
|
||||
snprintf(entry.objName, sizeof(entry.objName), "%s", "(General)");
|
||||
snprintf(entry.evtName, sizeof(entry.evtName), "%s", procName);
|
||||
}
|
||||
|
||||
sProcTable[sProcCount].lineNum = lineNum;
|
||||
sProcCount++;
|
||||
arrput(sProcTable, entry);
|
||||
}
|
||||
|
||||
// Advance to end of line
|
||||
|
|
@ -1207,28 +1138,31 @@ static void updateDropdowns(void) {
|
|||
}
|
||||
|
||||
// Build unique object names for the Object dropdown
|
||||
const char *objItems[IDE_MAX_PROCS];
|
||||
int32_t objCount = 0;
|
||||
int32_t procCount = (int32_t)arrlen(sProcTable);
|
||||
|
||||
for (int32_t i = 0; i < sProcCount; i++) {
|
||||
for (int32_t i = 0; i < procCount; i++) {
|
||||
bool found = false;
|
||||
int32_t objCount = (int32_t)arrlen(sObjItems);
|
||||
|
||||
for (int32_t j = 0; j < objCount; j++) {
|
||||
if (strcasecmp(objItems[j], sProcTable[i].objName) == 0) {
|
||||
if (strcasecmp(sObjItems[j], sProcTable[i].objName) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && objCount < IDE_MAX_PROCS) {
|
||||
objItems[objCount++] = sProcTable[i].objName;
|
||||
if (!found) {
|
||||
arrput(sObjItems, sProcTable[i].objName);
|
||||
}
|
||||
}
|
||||
|
||||
wgtDropdownSetItems(sObjDropdown, objItems, objCount);
|
||||
int32_t objCount = (int32_t)arrlen(sObjItems);
|
||||
wgtDropdownSetItems(sObjDropdown, sObjItems, objCount);
|
||||
|
||||
if (objCount > 0) {
|
||||
wgtDropdownSetSelected(sObjDropdown, 0);
|
||||
onObjDropdownChange(sObjDropdown);
|
||||
} else {
|
||||
wgtDropdownSetItems(sEvtDropdown, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2516,19 +2516,41 @@ void wgtTextAreaGoToLine(WidgetT *w, int32_t line) {
|
|||
ta->selAnchor = lineStart;
|
||||
ta->selCursor = lineStart + lineL;
|
||||
|
||||
// Scroll into view
|
||||
// Scroll to put the target line near the top of the visible area
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
const BitmapFontT *font = &ctx->font;
|
||||
int32_t gutterW = textAreaGutterWidth(w, font);
|
||||
int32_t innerW = w->w - TEXTAREA_BORDER * 2 - TEXTAREA_PAD * 2 - TEXTAREA_SB_W - gutterW;
|
||||
int32_t visCols = innerW / font->charWidth;
|
||||
int32_t innerH = w->h - TEXTAREA_BORDER * 2;
|
||||
int32_t visRows = innerH / font->charHeight;
|
||||
|
||||
if (visCols < 1) { visCols = 1; }
|
||||
if (visRows < 1) { visRows = 1; }
|
||||
if (ctx) {
|
||||
const BitmapFontT *font = &ctx->font;
|
||||
int32_t gutterW = textAreaGutterWidth(w, font);
|
||||
int32_t innerW = w->w - TEXTAREA_BORDER * 2 - TEXTAREA_PAD * 2 - TEXTAREA_SB_W - gutterW;
|
||||
int32_t visCols = innerW / font->charWidth;
|
||||
int32_t innerH = w->h - TEXTAREA_BORDER * 2;
|
||||
int32_t visRows = innerH / font->charHeight;
|
||||
|
||||
if (visCols < 1) { visCols = 1; }
|
||||
if (visRows < 1) { visRows = 1; }
|
||||
|
||||
// Place target line 1/4 from top for context, then clamp
|
||||
int32_t targetScroll = row - visRows / 4;
|
||||
|
||||
if (targetScroll < 0) {
|
||||
targetScroll = 0;
|
||||
}
|
||||
|
||||
int32_t maxScroll = totalLines - visRows;
|
||||
|
||||
if (maxScroll < 0) {
|
||||
maxScroll = 0;
|
||||
}
|
||||
|
||||
if (targetScroll > maxScroll) {
|
||||
targetScroll = maxScroll;
|
||||
}
|
||||
|
||||
ta->scrollRow = targetScroll;
|
||||
ta->scrollCol = 0;
|
||||
}
|
||||
|
||||
textAreaEnsureVisible(w, visRows, visCols);
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue