// dvxdemo.c — DVX GUI demonstration app (DXE version) // // Callback-only DXE app (hasMainLoop = false) that opens several windows // showcasing the DVX widget system, paint callbacks, menus, accelerators, etc. // // This serves as a comprehensive widget catalog and integration test: // - setupMainWindow: raw onPaint callbacks (text, gradient, pattern) // - setupWidgetDemo: form widgets (input, checkbox, radio, listbox) // - setupControlsWindow: advanced widgets in a TabControl (dropdown, // progress, slider, spinner, tree, listview, // scrollpane, toolbar, canvas, splitter, image) // - setupTerminalWindow: ANSI terminal emulator widget // // Each window is independent; closing one doesn't affect the others. // The app has no persistent state — it's purely a showcase. #include "dvxApp.h" #include "dvxDialog.h" #include "dvxWidget.h" #include "dvxWm.h" #include "shellApp.h" #include #include #include #include #include // ============================================================ // Menu command IDs // ============================================================ #define CMD_FILE_NEW 100 #define CMD_FILE_OPEN 101 #define CMD_FILE_SAVE 102 #define CMD_FILE_EXIT 103 #define CMD_EDIT_CUT 200 #define CMD_EDIT_COPY 201 #define CMD_EDIT_PASTE 202 #define CMD_VIEW_TERM 300 #define CMD_VIEW_CTRL 301 #define CMD_VIEW_ZOOM_IN 302 #define CMD_VIEW_ZOOM_OUT 303 #define CMD_VIEW_ZOOM_FIT 304 #define CMD_VIEW_TOOLBAR 305 #define CMD_VIEW_STATUSBAR 306 #define CMD_VIEW_SIZE_SMALL 307 #define CMD_VIEW_SIZE_MED 308 #define CMD_VIEW_SIZE_LARGE 309 #define CMD_VIEW_DEBUG_LYT 310 #define CMD_WIN_CASCADE 350 #define CMD_WIN_TILE_H 351 #define CMD_WIN_TILE_V 352 #define CMD_WIN_TILE 353 #define CMD_HELP_ABOUT 400 #define CMD_CTX_CUT 500 #define CMD_CTX_COPY 501 #define CMD_CTX_PASTE 502 #define CMD_CTX_DELETE 503 #define CMD_CTX_SELALL 504 #define CMD_CTX_PROPS 505 // ============================================================ // Prototypes // ============================================================ int32_t appMain(DxeAppContextT *ctx); static void onCanvasDraw(WidgetT *w, int32_t cx, int32_t cy, bool drag); static void onCloseCb(WindowT *win); static void onCloseMainCb(WindowT *win); static void onMenuCb(WindowT *win, int32_t menuId); static void onOkClick(WidgetT *w); static void onPaintColor(WindowT *win, RectT *dirtyArea); static void onPaintPattern(WindowT *win, RectT *dirtyArea); static void onPaintText(WindowT *win, RectT *dirtyArea); static void onToolbarClick(WidgetT *w); static void setupControlsWindow(void); static void setupMainWindow(void); static void setupTerminalWindow(void); static void setupWidgetDemo(void); // ============================================================ // Module state // ============================================================ static AppContextT *sAc = NULL; static DxeAppContextT *sDxeCtx = NULL; // ============================================================ // App descriptor // ============================================================ // Callback-only: all windows use either widget trees (which handle their own // input) or onPaint callbacks (which are invoked by the compositor). No need // for a dedicated task since there's no ongoing computation. AppDescriptorT appDescriptor = { .name = "DVX Demo", .hasMainLoop = false, .stackSize = SHELL_STACK_DEFAULT, .priority = TS_PRIORITY_NORMAL }; // ============================================================ // Callbacks // ============================================================ static void onCanvasDraw(WidgetT *w, int32_t cx, int32_t cy, bool drag) { if (drag && w->as.canvas.lastX >= 0) { wgtCanvasDrawLine(w, w->as.canvas.lastX, w->as.canvas.lastY, cx, cy); } else { wgtCanvasDrawLine(w, cx, cy, cx, cy); } } static void onCloseCb(WindowT *win) { dvxDestroyWindow(sAc, win); } static void onCloseMainCb(WindowT *win) { int32_t result = dvxMessageBox(sAc, "Close", "Close this window?", MB_YESNO | MB_ICONQUESTION); if (result == ID_YES) { dvxDestroyWindow(sAc, win); } } static const FileFilterT sFileFilters[] = { {"All Files (*.*)", "*.*"}, {"Text Files (*.txt)", "*.txt"}, {"Batch Files (*.bat)", "*.bat"}, {"Executables (*.exe)", "*.exe"}, {"Bitmap Files (*.bmp)", "*.bmp"} }; static void onMenuCb(WindowT *win, int32_t menuId) { switch (menuId) { case CMD_FILE_OPEN: { char path[260]; if (dvxFileDialog(sAc, "Open File", FD_OPEN, NULL, sFileFilters, 5, path, sizeof(path))) { char msg[300]; snprintf(msg, sizeof(msg), "Selected: %s", path); dvxMessageBox(sAc, "Open", msg, MB_OK | MB_ICONINFO); } break; } case CMD_FILE_SAVE: { char path[260]; if (dvxFileDialog(sAc, "Save As", FD_SAVE, NULL, sFileFilters, 5, path, sizeof(path))) { char msg[300]; snprintf(msg, sizeof(msg), "Save to: %s", path); dvxMessageBox(sAc, "Save", msg, MB_OK | MB_ICONINFO); } break; } case CMD_FILE_EXIT: onCloseMainCb(win); break; case CMD_VIEW_TERM: setupTerminalWindow(); break; case CMD_VIEW_CTRL: setupControlsWindow(); break; case CMD_VIEW_DEBUG_LYT: { static bool sDebugLyt = false; sDebugLyt = !sDebugLyt; wgtSetDebugLayout(sAc, sDebugLyt); break; } case CMD_WIN_CASCADE: dvxCascadeWindows(sAc); break; case CMD_WIN_TILE_H: dvxTileWindowsH(sAc); break; case CMD_WIN_TILE_V: dvxTileWindowsV(sAc); break; case CMD_WIN_TILE: dvxTileWindows(sAc); break; case CMD_HELP_ABOUT: dvxMessageBox(sAc, "About DVX Demo", "DVX GUI Demonstration\n" "A DOS Visual eXecutive windowing system for DOS.", MB_OK | MB_ICONINFO); break; case CMD_CTX_CUT: dvxMessageBox(sAc, "Context Menu", "Cut selected.", MB_OK | MB_ICONINFO); break; case CMD_CTX_COPY: dvxMessageBox(sAc, "Context Menu", "Copied to clipboard.", MB_OK | MB_ICONINFO); break; case CMD_CTX_PASTE: dvxMessageBox(sAc, "Context Menu", "Pasted from clipboard.", MB_OK | MB_ICONINFO); break; case CMD_CTX_DELETE: dvxMessageBox(sAc, "Context Menu", "Deleted.", MB_OK | MB_ICONINFO); break; case CMD_CTX_SELALL: dvxMessageBox(sAc, "Context Menu", "Selected all.", MB_OK | MB_ICONINFO); break; case CMD_CTX_PROPS: dvxMessageBox(sAc, "Properties", "Window properties dialog would appear here.", MB_OK | MB_ICONINFO); break; } } // Walk up the widget tree to find the root, then use wgtFind() to locate // the named status label. This pattern avoids static coupling between the // button and the status label — they're connected only by the name string. static void onOkClick(WidgetT *w) { WidgetT *root = w; while (root->parent) { root = root->parent; } WidgetT *status = wgtFind(root, "status"); if (status) { wgtSetText(status, "Button clicked!"); } } // Renders a vertical gradient directly into the window's content buffer. // This demonstrates the raw onPaint approach: the app has full pixel control // and uses the BlitOps span primitives for performance. The gradient is // computed per-scanline using the span fill operation (rep stosl on x86), // which is much faster than per-pixel writes. static void onPaintColor(WindowT *win, RectT *dirtyArea) { (void)dirtyArea; if (!win->contentBuf || !sAc) { return; } const DisplayT *d = dvxGetDisplay(sAc); const BlitOpsT *ops = dvxGetBlitOps(sAc); for (int32_t y = 0; y < win->contentH; y++) { uint8_t r = (uint8_t)((y * 255) / (win->contentH > 1 ? win->contentH - 1 : 1)); uint8_t g = (uint8_t)(100); uint8_t b = (uint8_t)(200 - (y * 150) / (win->contentH > 1 ? win->contentH - 1 : 1)); uint32_t color = packColor(d, r, g, b); uint8_t *row = win->contentBuf + y * win->contentPitch; ops->spanFill(row, color, win->contentW); } } // Renders a checkerboard pattern pixel-by-pixel. This is the slowest // approach (per-pixel BPP branching) and exists to test that the content // buffer renders correctly at all supported bit depths (8/16/32 bpp). static void onPaintPattern(WindowT *win, RectT *dirtyArea) { (void)dirtyArea; if (!win->contentBuf || !sAc) { return; } const DisplayT *d = dvxGetDisplay(sAc); int32_t bpp = d->format.bytesPerPixel; int32_t sq = 16; uint32_t c1 = packColor(d, 255, 255, 255); uint32_t c2 = packColor(d, 0, 0, 180); for (int32_t y = 0; y < win->contentH; y++) { for (int32_t x = 0; x < win->contentW; x++) { uint32_t color = ((x / sq) + (y / sq)) & 1 ? c2 : c1; uint8_t *px = win->contentBuf + y * win->contentPitch + x * bpp; if (bpp == 1) { *px = (uint8_t)color; } else if (bpp == 2) { *(uint16_t *)px = (uint16_t)color; } else { *(uint32_t *)px = color; } } } } // Renders text manually using the bitmap font glyph data, bypassing the // normal drawText helper. This demonstrates direct glyph rendering and // also serves as a regression test for the font subsystem — if the glyph // data format ever changes, this paint callback would visibly break. static void onPaintText(WindowT *win, RectT *dirtyArea) { (void)dirtyArea; if (!win->contentBuf || !sAc) { return; } const DisplayT *d = dvxGetDisplay(sAc); const BlitOpsT *ops = dvxGetBlitOps(sAc); const BitmapFontT *font = dvxGetFont(sAc); int32_t bpp = d->format.bytesPerPixel; uint32_t bg = packColor(d, 255, 255, 255); uint32_t fg = packColor(d, 0, 0, 0); for (int32_t y = 0; y < win->contentH; y++) { ops->spanFill(win->contentBuf + y * win->contentPitch, bg, win->contentW); } static const char *lines[] = { "DVX GUI Compositor", "", "A DOS Visual eXecutive windowed GUI", "compositor for DOS, targeting", "DJGPP/DPMI.", "", "Features:", " - VESA VBE 2.0+ LFB", " - Dirty-rect compositing", " - Beveled Motif-style chrome", " - Draggable/resizable windows", " - Menu bars with accelerators", " - Scrollbars", " - Widget system", " - ANSI terminal emulator", NULL }; int32_t textY = 4; for (int32_t i = 0; lines[i] != NULL; i++) { const char *line = lines[i]; int32_t textX = 4; for (int32_t j = 0; line[j] != '\0'; j++) { int32_t idx = (uint8_t)line[j] - font->firstChar; if (idx < 0 || idx >= font->numChars) { textX += font->charWidth; continue; } const uint8_t *glyph = font->glyphData + idx * font->charHeight; for (int32_t row = 0; row < font->charHeight; row++) { int32_t py = textY + row; if (py >= win->contentH) { break; } uint8_t bits = glyph[row]; for (int32_t col = 0; col < font->charWidth; col++) { int32_t px = textX + col; if (px >= win->contentW) { break; } if (bits & (0x80 >> col)) { uint8_t *dst = win->contentBuf + py * win->contentPitch + px * bpp; if (bpp == 1) { *dst = (uint8_t)fg; } else if (bpp == 2) { *(uint16_t *)dst = (uint16_t)fg; } else { *(uint32_t *)dst = fg; } } } } textX += font->charWidth; } textY += font->charHeight + 2; } } static void onToolbarClick(WidgetT *w) { WidgetT *root = w; while (root->parent) { root = root->parent; } WidgetT *status = wgtFind(root, "advStatus"); if (status) { wgtSetText(status, wgtGetText(w)); } } // ============================================================ // setupControlsWindow — advanced widgets with tabs // ============================================================ // Item arrays are static because dropdown/combobox widgets store pointers, // not copies. They must outlive the widgets that reference them. static const char *colorItems[] = {"Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"}; static const char *sizeItems[] = {"Small", "Medium", "Large", "Extra Large"}; // Demonstrates every advanced widget type, organized into tabs. The TabControl // widget manages tab pages; each page is a container that shows/hides based // on the selected tab. This is the densest widget test in the project. static void setupControlsWindow(void) { WindowT *win = dvxCreateWindow(sAc, "Advanced Widgets", 380, 50, 360, 440, true); if (!win) { return; } win->onClose = onCloseCb; WidgetT *root = wgtInitWindow(sAc, win); // TabControl at top WidgetT *tabs = wgtTabControl(root); // --- Tab 1: Controls --- WidgetT *page1 = wgtTabPage(tabs, "&Controls"); WidgetT *ddRow = wgtHBox(page1); wgtLabel(ddRow, "Co&lor:"); WidgetT *dd = wgtDropdown(ddRow); wgtDropdownSetItems(dd, colorItems, 6); wgtDropdownSetSelected(dd, 0); wgtSetTooltip(dd, "Choose a color"); WidgetT *cbRow = wgtHBox(page1); wgtLabel(cbRow, "Si&ze:"); WidgetT *cb = wgtComboBox(cbRow, 32); wgtComboBoxSetItems(cb, sizeItems, 4); wgtComboBoxSetSelected(cb, 1); wgtSetTooltip(cb, "Type or select a size"); wgtHSeparator(page1); wgtLabel(page1, "&Progress:"); WidgetT *pbRow = wgtHBox(page1); WidgetT *pb = wgtProgressBar(pbRow); pb->weight = 100; wgtProgressBarSetValue(pb, 65); wgtSetTooltip(pb, "Task progress: 65%"); WidgetT *pbV = wgtProgressBarV(pbRow); wgtProgressBarSetValue(pbV, 75); wgtSetTooltip(pbV, "Vertical progress: 75%"); wgtLabel(page1, "&Volume:"); WidgetT *slider = wgtSlider(page1, 0, 100); wgtSetTooltip(slider, "Adjust volume level"); WidgetT *spinRow = wgtHBox(page1); spinRow->maxH = wgtPixels(30); wgtLabel(spinRow, "&Quantity:"); WidgetT *spin = wgtSpinner(spinRow, 0, 999, 1); wgtSpinnerSetValue(spin, 42); spin->weight = 50; wgtSetTooltip(spin, "Enter quantity (0-999)"); // --- Tab 2: Tree --- WidgetT *page2 = wgtTabPage(tabs, "&Tree"); WidgetT *tree = wgtTreeView(page2); wgtTreeViewSetReorderable(tree, true); WidgetT *docs = wgtTreeItem(tree, "Documents"); wgtTreeItemSetExpanded(docs, true); wgtTreeItem(docs, "README.md"); wgtTreeItem(docs, "DESIGN.md"); WidgetT *src = wgtTreeItem(docs, "src"); wgtTreeItemSetExpanded(src, true); wgtTreeItem(src, "main.c"); wgtTreeItem(src, "utils.c"); wgtTreeItem(src, "render.c"); WidgetT *images = wgtTreeItem(tree, "Images"); wgtTreeItem(images, "logo.png"); wgtTreeItem(images, "icon.bmp"); WidgetT *config = wgtTreeItem(tree, "Config"); wgtTreeItem(config, "settings.ini"); wgtTreeItem(config, "palette.dat"); // --- Tab 3: ListView (multi-column list) --- WidgetT *page3 = wgtTabPage(tabs, "&List"); static const ListViewColT lvCols[] = { {"Name", (int32_t)(WGT_SIZE_CHARS | 16), ListViewAlignLeftE}, {"Size", (int32_t)(WGT_SIZE_CHARS | 8), ListViewAlignRightE}, {"Type", (int32_t)(WGT_SIZE_CHARS | 12), ListViewAlignLeftE}, {"Modified", (int32_t)(WGT_SIZE_CHARS | 12), ListViewAlignLeftE} }; static const char *lvData[] = { "AUTOEXEC.BAT", "412", "Batch File", "03/15/1994", "CONFIG.SYS", "256", "System File", "03/15/1994", "COMMAND.COM", "54,645", "Application", "09/30/1993", "HIMEM.SYS", "29,136", "System Driver", "09/30/1993", "EMM386.EXE", "120,926", "Application", "09/30/1993", "MOUSE.COM", "56,408", "Application", "06/01/1994", "DOSKEY.COM", "5,883", "Application", "09/30/1993", "EDIT.COM", "413", "Application", "09/30/1993", "README.TXT", "8,192", "Text File", "01/10/1994", "DVXDEMO.EXE", "98,304", "Application", "03/15/2026" }; WidgetT *lv = wgtListView(page3); wgtListViewSetColumns(lv, lvCols, 4); wgtListViewSetData(lv, lvData, 10); wgtListViewSetSelected(lv, 0); wgtListViewSetMultiSelect(lv, true); wgtListViewSetReorderable(lv, true); lv->weight = 100; // --- Tab 4: ScrollPane --- WidgetT *page4sp = wgtTabPage(tabs, "&Scroll"); WidgetT *sp = wgtScrollPane(page4sp); sp->weight = 100; sp->padding = wgtPixels(4); sp->spacing = wgtPixels(4); wgtLabel(sp, "ScrollPane Demo:"); wgtHSeparator(sp); static const char *spItems[] = { "Item &1", "Item &2", "Item &3", "Item &4", "Item &5", "Item &6", "Item &7", "Item &8" }; for (int32_t i = 0; i < 8; i++) { wgtCheckbox(sp, spItems[i]); } wgtHSeparator(sp); wgtLabel(sp, "More widgets below:"); WidgetT *spRg = wgtRadioGroup(sp); static const char *spRadios[] = { "Radio 1", "Radio 2", "Radio 3", "Radio 4", "Radio 5" }; for (int32_t i = 0; i < 5; i++) { wgtRadio(spRg, spRadios[i]); } wgtHSeparator(sp); wgtLabel(sp, "Bottom of scroll area"); wgtButton(sp, "Scrolled &Button"); // --- Tab 5: Toolbar (text buttons + VSeparator) --- WidgetT *page5tb = wgtTabPage(tabs, "Tool&bar"); WidgetT *tb = wgtToolbar(page5tb); char iconPath[272]; snprintf(iconPath, sizeof(iconPath), "%s/new.bmp", sDxeCtx->appDir); WidgetT *btnNew = wgtImageButtonFromFile(tb, iconPath); if (!btnNew) { btnNew = wgtButton(tb, "&New"); } btnNew->onClick = onToolbarClick; snprintf(iconPath, sizeof(iconPath), "%s/open.bmp", sDxeCtx->appDir); WidgetT *btnOpen = wgtImageButtonFromFile(tb, iconPath); if (!btnOpen) { btnOpen = wgtButton(tb, "&Open"); } btnOpen->onClick = onToolbarClick; snprintf(iconPath, sizeof(iconPath), "%s/save.bmp", sDxeCtx->appDir); WidgetT *btnSave = wgtImageButtonFromFile(tb, iconPath); if (!btnSave) { btnSave = wgtButton(tb, "&Save"); } btnSave->onClick = onToolbarClick; wgtVSeparator(tb); WidgetT *btnHelp = wgtButton(tb, "&Help"); btnHelp->onClick = onToolbarClick; wgtLabel(page5tb, "Toolbar with image buttons, text fallback, and VSeparator."); // --- Tab 6: Media (Image from file) --- WidgetT *page6m = wgtTabPage(tabs, "&Media"); char samplePath[272]; snprintf(samplePath, sizeof(samplePath), "%s/sample.bmp", sDxeCtx->appDir); wgtLabel(page6m, "ImageFromFile (sample.bmp):"); WidgetT *img = wgtImageFromFile(page6m, samplePath); if (!img) { wgtLabel(page6m, "(File not found)"); } // --- Tab 7: Editor (TextArea, Canvas) --- WidgetT *page7e = wgtTabPage(tabs, "&Editor"); wgtLabel(page7e, "TextArea:"); WidgetT *ta = wgtTextArea(page7e, 512); ta->weight = 100; wgtSetText(ta, "Multi-line text editor.\n\nFeatures:\n- Word wrap\n- Selection\n- Copy/Paste\n- Undo (Ctrl+Z)"); wgtHSeparator(page7e); wgtLabel(page7e, "Canvas (draw with mouse):"); const DisplayT *d = dvxGetDisplay(sAc); WidgetT *cv = wgtCanvas(page7e, 280, 80); wgtCanvasSetPenColor(cv, packColor(d, 200, 0, 0)); wgtCanvasDrawRect(cv, 5, 5, 50, 35); wgtCanvasSetPenColor(cv, packColor(d, 0, 0, 200)); wgtCanvasFillCircle(cv, 150, 40, 25); wgtCanvasSetPenColor(cv, packColor(d, 0, 150, 0)); wgtCanvasDrawLine(cv, 70, 5, 130, 70); wgtCanvasSetPenColor(cv, packColor(d, 0, 0, 0)); wgtCanvasSetMouseCallback(cv, onCanvasDraw); // --- Tab 8: Splitter --- // Demonstrates nested splitters: a horizontal split (top/bottom) where // the top pane contains a vertical split (left/right). This creates a // three-pane file-explorer layout similar to Windows Explorer. WidgetT *page8s = wgtTabPage(tabs, "S&plit"); WidgetT *hSplit = wgtSplitter(page8s, false); hSplit->weight = 100; wgtSplitterSetPos(hSplit, 120); // Top pane: vertical splitter (tree | list) WidgetT *vSplit = wgtSplitter(hSplit, true); wgtSplitterSetPos(vSplit, 120); // Left pane: multi-select tree view WidgetT *leftTree = wgtTreeView(vSplit); wgtTreeViewSetMultiSelect(leftTree, true); WidgetT *tiFolders = wgtTreeItem(leftTree, "Folders"); wgtTreeItemSetExpanded(tiFolders, true); wgtTreeItem(tiFolders, "Documents"); wgtTreeItem(tiFolders, "Pictures"); wgtTreeItem(tiFolders, "Music"); WidgetT *tiSystem = wgtTreeItem(leftTree, "System"); wgtTreeItemSetExpanded(tiSystem, true); wgtTreeItem(tiSystem, "Config"); wgtTreeItem(tiSystem, "Drivers"); // Right pane: list box WidgetT *rightList = wgtListBox(vSplit); static const char *explorerItems[] = { "README.TXT", "SETUP.EXE", "CONFIG.SYS", "AUTOEXEC.BAT", "COMMAND.COM", "HIMEM.SYS", "EMM386.EXE", "MOUSE.COM" }; wgtListBoxSetItems(rightList, explorerItems, 8); // Bottom pane: detail/preview area WidgetT *detailFrame = wgtFrame(hSplit, "Preview"); wgtLabel(detailFrame, "Select a file above to preview."); // --- Tab 9: Disabled --- // Side-by-side comparison of every widget in enabled vs disabled state. // Tests that wgtSetEnabled(w, false) correctly greys out each widget type. WidgetT *page9d = wgtTabPage(tabs, "&Disabled"); WidgetT *disRow = wgtHBox(page9d); disRow->weight = 100; // Enabled column WidgetT *enCol = wgtVBox(disRow); enCol->weight = 100; wgtLabel(enCol, "Enabled:"); wgtHSeparator(enCol); wgtLabel(enCol, "A &label"); wgtButton(enCol, "A &button"); WidgetT *enChk = wgtCheckbox(enCol, "&Check me"); enChk->as.checkbox.checked = true; WidgetT *enRg = wgtRadioGroup(enCol); wgtRadio(enRg, "&Radio 1"); WidgetT *enR2 = wgtRadio(enRg, "R&adio 2"); enRg->as.radioGroup.selectedIdx = enR2->as.radio.index; static const char *disItems[] = {"Alpha", "Beta", "Gamma"}; WidgetT *enDd = wgtDropdown(enCol); wgtDropdownSetItems(enDd, disItems, 3); wgtDropdownSetSelected(enDd, 0); WidgetT *enSlider = wgtSlider(enCol, 0, 100); wgtSliderSetValue(enSlider, 40); WidgetT *enProg = wgtProgressBar(enCol); wgtProgressBarSetValue(enProg, 60); WidgetT *enSpin = wgtSpinner(enCol, 0, 100, 1); wgtSpinnerSetValue(enSpin, 42); WidgetT *enText = wgtTextInput(enCol, 64); wgtSetText(enText, "Editable"); // Disabled column WidgetT *disCol = wgtVBox(disRow); disCol->weight = 100; wgtLabel(disCol, "Disabled:"); wgtHSeparator(disCol); WidgetT *dLbl = wgtLabel(disCol, "A &label"); wgtSetEnabled(dLbl, false); WidgetT *dBtn = wgtButton(disCol, "A &button"); wgtSetEnabled(dBtn, false); WidgetT *dChk = wgtCheckbox(disCol, "&Check me"); dChk->as.checkbox.checked = true; wgtSetEnabled(dChk, false); WidgetT *dRg = wgtRadioGroup(disCol); WidgetT *dR1 = wgtRadio(dRg, "&Radio 1"); WidgetT *dR2 = wgtRadio(dRg, "R&adio 2"); dRg->as.radioGroup.selectedIdx = dR2->as.radio.index; wgtSetEnabled(dR1, false); wgtSetEnabled(dR2, false); WidgetT *dDd = wgtDropdown(disCol); wgtDropdownSetItems(dDd, disItems, 3); wgtDropdownSetSelected(dDd, 0); wgtSetEnabled(dDd, false); WidgetT *dSlider = wgtSlider(disCol, 0, 100); wgtSliderSetValue(dSlider, 40); wgtSetEnabled(dSlider, false); WidgetT *dProg = wgtProgressBar(disCol); wgtProgressBarSetValue(dProg, 60); wgtSetEnabled(dProg, false); WidgetT *dSpin = wgtSpinner(disCol, 0, 100, 1); wgtSpinnerSetValue(dSpin, 42); wgtSetEnabled(dSpin, false); WidgetT *dText = wgtTextInput(disCol, 64); wgtSetText(dText, "Read-only"); wgtSetEnabled(dText, false); // Status bar at bottom (outside tabs) WidgetT *sb = wgtStatusBar(root); WidgetT *sbLabel = wgtLabel(sb, "Ready"); sbLabel->weight = 100; wgtSetName(sbLabel, "advStatus"); wgtLabel(sb, "Line 1, Col 1"); wgtInvalidate(root); } // ============================================================ // setupMainWindow — info window + paint demos // ============================================================ // Creates three windows that demonstrate raw onPaint rendering (no widgets): // 1. Text info window with full menu bar, accelerators, and context menu // 2. Color gradient window // 3. Checkerboard pattern window with scrollbars static void setupMainWindow(void) { WindowT *win1 = dvxCreateWindow(sAc, "DVX Information", 50, 40, 340, 350, true); if (win1) { win1->onPaint = onPaintText; win1->onClose = onCloseMainCb; win1->onMenu = onMenuCb; MenuBarT *bar = wmAddMenuBar(win1); if (bar) { MenuT *fileMenu = wmAddMenu(bar, "&File"); if (fileMenu) { wmAddMenuItem(fileMenu, "&New", CMD_FILE_NEW); wmAddMenuItem(fileMenu, "&Open...", CMD_FILE_OPEN); wmAddMenuItem(fileMenu, "&Save", CMD_FILE_SAVE); wmAddMenuSeparator(fileMenu); wmAddMenuItem(fileMenu, "E&xit", CMD_FILE_EXIT); } MenuT *editMenu = wmAddMenu(bar, "&Edit"); if (editMenu) { wmAddMenuItem(editMenu, "Cu&t", CMD_EDIT_CUT); wmAddMenuItem(editMenu, "&Copy", CMD_EDIT_COPY); wmAddMenuItem(editMenu, "&Paste", CMD_EDIT_PASTE); } MenuT *viewMenu = wmAddMenu(bar, "&View"); if (viewMenu) { wmAddMenuItem(viewMenu, "&Terminal", CMD_VIEW_TERM); wmAddMenuItem(viewMenu, "&Controls", CMD_VIEW_CTRL); wmAddMenuSeparator(viewMenu); wmAddMenuCheckItem(viewMenu, "Tool&bar", CMD_VIEW_TOOLBAR, true); wmAddMenuCheckItem(viewMenu, "&Status Bar", CMD_VIEW_STATUSBAR, true); wmAddMenuSeparator(viewMenu); wmAddMenuRadioItem(viewMenu, "S&mall", CMD_VIEW_SIZE_SMALL, false); wmAddMenuRadioItem(viewMenu, "Me&dium", CMD_VIEW_SIZE_MED, true); wmAddMenuRadioItem(viewMenu, "&Large", CMD_VIEW_SIZE_LARGE, false); wmAddMenuSeparator(viewMenu); MenuT *zoomMenu = wmAddSubMenu(viewMenu, "&Zoom"); if (zoomMenu) { wmAddMenuItem(zoomMenu, "Zoom &In", CMD_VIEW_ZOOM_IN); wmAddMenuItem(zoomMenu, "Zoom &Out", CMD_VIEW_ZOOM_OUT); wmAddMenuSeparator(zoomMenu); wmAddMenuItem(zoomMenu, "&Fit to Window", CMD_VIEW_ZOOM_FIT); } wmAddMenuSeparator(viewMenu); wmAddMenuCheckItem(viewMenu, "&Debug Layout", CMD_VIEW_DEBUG_LYT, false); } MenuT *winMenu = wmAddMenu(bar, "&Window"); if (winMenu) { wmAddMenuItem(winMenu, "&Cascade", CMD_WIN_CASCADE); wmAddMenuItem(winMenu, "Tile &Grid", CMD_WIN_TILE); wmAddMenuItem(winMenu, "Tile &Horizontal", CMD_WIN_TILE_H); wmAddMenuItem(winMenu, "Tile &Vertical", CMD_WIN_TILE_V); } MenuT *helpMenu = wmAddMenu(bar, "&Help"); if (helpMenu) { wmAddMenuItem(helpMenu, "&About...", CMD_HELP_ABOUT); } } // Accelerator table: keyboard shortcuts that work even without the // menu being open. The WM dispatches these via the window's onMenu // callback, making them indistinguishable from menu clicks. AccelTableT *accel = dvxCreateAccelTable(); dvxAddAccel(accel, 'N', ACCEL_CTRL, CMD_FILE_NEW); dvxAddAccel(accel, 'O', ACCEL_CTRL, CMD_FILE_OPEN); dvxAddAccel(accel, 'S', ACCEL_CTRL, CMD_FILE_SAVE); dvxAddAccel(accel, 'Q', ACCEL_CTRL, CMD_FILE_EXIT); dvxAddAccel(accel, KEY_F1, 0, CMD_HELP_ABOUT); win1->accelTable = accel; // Right-click context menu, attached at the window level. If a widget // has its own contextMenu it takes priority; otherwise this one fires. MenuT *winCtx = wmCreateMenu(); wmAddMenuItem(winCtx, "Cu&t", CMD_CTX_CUT); wmAddMenuItem(winCtx, "&Copy", CMD_CTX_COPY); wmAddMenuItem(winCtx, "&Paste", CMD_CTX_PASTE); wmAddMenuSeparator(winCtx); wmAddMenuItem(winCtx, "&Properties...", CMD_CTX_PROPS); win1->contextMenu = winCtx; // For raw-paint windows (no widget tree), we must manually update // the content rect and allocate the content buffer after adding // menu bars or scrollbars, since those shrink the content area. // Then we do an initial paint to fill the buffer before it's composited. wmUpdateContentRect(win1); wmReallocContentBuf(win1, &sAc->display); RectT fullRect = {0, 0, win1->contentW, win1->contentH}; win1->onPaint(win1, &fullRect); } // Window 2: Color gradient WindowT *win2 = dvxCreateWindow(sAc, "Color Gradient", 200, 100, 280, 250, true); if (win2) { win2->onPaint = onPaintColor; win2->onClose = onCloseCb; RectT fullRect = {0, 0, win2->contentW, win2->contentH}; win2->onPaint(win2, &fullRect); } // Window 3: Checkerboard pattern with scrollbars WindowT *win3 = dvxCreateWindow(sAc, "Pattern", 400, 150, 250, 220, true); if (win3) { win3->onPaint = onPaintPattern; win3->onClose = onCloseCb; wmAddVScrollbar(win3, 0, 100, 25); wmAddHScrollbar(win3, 0, 100, 25); wmUpdateContentRect(win3); wmReallocContentBuf(win3, &sAc->display); RectT fullRect = {0, 0, win3->contentW, win3->contentH}; win3->onPaint(win3, &fullRect); } } // ============================================================ // setupTerminalWindow — ANSI terminal widget demo // ============================================================ // Creates an ANSI terminal widget with sample output demonstrating all // supported attributes (bold, reverse, blink), 8+8 colors, background // colors, and CP437 box-drawing/graphic characters. The terminal is // minimized on creation to avoid cluttering the initial demo view. static void setupTerminalWindow(void) { WindowT *win = dvxCreateWindow(sAc, "ANSI Terminal", 60, 60, 660, 420, true); if (!win) { return; } win->onClose = onCloseCb; WidgetT *root = wgtInitWindow(sAc, win); WidgetT *term = wgtAnsiTerm(root, 80, 25); term->weight = 100; wgtAnsiTermSetScrollback(term, 500); // Feed some ANSI content to demonstrate the terminal static const uint8_t ansiDemo[] = "\x1B[2J" // clear screen "\x1B[1;34m========================================\r\n" " DVX ANSI Terminal Emulator\r\n" "========================================\x1B[0m\r\n" "\r\n" "\x1B[1mBold text\x1B[0m, " "\x1B[7mReverse\x1B[0m, " "\x1B[5mBlinking\x1B[0m\r\n" "\r\n" "Standard colors:\r\n" " \x1B[30m\x1B[47m Black \x1B[0m" " \x1B[31m Red \x1B[0m" " \x1B[32m Green \x1B[0m" " \x1B[33m Yellow \x1B[0m" " \x1B[34m Blue \x1B[0m" " \x1B[35m Magenta \x1B[0m" " \x1B[36m Cyan \x1B[0m" " \x1B[37m White \x1B[0m\r\n" "\r\n" "Bright colors:\r\n" " \x1B[1;30m\x1B[47m DkGray \x1B[0m" " \x1B[1;31m LtRed \x1B[0m" " \x1B[1;32m LtGreen \x1B[0m" " \x1B[1;33m LtYellow \x1B[0m" " \x1B[1;34m LtBlue \x1B[0m" " \x1B[1;35m LtMagenta \x1B[0m" " \x1B[1;36m LtCyan \x1B[0m" " \x1B[1;37m BrWhite \x1B[0m\r\n" "\r\n" "Background colors:\r\n" " \x1B[40m\x1B[37m Black \x1B[0m" " \x1B[41m\x1B[37m Red \x1B[0m" " \x1B[42m\x1B[30m Green \x1B[0m" " \x1B[43m\x1B[30m Yellow \x1B[0m" " \x1B[44m\x1B[37m Blue \x1B[0m" " \x1B[45m\x1B[37m Magenta \x1B[0m" " \x1B[46m\x1B[30m Cyan \x1B[0m" " \x1B[47m\x1B[30m White \x1B[0m\r\n" "\r\n" "CP437 graphics: " "\x01\x02\x03\x04\x05\x06" // smileys, hearts, diamonds, clubs, spades " \xB0\xB1\xB2\xDB" // shade blocks " \xC4\xC5\xB3\xDA\xBF\xC0\xD9" // box drawing "\r\n" "\r\n" "\x1B[1;32mTerminal ready.\x1B[0m " "\x1B[36m(Not connected to any host)\x1B[0m\r\n"; wgtAnsiTermWrite(term, ansiDemo, (int32_t)(sizeof(ansiDemo) - 1)); WidgetT *sb = wgtStatusBar(root); wgtLabel(sb, "80x25 [Local]"); dvxFitWindow(sAc, win); dvxMinimizeWindow(sAc, win); } // ============================================================ // setupWidgetDemo — form with accelerators // ============================================================ // Demonstrates the standard form pattern: labeled inputs in frames, checkbox // and radio groups, list boxes (single and multi-select with context menus), // and a button row. The '&' in label text marks the Alt+key mnemonic that // moves focus to the associated widget — this is the accelerator mechanism. static void setupWidgetDemo(void) { WindowT *win = dvxCreateWindow(sAc, "Widget Demo", 80, 200, 280, 360, true); if (!win) { return; } win->onClose = onCloseCb; win->onMenu = onMenuCb; WidgetT *root = wgtInitWindow(sAc, win); // Status label at top WidgetT *status = wgtLabel(root, "Ready."); wgtSetName(status, "status"); wgtHSeparator(root); // Frame with text input WidgetT *frame = wgtFrame(root, "&User Input"); WidgetT *row1 = wgtHBox(frame); wgtLabel(row1, "&Name:"); wgtTextInput(row1, 64); WidgetT *row2 = wgtHBox(frame); wgtLabel(row2, "&Password:"); wgtPasswordInput(row2, 32); WidgetT *row3 = wgtHBox(frame); wgtLabel(row3, "P&hone:"); wgtMaskedInput(row3, "(###) ###-####"); wgtHSeparator(root); // Checkboxes wgtCheckbox(root, "Enable feature &A"); wgtCheckbox(root, "Enable feature &B"); wgtHSeparator(root); // Radio buttons WidgetT *rg = wgtRadioGroup(root); wgtRadio(rg, "Option &1"); wgtRadio(rg, "Option &2"); wgtRadio(rg, "Option &3"); wgtHSeparator(root); // List boxes static const char *listItems[] = {"Alpha", "Beta", "Gamma", "Delta", "Epsilon"}; static const char *multiItems[] = {"Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet"}; WidgetT *listRow = wgtHBox(root); wgtLabel(listRow, "&Items:"); WidgetT *lb = wgtListBox(listRow); wgtListBoxSetItems(lb, listItems, 5); wgtListBoxSetReorderable(lb, true); lb->weight = 100; // Context menu on the list box MenuT *lbCtx = wmCreateMenu(); wmAddMenuItem(lbCtx, "Cu&t", CMD_CTX_CUT); wmAddMenuItem(lbCtx, "&Copy", CMD_CTX_COPY); wmAddMenuItem(lbCtx, "&Paste", CMD_CTX_PASTE); wmAddMenuSeparator(lbCtx); wmAddMenuItem(lbCtx, "&Delete", CMD_CTX_DELETE); wmAddMenuSeparator(lbCtx); wmAddMenuItem(lbCtx, "Select &All", CMD_CTX_SELALL); lb->contextMenu = lbCtx; wgtLabel(listRow, "&Multi:"); WidgetT *mlb = wgtListBox(listRow); wgtListBoxSetMultiSelect(mlb, true); wgtListBoxSetItems(mlb, multiItems, 7); mlb->weight = 100; wgtHSeparator(root); // Button row at bottom WidgetT *btnRow = wgtHBox(root); btnRow->align = AlignEndE; WidgetT *okBtn = wgtButton(btnRow, "&OK"); okBtn->onClick = onOkClick; wgtButton(btnRow, "&Cancel"); wgtInvalidate(root); } // ============================================================ // Entry point // ============================================================ // Create all demo windows at once. Because this is a callback-only app, // appMain returns immediately and the shell's event loop takes over. // Each window is self-contained with its own callbacks. int32_t appMain(DxeAppContextT *ctx) { sDxeCtx = ctx; sAc = ctx->shellCtx; setupMainWindow(); setupWidgetDemo(); setupControlsWindow(); setupTerminalWindow(); return 0; }