DVX_GUI/dvxdemo/demo.c

485 lines
13 KiB
C

// demo.c — DV/X GUI demonstration application
#include "dvxApp.h"
#include "dvxWidget.h"
#include <stdio.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_HELP_ABOUT 200
// ============================================================
// Prototypes
// ============================================================
static void onCloseCb(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 setupWidgetDemo2(AppContextT *ctx);
static void setupWindows(AppContextT *ctx);
// ============================================================
// onCloseCb
// ============================================================
static void onCloseCb(WindowT *win) {
AppContextT *ctx = (AppContextT *)win->userData;
if (ctx) {
dvxDestroyWindow(ctx, win);
}
}
// ============================================================
// onMenuCb
// ============================================================
static void onMenuCb(WindowT *win, int32_t menuId) {
AppContextT *ctx = (AppContextT *)win->userData;
switch (menuId) {
case CMD_FILE_EXIT:
if (ctx) {
dvxQuit(ctx);
}
break;
case CMD_HELP_ABOUT:
// Could create an about dialog window here
break;
}
}
// ============================================================
// onOkClick
// ============================================================
static void onOkClick(WidgetT *w) {
// Find the status label and update it
WidgetT *root = w;
while (root->parent) {
root = root->parent;
}
WidgetT *status = wgtFind(root, "status");
if (status) {
wgtSetText(status, "Button clicked!");
wgtInvalidate(status);
}
}
// ============================================================
// onPaintColor
// ============================================================
static void onPaintColor(WindowT *win, RectT *dirtyArea) {
(void)dirtyArea;
AppContextT *ctx = (AppContextT *)win->userData;
if (!win->contentBuf || !ctx) {
return;
}
const DisplayT *d = dvxGetDisplay(ctx);
const BlitOpsT *ops = dvxGetBlitOps(ctx);
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);
}
}
// ============================================================
// onPaintPattern
// ============================================================
static void onPaintPattern(WindowT *win, RectT *dirtyArea) {
(void)dirtyArea;
AppContextT *ctx = (AppContextT *)win->userData;
if (!win->contentBuf || !ctx) {
return;
}
const DisplayT *d = dvxGetDisplay(ctx);
int32_t bpp = d->format.bytesPerPixel;
int32_t sq = 16; // square size
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;
}
}
}
}
// ============================================================
// onPaintText
// ============================================================
static void onPaintText(WindowT *win, RectT *dirtyArea) {
(void)dirtyArea;
AppContextT *ctx = (AppContextT *)win->userData;
if (!win->contentBuf || !ctx) {
return;
}
const DisplayT *d = dvxGetDisplay(ctx);
const BlitOpsT *ops = dvxGetBlitOps(ctx);
const BitmapFontT *font = dvxGetFont(ctx);
int32_t bpp = d->format.bytesPerPixel;
// Fill white background
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);
}
// Draw text lines directly into the content buffer
static const char *lines[] = {
"DV/X GUI Compositor",
"",
"A DESQview/X-style windowed GUI",
"compositor for DOS, targeting",
"DJGPP/DPMI.",
"",
"Features:",
" - VESA VBE 2.0+ LFB",
" - Dirty-rect compositing",
" - Beveled Motif-style chrome",
" - Draggable windows",
" - Resizable windows",
" - Menu bars",
" - Scrollbars",
" - Widget system",
"",
"Press ESC to exit.",
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;
}
}
// ============================================================
// setupWidgetDemo2
// ============================================================
static const char *colorItems[] = {"Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"};
static const char *sizeItems[] = {"Small", "Medium", "Large", "Extra Large"};
static void setupWidgetDemo2(AppContextT *ctx) {
WindowT *win = dvxCreateWindow(ctx, "Advanced Widgets", 380, 50, 320, 400, true);
if (!win) {
return;
}
win->userData = ctx;
win->onClose = onCloseCb;
WidgetT *root = wgtInitWindow(ctx, win);
// TabControl at top
WidgetT *tabs = wgtTabControl(root);
// --- Tab 1: Controls ---
WidgetT *page1 = wgtTabPage(tabs, "Controls");
WidgetT *ddRow = wgtHBox(page1);
wgtLabel(ddRow, "Color:");
WidgetT *dd = wgtDropdown(ddRow);
wgtDropdownSetItems(dd, colorItems, 6);
wgtDropdownSetSelected(dd, 0);
WidgetT *cbRow = wgtHBox(page1);
wgtLabel(cbRow, "Size:");
WidgetT *cb = wgtComboBox(cbRow, 32);
wgtComboBoxSetItems(cb, sizeItems, 4);
wgtComboBoxSetSelected(cb, 1);
wgtHSeparator(page1);
wgtLabel(page1, "Progress:");
WidgetT *pb = wgtProgressBar(page1);
wgtProgressBarSetValue(pb, 65);
wgtLabel(page1, "Volume:");
wgtSlider(page1, 0, 100);
// --- Tab 2: Tree ---
WidgetT *page2 = wgtTabPage(tabs, "Tree");
WidgetT *tree = wgtTreeView(page2);
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: Toolbar ---
WidgetT *page3 = wgtTabPage(tabs, "Toolbar");
WidgetT *tb = wgtToolbar(page3);
wgtButton(tb, "New");
wgtButton(tb, "Open");
wgtButton(tb, "Save");
wgtLabel(page3, "Toolbar with buttons above.");
// Status bar at bottom (outside tabs)
WidgetT *sb = wgtStatusBar(root);
WidgetT *sbLabel = wgtLabel(sb, "Ready");
sbLabel->weight = 100;
wgtLabel(sb, "Line 1, Col 1");
wgtInvalidate(root);
}
// ============================================================
// setupWindows
// ============================================================
static void setupWindows(AppContextT *ctx) {
// Window 1: Text information window with menu bar
WindowT *win1 = dvxCreateWindow(ctx, "DV/X Information", 50, 40, 340, 350, true);
if (win1) {
win1->userData = ctx;
win1->onPaint = onPaintText;
win1->onClose = onCloseCb;
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, "Exit", CMD_FILE_EXIT);
}
MenuT *helpMenu = wmAddMenu(bar, "Help");
if (helpMenu) {
wmAddMenuItem(helpMenu, "About...", CMD_HELP_ABOUT);
}
}
wmUpdateContentRect(win1);
wmReallocContentBuf(win1, &ctx->display);
// Paint initial content
RectT fullRect = {0, 0, win1->contentW, win1->contentH};
win1->onPaint(win1, &fullRect);
}
// Window 2: Color gradient
WindowT *win2 = dvxCreateWindow(ctx, "Color Gradient", 200, 100, 280, 250, true);
if (win2) {
win2->userData = ctx;
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(ctx, "Pattern", 400, 150, 250, 220, true);
if (win3) {
win3->userData = ctx;
win3->onPaint = onPaintPattern;
win3->onClose = onCloseCb;
wmAddVScrollbar(win3, 0, 100, 25);
wmAddHScrollbar(win3, 0, 100, 25);
wmUpdateContentRect(win3);
wmReallocContentBuf(win3, &ctx->display);
RectT fullRect = {0, 0, win3->contentW, win3->contentH};
win3->onPaint(win3, &fullRect);
}
// Window 4: Widget demo
WindowT *win4 = dvxCreateWindow(ctx, "Widget Demo", 80, 200, 280, 320, true);
if (win4) {
win4->userData = ctx;
win4->onClose = onCloseCb;
WidgetT *root = wgtInitWindow(ctx, win4);
// Status label at top
WidgetT *status = wgtLabel(root, "Ready.");
strncpy(status->name, "status", MAX_WIDGET_NAME);
wgtHSeparator(root);
// Frame with text input
WidgetT *frame = wgtFrame(root, "User Input");
WidgetT *row1 = wgtHBox(frame);
wgtLabel(row1, "Name:");
wgtTextInput(row1, 64);
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);
// Button row at bottom
WidgetT *btnRow = wgtHBox(root);
btnRow->align = AlignEndE;
WidgetT *okBtn = wgtButton(btnRow, "OK");
okBtn->onClick = onOkClick;
wgtButton(btnRow, "Cancel");
// Layout and paint now that widget tree is complete
wgtInvalidate(root);
}
}
// ============================================================
// main
// ============================================================
int main(void) {
AppContextT ctx;
printf("DV/X GUI Demo\n");
printf("Initializing VESA video...\n");
if (dvxInit(&ctx, 1024, 768, 16) != 0) {
fprintf(stderr, "Failed to initialize DV/X GUI\n");
return 1;
}
setupWindows(&ctx);
setupWidgetDemo2(&ctx);
dvxRun(&ctx);
dvxShutdown(&ctx);
printf("DV/X GUI Demo ended.\n");
return 0;
}