Working on keyboard control of the GUI.
This commit is contained in:
parent
324382d758
commit
14eca6fcd2
17 changed files with 1507 additions and 144 deletions
898
dvx/dvxApp.c
898
dvx/dvxApp.c
File diff suppressed because it is too large
Load diff
|
|
@ -22,6 +22,8 @@ typedef struct AppContextT {
|
|||
BitmapFontT font;
|
||||
ColorSchemeT colors;
|
||||
PopupStateT popup;
|
||||
SysMenuStateT sysMenu;
|
||||
KbMoveResizeT kbMoveResize;
|
||||
CursorT cursors[5]; // indexed by CURSOR_xxx
|
||||
int32_t cursorId; // active cursor shape
|
||||
uint32_t cursorFg; // pre-packed cursor colors
|
||||
|
|
|
|||
136
dvx/dvxDraw.c
136
dvx/dvxDraw.c
|
|
@ -8,6 +8,7 @@
|
|||
// Prototypes
|
||||
// ============================================================
|
||||
|
||||
char accelParse(const char *text);
|
||||
static inline void clipRect(const DisplayT *d, int32_t *x, int32_t *y, int32_t *w, int32_t *h);
|
||||
static inline void putPixel(uint8_t *dst, uint32_t color, int32_t bpp);
|
||||
static void spanCopy8(uint8_t *dst, const uint8_t *src, int32_t count);
|
||||
|
|
@ -18,6 +19,53 @@ static void spanFill16(uint8_t *dst, uint32_t color, int32_t count);
|
|||
static void spanFill32(uint8_t *dst, uint32_t color, int32_t count);
|
||||
|
||||
|
||||
// ============================================================
|
||||
// accelParse
|
||||
// ============================================================
|
||||
|
||||
char accelParse(const char *text) {
|
||||
if (!text) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (*text) {
|
||||
if (*text == '&') {
|
||||
text++;
|
||||
|
||||
if (*text == '&') {
|
||||
// Escaped && — literal &, not an accelerator
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*text && *text != '&') {
|
||||
char ch = *text;
|
||||
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
return (char)(ch + 32);
|
||||
}
|
||||
|
||||
if (ch >= 'a' && ch <= 'z') {
|
||||
return ch;
|
||||
}
|
||||
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
return ch;
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
text++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// clipRect
|
||||
// ============================================================
|
||||
|
|
@ -353,6 +401,58 @@ void drawText(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// drawTextAccel
|
||||
// ============================================================
|
||||
|
||||
void drawTextAccel(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, const char *text, uint32_t fg, uint32_t bg, bool opaque) {
|
||||
int32_t cw = font->charWidth;
|
||||
int32_t clipX2 = d->clipX + d->clipW;
|
||||
|
||||
while (*text) {
|
||||
if (__builtin_expect(x >= clipX2, 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*text == '&') {
|
||||
text++;
|
||||
|
||||
if (*text == '&') {
|
||||
// Escaped && — draw literal &
|
||||
if (x + cw > d->clipX) {
|
||||
drawChar(d, ops, font, x, y, '&', fg, bg, opaque);
|
||||
}
|
||||
|
||||
x += cw;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*text) {
|
||||
// Accelerator character — draw it then underline
|
||||
if (x + cw > d->clipX) {
|
||||
drawChar(d, ops, font, x, y, *text, fg, bg, opaque);
|
||||
drawHLine(d, ops, x, y + font->charHeight - 1, cw, fg);
|
||||
}
|
||||
|
||||
x += cw;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (x + cw > d->clipX) {
|
||||
drawChar(d, ops, font, x, y, *text, fg, bg, opaque);
|
||||
}
|
||||
|
||||
x += cw;
|
||||
text++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// drawVLine
|
||||
// ============================================================
|
||||
|
|
@ -640,3 +740,39 @@ int32_t textWidth(const BitmapFontT *font, const char *text) {
|
|||
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// textWidthAccel
|
||||
// ============================================================
|
||||
|
||||
int32_t textWidthAccel(const BitmapFontT *font, const char *text) {
|
||||
int32_t w = 0;
|
||||
|
||||
while (*text) {
|
||||
if (*text == '&') {
|
||||
text++;
|
||||
|
||||
if (*text == '&') {
|
||||
// Escaped && — counts as one character
|
||||
w += font->charWidth;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*text) {
|
||||
// Accelerator character — counts as one character, & is skipped
|
||||
w += font->charWidth;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
w += font->charWidth;
|
||||
text++;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,16 @@ void drawText(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t
|
|||
// Measure text width in pixels
|
||||
int32_t textWidth(const BitmapFontT *font, const char *text);
|
||||
|
||||
// Parse accelerator key from text with & markers (e.g. "E&xit" -> 'x')
|
||||
// Returns the lowercase accelerator character, or 0 if none found.
|
||||
char accelParse(const char *text);
|
||||
|
||||
// Draw text with & accelerator processing (underlines the accelerator character)
|
||||
void drawTextAccel(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, const char *text, uint32_t fg, uint32_t bg, bool opaque);
|
||||
|
||||
// Measure text width in pixels, skipping & markers
|
||||
int32_t textWidthAccel(const BitmapFontT *font, const char *text);
|
||||
|
||||
// Draw a 1-bit bitmap with mask (for cursors, icons)
|
||||
// andMask/xorData are arrays of uint16_t, one per row
|
||||
void drawMaskedBitmap(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *andMask, const uint16_t *xorData, uint32_t fgColor, uint32_t bgColor);
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ typedef struct {
|
|||
bool separator; // true = this is a separator line, not a clickable item
|
||||
bool enabled;
|
||||
bool checked;
|
||||
char accelKey; // lowercase accelerator character, 0 if none
|
||||
} MenuItemT;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -161,6 +162,7 @@ typedef struct {
|
|||
int32_t itemCount;
|
||||
int32_t barX; // computed position on menu bar
|
||||
int32_t barW; // computed width on menu bar
|
||||
char accelKey; // lowercase accelerator character, 0 if none
|
||||
} MenuT;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -312,6 +314,60 @@ typedef struct {
|
|||
int32_t hoverItem; // which item is highlighted (-1 = none)
|
||||
} PopupStateT;
|
||||
|
||||
// ============================================================
|
||||
// System menu (control menu / close box menu)
|
||||
// ============================================================
|
||||
|
||||
typedef enum {
|
||||
SysMenuRestoreE = 1,
|
||||
SysMenuMoveE = 2,
|
||||
SysMenuSizeE = 3,
|
||||
SysMenuMinimizeE = 4,
|
||||
SysMenuMaximizeE = 5,
|
||||
SysMenuCloseE = 6
|
||||
} SysMenuCmdE;
|
||||
|
||||
#define SYS_MENU_MAX_ITEMS 8
|
||||
|
||||
typedef struct {
|
||||
char label[MAX_MENU_LABEL];
|
||||
int32_t cmd;
|
||||
bool separator;
|
||||
bool enabled;
|
||||
char accelKey;
|
||||
} SysMenuItemT;
|
||||
|
||||
typedef struct {
|
||||
bool active;
|
||||
int32_t windowId;
|
||||
int32_t popupX;
|
||||
int32_t popupY;
|
||||
int32_t popupW;
|
||||
int32_t popupH;
|
||||
int32_t hoverItem;
|
||||
SysMenuItemT items[SYS_MENU_MAX_ITEMS];
|
||||
int32_t itemCount;
|
||||
} SysMenuStateT;
|
||||
|
||||
// ============================================================
|
||||
// Keyboard move/resize mode
|
||||
// ============================================================
|
||||
|
||||
typedef enum {
|
||||
KbModeNoneE = 0,
|
||||
KbModeMoveE = 1,
|
||||
KbModeResizeE = 2
|
||||
} KbModeE;
|
||||
|
||||
typedef struct {
|
||||
KbModeE mode;
|
||||
int32_t windowId;
|
||||
int32_t origX;
|
||||
int32_t origY;
|
||||
int32_t origW;
|
||||
int32_t origH;
|
||||
} KbMoveResizeT;
|
||||
|
||||
// ============================================================
|
||||
// Utility macros
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ typedef struct WidgetT {
|
|||
bool visible;
|
||||
bool enabled;
|
||||
bool focused;
|
||||
char accelKey; // lowercase accelerator character, 0 if none
|
||||
|
||||
// User data and callbacks
|
||||
void *userData;
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ static void computeMenuBarPositions(WindowT *win, const BitmapFontT *font) {
|
|||
|
||||
for (int32_t i = 0; i < win->menuBar->menuCount; i++) {
|
||||
MenuT *menu = &win->menuBar->menus[i];
|
||||
int32_t labelW = (int32_t)strlen(menu->label) * font->charWidth + CHROME_TITLE_PAD * 2;
|
||||
int32_t labelW = textWidthAccel(font, menu->label) + CHROME_TITLE_PAD * 2;
|
||||
|
||||
menu->barX = x;
|
||||
menu->barW = labelW;
|
||||
|
|
@ -193,7 +193,7 @@ static void drawMenuBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fon
|
|||
int32_t textX = win->x + menu->barX + CHROME_TITLE_PAD;
|
||||
int32_t textY = barY + (barH - font->charHeight) / 2;
|
||||
|
||||
drawText(d, ops, font, textX, textY, menu->label,
|
||||
drawTextAccel(d, ops, font, textX, textY, menu->label,
|
||||
colors->menuFg, colors->menuBg, true);
|
||||
}
|
||||
|
||||
|
|
@ -567,6 +567,7 @@ MenuT *wmAddMenu(MenuBarT *bar, const char *label) {
|
|||
memset(menu, 0, sizeof(*menu));
|
||||
strncpy(menu->label, label, MAX_MENU_LABEL - 1);
|
||||
menu->label[MAX_MENU_LABEL - 1] = '\0';
|
||||
menu->accelKey = accelParse(label);
|
||||
bar->menuCount++;
|
||||
|
||||
return menu;
|
||||
|
|
@ -608,6 +609,7 @@ void wmAddMenuItem(MenuT *menu, const char *label, int32_t id) {
|
|||
item->separator = false;
|
||||
item->enabled = true;
|
||||
item->checked = false;
|
||||
item->accelKey = accelParse(label);
|
||||
menu->itemCount++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,13 +49,13 @@ void widgetFramePaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitmap
|
|||
|
||||
// Draw title centered vertically on the top border line
|
||||
if (w->as.frame.title && w->as.frame.title[0]) {
|
||||
int32_t titleW = (int32_t)strlen(w->as.frame.title) * font->charWidth;
|
||||
int32_t titleW = textWidthAccel(font, w->as.frame.title);
|
||||
int32_t titleX = w->x + DEFAULT_PADDING + fb;
|
||||
int32_t titleY = boxY + (fb - font->charHeight) / 2;
|
||||
|
||||
rectFill(d, ops, titleX - 2, titleY,
|
||||
titleW + 4, font->charHeight, bg);
|
||||
drawText(d, ops, font, titleX, titleY,
|
||||
drawTextAccel(d, ops, font, titleX, titleY,
|
||||
w->as.frame.title, fg, bg, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -72,6 +72,7 @@ WidgetT *wgtFrame(WidgetT *parent, const char *title) {
|
|||
w->as.frame.title = title;
|
||||
w->as.frame.style = FrameInE;
|
||||
w->as.frame.color = 0;
|
||||
w->accelKey = accelParse(title);
|
||||
}
|
||||
|
||||
return w;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ WidgetT *wgtButton(WidgetT *parent, const char *text) {
|
|||
if (w) {
|
||||
w->as.button.text = text;
|
||||
w->as.button.pressed = false;
|
||||
w->accelKey = accelParse(text);
|
||||
}
|
||||
|
||||
return w;
|
||||
|
|
@ -24,7 +25,7 @@ WidgetT *wgtButton(WidgetT *parent, const char *text) {
|
|||
// ============================================================
|
||||
|
||||
void widgetButtonCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||
w->calcMinW = (int32_t)strlen(w->as.button.text) * font->charWidth + BUTTON_PAD_H * 2;
|
||||
w->calcMinW = textWidthAccel(font, w->as.button.text) + BUTTON_PAD_H * 2;
|
||||
w->calcMinH = font->charHeight + BUTTON_PAD_V * 2;
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +55,7 @@ void widgetButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
|
|||
bevel.width = 2;
|
||||
drawBevel(d, ops, w->x, w->y, w->w, w->h, &bevel);
|
||||
|
||||
int32_t textW = (int32_t)strlen(w->as.button.text) * font->charWidth;
|
||||
int32_t textW = textWidthAccel(font, w->as.button.text);
|
||||
int32_t textX = w->x + (w->w - textW) / 2;
|
||||
int32_t textY = w->y + (w->h - font->charHeight) / 2;
|
||||
|
||||
|
|
@ -63,7 +64,7 @@ void widgetButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
|
|||
textY++;
|
||||
}
|
||||
|
||||
drawText(d, ops, font, textX, textY,
|
||||
drawTextAccel(d, ops, font, textX, textY,
|
||||
w->as.button.text,
|
||||
w->enabled ? fg : colors->windowShadow,
|
||||
bgFace, true);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ WidgetT *wgtCheckbox(WidgetT *parent, const char *text) {
|
|||
if (w) {
|
||||
w->as.checkbox.text = text;
|
||||
w->as.checkbox.checked = false;
|
||||
w->accelKey = accelParse(text);
|
||||
}
|
||||
|
||||
return w;
|
||||
|
|
@ -25,7 +26,7 @@ WidgetT *wgtCheckbox(WidgetT *parent, const char *text) {
|
|||
|
||||
void widgetCheckboxCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||
w->calcMinW = CHECKBOX_BOX_SIZE + CHECKBOX_GAP +
|
||||
(int32_t)strlen(w->as.checkbox.text) * font->charWidth;
|
||||
textWidthAccel(font, w->as.checkbox.text);
|
||||
w->calcMinH = DVX_MAX(CHECKBOX_BOX_SIZE, font->charHeight);
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +74,7 @@ void widgetCheckboxPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
|||
}
|
||||
|
||||
// Draw label
|
||||
drawText(d, ops, font,
|
||||
drawTextAccel(d, ops, font,
|
||||
w->x + CHECKBOX_BOX_SIZE + CHECKBOX_GAP,
|
||||
w->y + (w->h - font->charHeight) / 2,
|
||||
w->as.checkbox.text, fg, bg, false);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,23 @@ WidgetT *widgetAlloc(WidgetT *parent, WidgetTypeE type) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetClearFocus
|
||||
// ============================================================
|
||||
|
||||
void widgetClearFocus(WidgetT *root) {
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
root->focused = false;
|
||||
|
||||
for (WidgetT *c = root->firstChild; c; c = c->nextSibling) {
|
||||
widgetClearFocus(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetCountVisibleChildren
|
||||
// ============================================================
|
||||
|
|
@ -170,6 +187,89 @@ void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font, int32_t conten
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetFindByAccel
|
||||
// ============================================================
|
||||
|
||||
WidgetT *widgetFindByAccel(WidgetT *root, char key) {
|
||||
if (!root || !root->enabled) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Invisible tab pages: match the page itself (for tab switching)
|
||||
// but don't recurse into children (their accels shouldn't be active)
|
||||
if (!root->visible) {
|
||||
if (root->type == WidgetTabPageE && root->accelKey == key) {
|
||||
return root;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (root->accelKey == key) {
|
||||
return root;
|
||||
}
|
||||
|
||||
for (WidgetT *c = root->firstChild; c; c = c->nextSibling) {
|
||||
WidgetT *found = widgetFindByAccel(c, key);
|
||||
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetFindNextFocusable
|
||||
// ============================================================
|
||||
//
|
||||
// Depth-first walk of the widget tree. Returns the first focusable
|
||||
// widget found after 'after'. If 'after' is NULL, returns the first
|
||||
// focusable widget. Wraps around to the beginning if needed.
|
||||
|
||||
static WidgetT *findNextFocusableImpl(WidgetT *w, WidgetT *after, bool *pastAfter) {
|
||||
if (!w->visible || !w->enabled) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (after == NULL) {
|
||||
*pastAfter = true;
|
||||
}
|
||||
|
||||
if (w == after) {
|
||||
*pastAfter = true;
|
||||
} else if (*pastAfter && widgetIsFocusable(w->type)) {
|
||||
return w;
|
||||
}
|
||||
|
||||
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
|
||||
WidgetT *found = findNextFocusableImpl(c, after, pastAfter);
|
||||
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WidgetT *widgetFindNextFocusable(WidgetT *root, WidgetT *after) {
|
||||
bool pastAfter = false;
|
||||
WidgetT *found = findNextFocusableImpl(root, after, &pastAfter);
|
||||
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
|
||||
// Wrap around — search from the beginning
|
||||
pastAfter = true;
|
||||
return findNextFocusableImpl(root, NULL, &pastAfter);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetFrameBorderWidth
|
||||
// ============================================================
|
||||
|
|
@ -220,6 +320,19 @@ WidgetT *widgetHitTest(WidgetT *w, int32_t x, int32_t y) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetIsFocusable
|
||||
// ============================================================
|
||||
|
||||
bool widgetIsFocusable(WidgetTypeE type) {
|
||||
return type == WidgetTextInputE || type == WidgetComboBoxE ||
|
||||
type == WidgetDropdownE || type == WidgetCheckboxE ||
|
||||
type == WidgetRadioE || type == WidgetButtonE ||
|
||||
type == WidgetSliderE || type == WidgetListBoxE ||
|
||||
type == WidgetTreeViewE || type == WidgetAnsiTermE;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetIsBoxContainer
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -57,11 +57,15 @@ extern int32_t sDragOffset;
|
|||
|
||||
void widgetAddChild(WidgetT *parent, WidgetT *child);
|
||||
WidgetT *widgetAlloc(WidgetT *parent, WidgetTypeE type);
|
||||
void widgetClearFocus(WidgetT *root);
|
||||
int32_t widgetCountVisibleChildren(const WidgetT *w);
|
||||
void widgetDestroyChildren(WidgetT *w);
|
||||
void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH);
|
||||
WidgetT *widgetFindByAccel(WidgetT *root, char key);
|
||||
WidgetT *widgetFindNextFocusable(WidgetT *root, WidgetT *after);
|
||||
int32_t widgetFrameBorderWidth(const WidgetT *w);
|
||||
WidgetT *widgetHitTest(WidgetT *w, int32_t x, int32_t y);
|
||||
bool widgetIsFocusable(WidgetTypeE type);
|
||||
bool widgetIsBoxContainer(WidgetTypeE type);
|
||||
bool widgetIsHorizContainer(WidgetTypeE type);
|
||||
void widgetRemoveChild(WidgetT *parent, WidgetT *child);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ WidgetT *wgtLabel(WidgetT *parent, const char *text) {
|
|||
|
||||
if (w) {
|
||||
w->as.label.text = text;
|
||||
w->accelKey = accelParse(text);
|
||||
}
|
||||
|
||||
return w;
|
||||
|
|
@ -23,8 +24,8 @@ WidgetT *wgtLabel(WidgetT *parent, const char *text) {
|
|||
// ============================================================
|
||||
|
||||
void widgetLabelCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||
w->calcMinW = (int32_t)strlen(w->as.label.text) * font->charWidth;
|
||||
w->calcMinH = font->charHeight;
|
||||
w->calcMinW = textWidthAccel(font, w->as.label.text);
|
||||
w->calcMinH = font->charHeight + 1;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -36,6 +37,6 @@ void widgetLabelPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitmap
|
|||
uint32_t fg = w->fgColor ? w->fgColor : colors->contentFg;
|
||||
uint32_t bg = w->bgColor ? w->bgColor : colors->contentBg;
|
||||
|
||||
drawText(d, ops, font, w->x, w->y + (w->h - font->charHeight) / 2,
|
||||
drawTextAccel(d, ops, font, w->x, w->y + (w->h - font->charHeight) / 2,
|
||||
w->as.label.text, fg, bg, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -396,18 +396,22 @@ void wgtSetText(WidgetT *w, const char *text) {
|
|||
switch (w->type) {
|
||||
case WidgetLabelE:
|
||||
w->as.label.text = text;
|
||||
w->accelKey = accelParse(text);
|
||||
break;
|
||||
|
||||
case WidgetButtonE:
|
||||
w->as.button.text = text;
|
||||
w->accelKey = accelParse(text);
|
||||
break;
|
||||
|
||||
case WidgetCheckboxE:
|
||||
w->as.checkbox.text = text;
|
||||
w->accelKey = accelParse(text);
|
||||
break;
|
||||
|
||||
case WidgetRadioE:
|
||||
w->as.radio.text = text;
|
||||
w->accelKey = accelParse(text);
|
||||
break;
|
||||
|
||||
case WidgetTextInputE:
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ WidgetT *wgtRadio(WidgetT *parent, const char *text) {
|
|||
|
||||
if (w) {
|
||||
w->as.radio.text = text;
|
||||
w->accelKey = accelParse(text);
|
||||
|
||||
// Auto-assign index based on position in parent
|
||||
int32_t idx = 0;
|
||||
|
|
@ -50,7 +51,7 @@ WidgetT *wgtRadioGroup(WidgetT *parent) {
|
|||
|
||||
void widgetRadioCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||
w->calcMinW = CHECKBOX_BOX_SIZE + CHECKBOX_GAP +
|
||||
(int32_t)strlen(w->as.radio.text) * font->charWidth;
|
||||
textWidthAccel(font, w->as.radio.text);
|
||||
w->calcMinH = DVX_MAX(CHECKBOX_BOX_SIZE, font->charHeight);
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +95,7 @@ void widgetRadioPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitmap
|
|||
CHECKBOX_BOX_SIZE - 6, CHECKBOX_BOX_SIZE - 6, fg);
|
||||
}
|
||||
|
||||
drawText(d, ops, font,
|
||||
drawTextAccel(d, ops, font,
|
||||
w->x + CHECKBOX_BOX_SIZE + CHECKBOX_GAP,
|
||||
w->y + (w->h - font->charHeight) / 2,
|
||||
w->as.radio.text, fg, bg, false);
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ WidgetT *wgtTabPage(WidgetT *parent, const char *title) {
|
|||
|
||||
if (w) {
|
||||
w->as.tabPage.title = title;
|
||||
w->accelKey = accelParse(title);
|
||||
}
|
||||
|
||||
return w;
|
||||
|
|
@ -79,7 +80,7 @@ void widgetTabControlCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
|||
maxPageW = DVX_MAX(maxPageW, c->calcMinW);
|
||||
maxPageH = DVX_MAX(maxPageH, c->calcMinH);
|
||||
|
||||
int32_t labelW = (int32_t)strlen(c->as.tabPage.title) * font->charWidth + TAB_PAD_H * 2;
|
||||
int32_t labelW = textWidthAccel(font, c->as.tabPage.title) + TAB_PAD_H * 2;
|
||||
tabHeaderW += labelW;
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +146,7 @@ void widgetTabControlOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy
|
|||
continue;
|
||||
}
|
||||
|
||||
int32_t tw = (int32_t)strlen(c->as.tabPage.title) * font->charWidth + TAB_PAD_H * 2;
|
||||
int32_t tw = textWidthAccel(font, c->as.tabPage.title) + TAB_PAD_H * 2;
|
||||
|
||||
if (vx >= tabX && vx < tabX + tw) {
|
||||
if (tabIdx != hit->as.tabControl.activeTab) {
|
||||
|
|
@ -190,7 +191,7 @@ void widgetTabControlPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const B
|
|||
continue;
|
||||
}
|
||||
|
||||
int32_t tw = (int32_t)strlen(c->as.tabPage.title) * font->charWidth + TAB_PAD_H * 2;
|
||||
int32_t tw = textWidthAccel(font, c->as.tabPage.title) + TAB_PAD_H * 2;
|
||||
bool isActive = (tabIdx == w->as.tabControl.activeTab);
|
||||
int32_t ty = isActive ? w->y : w->y + 2;
|
||||
int32_t th = isActive ? tabH + 2 : tabH;
|
||||
|
|
@ -227,7 +228,7 @@ void widgetTabControlPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const B
|
|||
labelY++;
|
||||
}
|
||||
|
||||
drawText(d, ops, font, tabX + TAB_PAD_H, labelY,
|
||||
drawTextAccel(d, ops, font, tabX + TAB_PAD_H, labelY,
|
||||
c->as.tabPage.title, colors->contentFg, tabFace, true);
|
||||
|
||||
tabX += tw;
|
||||
|
|
|
|||
301
dvxdemo/demo.c
301
dvxdemo/demo.c
|
|
@ -14,21 +14,35 @@
|
|||
#define CMD_FILE_OPEN 101
|
||||
#define CMD_FILE_SAVE 102
|
||||
#define CMD_FILE_EXIT 103
|
||||
#define CMD_HELP_ABOUT 200
|
||||
#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_HELP_ABOUT 400
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
||||
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 onToolbarClick(WidgetT *w);
|
||||
static void onPaintPattern(WindowT *win, RectT *dirtyArea);
|
||||
static void onPaintText(WindowT *win, RectT *dirtyArea);
|
||||
static void setupWidgetDemo2(AppContextT *ctx);
|
||||
static void setupWindows(AppContextT *ctx);
|
||||
static void onToolbarClick(WidgetT *w);
|
||||
static void setupControlsWindow(AppContextT *ctx);
|
||||
static void setupMainWindow(AppContextT *ctx);
|
||||
static void setupTerminalWindow(AppContextT *ctx);
|
||||
static void setupWidgetDemo(AppContextT *ctx);
|
||||
|
||||
// ============================================================
|
||||
// Globals
|
||||
// ============================================================
|
||||
|
||||
static AppContextT *sCtx = NULL;
|
||||
|
||||
|
||||
// ============================================================
|
||||
|
|
@ -44,6 +58,19 @@ static void onCloseCb(WindowT *win) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onCloseMainCb
|
||||
// ============================================================
|
||||
|
||||
static void onCloseMainCb(WindowT *win) {
|
||||
AppContextT *ctx = (AppContextT *)win->userData;
|
||||
|
||||
if (ctx) {
|
||||
dvxQuit(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onMenuCb
|
||||
// ============================================================
|
||||
|
|
@ -58,8 +85,15 @@ static void onMenuCb(WindowT *win, int32_t menuId) {
|
|||
}
|
||||
break;
|
||||
|
||||
case CMD_VIEW_TERM:
|
||||
setupTerminalWindow(ctx);
|
||||
break;
|
||||
|
||||
case CMD_VIEW_CTRL:
|
||||
setupControlsWindow(ctx);
|
||||
break;
|
||||
|
||||
case CMD_HELP_ABOUT:
|
||||
// Could create an about dialog window here
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -70,7 +104,6 @@ static void onMenuCb(WindowT *win, int32_t menuId) {
|
|||
// ============================================================
|
||||
|
||||
static void onOkClick(WidgetT *w) {
|
||||
// Find the status label and update it
|
||||
WidgetT *root = w;
|
||||
|
||||
while (root->parent) {
|
||||
|
|
@ -86,26 +119,6 @@ static void onOkClick(WidgetT *w) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onToolbarClick
|
||||
// ============================================================
|
||||
|
||||
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));
|
||||
wgtInvalidate(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onPaintColor
|
||||
// ============================================================
|
||||
|
|
@ -148,7 +161,7 @@ static void onPaintPattern(WindowT *win, RectT *dirtyArea) {
|
|||
|
||||
const DisplayT *d = dvxGetDisplay(ctx);
|
||||
int32_t bpp = d->format.bytesPerPixel;
|
||||
int32_t sq = 16; // square size
|
||||
int32_t sq = 16;
|
||||
|
||||
uint32_t c1 = packColor(d, 255, 255, 255);
|
||||
uint32_t c2 = packColor(d, 0, 0, 180);
|
||||
|
|
@ -187,7 +200,6 @@ static void onPaintText(WindowT *win, RectT *dirtyArea) {
|
|||
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);
|
||||
|
||||
|
|
@ -195,7 +207,6 @@ static void onPaintText(WindowT *win, RectT *dirtyArea) {
|
|||
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",
|
||||
"",
|
||||
|
|
@ -207,13 +218,11 @@ static void onPaintText(WindowT *win, RectT *dirtyArea) {
|
|||
" - VESA VBE 2.0+ LFB",
|
||||
" - Dirty-rect compositing",
|
||||
" - Beveled Motif-style chrome",
|
||||
" - Draggable windows",
|
||||
" - Resizable windows",
|
||||
" - Menu bars",
|
||||
" - Draggable/resizable windows",
|
||||
" - Menu bars with accelerators",
|
||||
" - Scrollbars",
|
||||
" - Widget system",
|
||||
"",
|
||||
"Press ESC to exit.",
|
||||
" - ANSI terminal emulator",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -272,13 +281,33 @@ static void onPaintText(WindowT *win, RectT *dirtyArea) {
|
|||
|
||||
|
||||
// ============================================================
|
||||
// setupWidgetDemo2
|
||||
// onToolbarClick
|
||||
// ============================================================
|
||||
|
||||
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));
|
||||
wgtInvalidate(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// setupControlsWindow — advanced widgets with tabs
|
||||
// ============================================================
|
||||
|
||||
static const char *colorItems[] = {"Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"};
|
||||
static const char *sizeItems[] = {"Small", "Medium", "Large", "Extra Large"};
|
||||
|
||||
static void setupWidgetDemo2(AppContextT *ctx) {
|
||||
static void setupControlsWindow(AppContextT *ctx) {
|
||||
WindowT *win = dvxCreateWindow(ctx, "Advanced Widgets", 380, 50, 320, 400, true);
|
||||
|
||||
if (!win) {
|
||||
|
|
@ -294,31 +323,31 @@ static void setupWidgetDemo2(AppContextT *ctx) {
|
|||
WidgetT *tabs = wgtTabControl(root);
|
||||
|
||||
// --- Tab 1: Controls ---
|
||||
WidgetT *page1 = wgtTabPage(tabs, "Controls");
|
||||
WidgetT *page1 = wgtTabPage(tabs, "&Controls");
|
||||
|
||||
WidgetT *ddRow = wgtHBox(page1);
|
||||
wgtLabel(ddRow, "Color:");
|
||||
wgtLabel(ddRow, "Co&lor:");
|
||||
WidgetT *dd = wgtDropdown(ddRow);
|
||||
wgtDropdownSetItems(dd, colorItems, 6);
|
||||
wgtDropdownSetSelected(dd, 0);
|
||||
|
||||
WidgetT *cbRow = wgtHBox(page1);
|
||||
wgtLabel(cbRow, "Size:");
|
||||
wgtLabel(cbRow, "Si&ze:");
|
||||
WidgetT *cb = wgtComboBox(cbRow, 32);
|
||||
wgtComboBoxSetItems(cb, sizeItems, 4);
|
||||
wgtComboBoxSetSelected(cb, 1);
|
||||
|
||||
wgtHSeparator(page1);
|
||||
|
||||
wgtLabel(page1, "Progress:");
|
||||
wgtLabel(page1, "&Progress:");
|
||||
WidgetT *pb = wgtProgressBar(page1);
|
||||
wgtProgressBarSetValue(pb, 65);
|
||||
|
||||
wgtLabel(page1, "Volume:");
|
||||
wgtLabel(page1, "&Volume:");
|
||||
wgtSlider(page1, 0, 100);
|
||||
|
||||
// --- Tab 2: Tree ---
|
||||
WidgetT *page2 = wgtTabPage(tabs, "Tree");
|
||||
WidgetT *page2 = wgtTabPage(tabs, "&Tree");
|
||||
|
||||
WidgetT *tree = wgtTreeView(page2);
|
||||
WidgetT *docs = wgtTreeItem(tree, "Documents");
|
||||
|
|
@ -338,12 +367,12 @@ static void setupWidgetDemo2(AppContextT *ctx) {
|
|||
wgtTreeItem(config, "palette.dat");
|
||||
|
||||
// --- Tab 3: Toolbar ---
|
||||
WidgetT *page3 = wgtTabPage(tabs, "Toolbar");
|
||||
WidgetT *page3 = wgtTabPage(tabs, "Tool&bar");
|
||||
|
||||
WidgetT *tb = wgtToolbar(page3);
|
||||
WidgetT *btnNew = wgtButton(tb, "New");
|
||||
WidgetT *btnOpen = wgtButton(tb, "Open");
|
||||
WidgetT *btnSave = wgtButton(tb, "Save");
|
||||
WidgetT *btnNew = wgtButton(tb, "&New");
|
||||
WidgetT *btnOpen = wgtButton(tb, "&Open");
|
||||
WidgetT *btnSave = wgtButton(tb, "&Save");
|
||||
btnNew->onClick = onToolbarClick;
|
||||
btnOpen->onClick = onToolbarClick;
|
||||
btnSave->onClick = onToolbarClick;
|
||||
|
|
@ -362,43 +391,57 @@ static void setupWidgetDemo2(AppContextT *ctx) {
|
|||
|
||||
|
||||
// ============================================================
|
||||
// setupWindows
|
||||
// setupMainWindow — info window + paint demos
|
||||
// ============================================================
|
||||
|
||||
static void setupWindows(AppContextT *ctx) {
|
||||
static void setupMainWindow(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->onClose = onCloseMainCb;
|
||||
win1->onMenu = onMenuCb;
|
||||
|
||||
MenuBarT *bar = wmAddMenuBar(win1);
|
||||
|
||||
if (bar) {
|
||||
MenuT *fileMenu = wmAddMenu(bar, "File");
|
||||
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);
|
||||
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);
|
||||
wmAddMenuItem(fileMenu, "E&xit", CMD_FILE_EXIT);
|
||||
}
|
||||
|
||||
MenuT *helpMenu = wmAddMenu(bar, "Help");
|
||||
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);
|
||||
}
|
||||
|
||||
MenuT *helpMenu = wmAddMenu(bar, "&Help");
|
||||
|
||||
if (helpMenu) {
|
||||
wmAddMenuItem(helpMenu, "About...", CMD_HELP_ABOUT);
|
||||
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);
|
||||
}
|
||||
|
|
@ -431,15 +474,105 @@ static void setupWindows(AppContextT *ctx) {
|
|||
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;
|
||||
// ============================================================
|
||||
// setupTerminalWindow — ANSI terminal widget demo
|
||||
// ============================================================
|
||||
|
||||
WidgetT *root = wgtInitWindow(ctx, win4);
|
||||
static void setupTerminalWindow(AppContextT *ctx) {
|
||||
WindowT *win = dvxCreateWindow(ctx, "ANSI Terminal", 60, 60, 660, 420, true);
|
||||
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
win->userData = ctx;
|
||||
win->onClose = onCloseCb;
|
||||
|
||||
WidgetT *root = wgtInitWindow(ctx, 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"
|
||||
" DV/X ANSI Terminal Emulator\r\n"
|
||||
"========================================\x1B[0m\r\n"
|
||||
"\r\n"
|
||||
"\x1B[1mBold text\x1B[0m, "
|
||||
"\x1B[4mUnderlined\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(ctx, win);
|
||||
wgtInvalidate(root);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// setupWidgetDemo — form with accelerators
|
||||
// ============================================================
|
||||
|
||||
static void setupWidgetDemo(AppContextT *ctx) {
|
||||
WindowT *win = dvxCreateWindow(ctx, "Widget Demo", 80, 200, 280, 360, true);
|
||||
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
win->userData = ctx;
|
||||
win->onClose = onCloseCb;
|
||||
|
||||
WidgetT *root = wgtInitWindow(ctx, win);
|
||||
|
||||
// Status label at top
|
||||
WidgetT *status = wgtLabel(root, "Ready.");
|
||||
|
|
@ -448,38 +581,50 @@ static void setupWindows(AppContextT *ctx) {
|
|||
wgtHSeparator(root);
|
||||
|
||||
// Frame with text input
|
||||
WidgetT *frame = wgtFrame(root, "User Input");
|
||||
WidgetT *frame = wgtFrame(root, "&User Input");
|
||||
WidgetT *row1 = wgtHBox(frame);
|
||||
wgtLabel(row1, "Name:");
|
||||
wgtLabel(row1, "&Name:");
|
||||
wgtTextInput(row1, 64);
|
||||
|
||||
WidgetT *row2 = wgtHBox(frame);
|
||||
wgtLabel(row2, "A&ddress:");
|
||||
wgtTextInput(row2, 64);
|
||||
|
||||
wgtHSeparator(root);
|
||||
|
||||
// Checkboxes
|
||||
wgtCheckbox(root, "Enable feature A");
|
||||
wgtCheckbox(root, "Enable feature B");
|
||||
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");
|
||||
wgtRadio(rg, "Option &1");
|
||||
wgtRadio(rg, "Option &2");
|
||||
wgtRadio(rg, "Option &3");
|
||||
|
||||
wgtHSeparator(root);
|
||||
|
||||
// List box
|
||||
static const char *listItems[] = {"Alpha", "Beta", "Gamma", "Delta", "Epsilon"};
|
||||
WidgetT *listRow = wgtHBox(root);
|
||||
wgtLabel(listRow, "&Items:");
|
||||
WidgetT *lb = wgtListBox(listRow);
|
||||
wgtListBoxSetItems(lb, listItems, 5);
|
||||
lb->weight = 100;
|
||||
|
||||
wgtHSeparator(root);
|
||||
|
||||
// Button row at bottom
|
||||
WidgetT *btnRow = wgtHBox(root);
|
||||
btnRow->align = AlignEndE;
|
||||
WidgetT *okBtn = wgtButton(btnRow, "OK");
|
||||
WidgetT *okBtn = wgtButton(btnRow, "&OK");
|
||||
okBtn->onClick = onOkClick;
|
||||
wgtButton(btnRow, "Cancel");
|
||||
wgtButton(btnRow, "&Cancel");
|
||||
|
||||
// Layout and paint now that widget tree is complete
|
||||
wgtInvalidate(root);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
|
|
@ -497,8 +642,12 @@ int main(void) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
setupWindows(&ctx);
|
||||
setupWidgetDemo2(&ctx);
|
||||
sCtx = &ctx;
|
||||
|
||||
setupMainWindow(&ctx);
|
||||
setupWidgetDemo(&ctx);
|
||||
setupControlsWindow(&ctx);
|
||||
setupTerminalWindow(&ctx);
|
||||
|
||||
dvxRun(&ctx);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue