1128 lines
37 KiB
C
1128 lines
37 KiB
C
// 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 <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// ============================================================
|
|
// 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;
|
|
}
|