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
902
dvx/dvxApp.c
902
dvx/dvxApp.c
File diff suppressed because it is too large
Load diff
|
|
@ -22,6 +22,8 @@ typedef struct AppContextT {
|
||||||
BitmapFontT font;
|
BitmapFontT font;
|
||||||
ColorSchemeT colors;
|
ColorSchemeT colors;
|
||||||
PopupStateT popup;
|
PopupStateT popup;
|
||||||
|
SysMenuStateT sysMenu;
|
||||||
|
KbMoveResizeT kbMoveResize;
|
||||||
CursorT cursors[5]; // indexed by CURSOR_xxx
|
CursorT cursors[5]; // indexed by CURSOR_xxx
|
||||||
int32_t cursorId; // active cursor shape
|
int32_t cursorId; // active cursor shape
|
||||||
uint32_t cursorFg; // pre-packed cursor colors
|
uint32_t cursorFg; // pre-packed cursor colors
|
||||||
|
|
|
||||||
136
dvx/dvxDraw.c
136
dvx/dvxDraw.c
|
|
@ -8,6 +8,7 @@
|
||||||
// Prototypes
|
// 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 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 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);
|
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);
|
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
|
// 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
|
// drawVLine
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -640,3 +740,39 @@ int32_t textWidth(const BitmapFontT *font, const char *text) {
|
||||||
|
|
||||||
return w;
|
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
|
// Measure text width in pixels
|
||||||
int32_t textWidth(const BitmapFontT *font, const char *text);
|
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)
|
// Draw a 1-bit bitmap with mask (for cursors, icons)
|
||||||
// andMask/xorData are arrays of uint16_t, one per row
|
// 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);
|
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 separator; // true = this is a separator line, not a clickable item
|
||||||
bool enabled;
|
bool enabled;
|
||||||
bool checked;
|
bool checked;
|
||||||
|
char accelKey; // lowercase accelerator character, 0 if none
|
||||||
} MenuItemT;
|
} MenuItemT;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -161,6 +162,7 @@ typedef struct {
|
||||||
int32_t itemCount;
|
int32_t itemCount;
|
||||||
int32_t barX; // computed position on menu bar
|
int32_t barX; // computed position on menu bar
|
||||||
int32_t barW; // computed width on menu bar
|
int32_t barW; // computed width on menu bar
|
||||||
|
char accelKey; // lowercase accelerator character, 0 if none
|
||||||
} MenuT;
|
} MenuT;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -312,6 +314,60 @@ typedef struct {
|
||||||
int32_t hoverItem; // which item is highlighted (-1 = none)
|
int32_t hoverItem; // which item is highlighted (-1 = none)
|
||||||
} PopupStateT;
|
} 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
|
// Utility macros
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,7 @@ typedef struct WidgetT {
|
||||||
bool visible;
|
bool visible;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
bool focused;
|
bool focused;
|
||||||
|
char accelKey; // lowercase accelerator character, 0 if none
|
||||||
|
|
||||||
// User data and callbacks
|
// User data and callbacks
|
||||||
void *userData;
|
void *userData;
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ static void computeMenuBarPositions(WindowT *win, const BitmapFontT *font) {
|
||||||
|
|
||||||
for (int32_t i = 0; i < win->menuBar->menuCount; i++) {
|
for (int32_t i = 0; i < win->menuBar->menuCount; i++) {
|
||||||
MenuT *menu = &win->menuBar->menus[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->barX = x;
|
||||||
menu->barW = labelW;
|
menu->barW = labelW;
|
||||||
|
|
@ -193,8 +193,8 @@ static void drawMenuBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fon
|
||||||
int32_t textX = win->x + menu->barX + CHROME_TITLE_PAD;
|
int32_t textX = win->x + menu->barX + CHROME_TITLE_PAD;
|
||||||
int32_t textY = barY + (barH - font->charHeight) / 2;
|
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);
|
colors->menuFg, colors->menuBg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw bottom separator line
|
// Draw bottom separator line
|
||||||
|
|
@ -567,6 +567,7 @@ MenuT *wmAddMenu(MenuBarT *bar, const char *label) {
|
||||||
memset(menu, 0, sizeof(*menu));
|
memset(menu, 0, sizeof(*menu));
|
||||||
strncpy(menu->label, label, MAX_MENU_LABEL - 1);
|
strncpy(menu->label, label, MAX_MENU_LABEL - 1);
|
||||||
menu->label[MAX_MENU_LABEL - 1] = '\0';
|
menu->label[MAX_MENU_LABEL - 1] = '\0';
|
||||||
|
menu->accelKey = accelParse(label);
|
||||||
bar->menuCount++;
|
bar->menuCount++;
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
|
|
@ -608,6 +609,7 @@ void wmAddMenuItem(MenuT *menu, const char *label, int32_t id) {
|
||||||
item->separator = false;
|
item->separator = false;
|
||||||
item->enabled = true;
|
item->enabled = true;
|
||||||
item->checked = false;
|
item->checked = false;
|
||||||
|
item->accelKey = accelParse(label);
|
||||||
menu->itemCount++;
|
menu->itemCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,14 +49,14 @@ void widgetFramePaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitmap
|
||||||
|
|
||||||
// Draw title centered vertically on the top border line
|
// Draw title centered vertically on the top border line
|
||||||
if (w->as.frame.title && w->as.frame.title[0]) {
|
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 titleX = w->x + DEFAULT_PADDING + fb;
|
||||||
int32_t titleY = boxY + (fb - font->charHeight) / 2;
|
int32_t titleY = boxY + (fb - font->charHeight) / 2;
|
||||||
|
|
||||||
rectFill(d, ops, titleX - 2, titleY,
|
rectFill(d, ops, titleX - 2, titleY,
|
||||||
titleW + 4, font->charHeight, bg);
|
titleW + 4, font->charHeight, bg);
|
||||||
drawText(d, ops, font, titleX, titleY,
|
drawTextAccel(d, ops, font, titleX, titleY,
|
||||||
w->as.frame.title, fg, bg, true);
|
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.title = title;
|
||||||
w->as.frame.style = FrameInE;
|
w->as.frame.style = FrameInE;
|
||||||
w->as.frame.color = 0;
|
w->as.frame.color = 0;
|
||||||
|
w->accelKey = accelParse(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ WidgetT *wgtButton(WidgetT *parent, const char *text) {
|
||||||
if (w) {
|
if (w) {
|
||||||
w->as.button.text = text;
|
w->as.button.text = text;
|
||||||
w->as.button.pressed = false;
|
w->as.button.pressed = false;
|
||||||
|
w->accelKey = accelParse(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
|
|
@ -24,7 +25,7 @@ WidgetT *wgtButton(WidgetT *parent, const char *text) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
void widgetButtonCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
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;
|
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;
|
bevel.width = 2;
|
||||||
drawBevel(d, ops, w->x, w->y, w->w, w->h, &bevel);
|
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 textX = w->x + (w->w - textW) / 2;
|
||||||
int32_t textY = w->y + (w->h - font->charHeight) / 2;
|
int32_t textY = w->y + (w->h - font->charHeight) / 2;
|
||||||
|
|
||||||
|
|
@ -63,8 +64,8 @@ void widgetButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
|
||||||
textY++;
|
textY++;
|
||||||
}
|
}
|
||||||
|
|
||||||
drawText(d, ops, font, textX, textY,
|
drawTextAccel(d, ops, font, textX, textY,
|
||||||
w->as.button.text,
|
w->as.button.text,
|
||||||
w->enabled ? fg : colors->windowShadow,
|
w->enabled ? fg : colors->windowShadow,
|
||||||
bgFace, true);
|
bgFace, true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ WidgetT *wgtCheckbox(WidgetT *parent, const char *text) {
|
||||||
if (w) {
|
if (w) {
|
||||||
w->as.checkbox.text = text;
|
w->as.checkbox.text = text;
|
||||||
w->as.checkbox.checked = false;
|
w->as.checkbox.checked = false;
|
||||||
|
w->accelKey = accelParse(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
|
|
@ -25,7 +26,7 @@ WidgetT *wgtCheckbox(WidgetT *parent, const char *text) {
|
||||||
|
|
||||||
void widgetCheckboxCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
void widgetCheckboxCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||||
w->calcMinW = CHECKBOX_BOX_SIZE + CHECKBOX_GAP +
|
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);
|
w->calcMinH = DVX_MAX(CHECKBOX_BOX_SIZE, font->charHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,8 +74,8 @@ void widgetCheckboxPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw label
|
// Draw label
|
||||||
drawText(d, ops, font,
|
drawTextAccel(d, ops, font,
|
||||||
w->x + CHECKBOX_BOX_SIZE + CHECKBOX_GAP,
|
w->x + CHECKBOX_BOX_SIZE + CHECKBOX_GAP,
|
||||||
w->y + (w->h - font->charHeight) / 2,
|
w->y + (w->h - font->charHeight) / 2,
|
||||||
w->as.checkbox.text, fg, bg, false);
|
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
|
// 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
|
// 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
|
// widgetIsBoxContainer
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -57,11 +57,15 @@ extern int32_t sDragOffset;
|
||||||
|
|
||||||
void widgetAddChild(WidgetT *parent, WidgetT *child);
|
void widgetAddChild(WidgetT *parent, WidgetT *child);
|
||||||
WidgetT *widgetAlloc(WidgetT *parent, WidgetTypeE type);
|
WidgetT *widgetAlloc(WidgetT *parent, WidgetTypeE type);
|
||||||
|
void widgetClearFocus(WidgetT *root);
|
||||||
int32_t widgetCountVisibleChildren(const WidgetT *w);
|
int32_t widgetCountVisibleChildren(const WidgetT *w);
|
||||||
void widgetDestroyChildren(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);
|
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);
|
int32_t widgetFrameBorderWidth(const WidgetT *w);
|
||||||
WidgetT *widgetHitTest(WidgetT *w, int32_t x, int32_t y);
|
WidgetT *widgetHitTest(WidgetT *w, int32_t x, int32_t y);
|
||||||
|
bool widgetIsFocusable(WidgetTypeE type);
|
||||||
bool widgetIsBoxContainer(WidgetTypeE type);
|
bool widgetIsBoxContainer(WidgetTypeE type);
|
||||||
bool widgetIsHorizContainer(WidgetTypeE type);
|
bool widgetIsHorizContainer(WidgetTypeE type);
|
||||||
void widgetRemoveChild(WidgetT *parent, WidgetT *child);
|
void widgetRemoveChild(WidgetT *parent, WidgetT *child);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ WidgetT *wgtLabel(WidgetT *parent, const char *text) {
|
||||||
|
|
||||||
if (w) {
|
if (w) {
|
||||||
w->as.label.text = text;
|
w->as.label.text = text;
|
||||||
|
w->accelKey = accelParse(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
|
|
@ -23,8 +24,8 @@ WidgetT *wgtLabel(WidgetT *parent, const char *text) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
void widgetLabelCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
void widgetLabelCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||||
w->calcMinW = (int32_t)strlen(w->as.label.text) * font->charWidth;
|
w->calcMinW = textWidthAccel(font, w->as.label.text);
|
||||||
w->calcMinH = font->charHeight;
|
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 fg = w->fgColor ? w->fgColor : colors->contentFg;
|
||||||
uint32_t bg = w->bgColor ? w->bgColor : colors->contentBg;
|
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);
|
w->as.label.text, fg, bg, false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -396,18 +396,22 @@ void wgtSetText(WidgetT *w, const char *text) {
|
||||||
switch (w->type) {
|
switch (w->type) {
|
||||||
case WidgetLabelE:
|
case WidgetLabelE:
|
||||||
w->as.label.text = text;
|
w->as.label.text = text;
|
||||||
|
w->accelKey = accelParse(text);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WidgetButtonE:
|
case WidgetButtonE:
|
||||||
w->as.button.text = text;
|
w->as.button.text = text;
|
||||||
|
w->accelKey = accelParse(text);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WidgetCheckboxE:
|
case WidgetCheckboxE:
|
||||||
w->as.checkbox.text = text;
|
w->as.checkbox.text = text;
|
||||||
|
w->accelKey = accelParse(text);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WidgetRadioE:
|
case WidgetRadioE:
|
||||||
w->as.radio.text = text;
|
w->as.radio.text = text;
|
||||||
|
w->accelKey = accelParse(text);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WidgetTextInputE:
|
case WidgetTextInputE:
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ WidgetT *wgtRadio(WidgetT *parent, const char *text) {
|
||||||
|
|
||||||
if (w) {
|
if (w) {
|
||||||
w->as.radio.text = text;
|
w->as.radio.text = text;
|
||||||
|
w->accelKey = accelParse(text);
|
||||||
|
|
||||||
// Auto-assign index based on position in parent
|
// Auto-assign index based on position in parent
|
||||||
int32_t idx = 0;
|
int32_t idx = 0;
|
||||||
|
|
@ -50,7 +51,7 @@ WidgetT *wgtRadioGroup(WidgetT *parent) {
|
||||||
|
|
||||||
void widgetRadioCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
void widgetRadioCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||||
w->calcMinW = CHECKBOX_BOX_SIZE + CHECKBOX_GAP +
|
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);
|
w->calcMinH = DVX_MAX(CHECKBOX_BOX_SIZE, font->charHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,8 +95,8 @@ void widgetRadioPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitmap
|
||||||
CHECKBOX_BOX_SIZE - 6, CHECKBOX_BOX_SIZE - 6, fg);
|
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->x + CHECKBOX_BOX_SIZE + CHECKBOX_GAP,
|
||||||
w->y + (w->h - font->charHeight) / 2,
|
w->y + (w->h - font->charHeight) / 2,
|
||||||
w->as.radio.text, fg, bg, false);
|
w->as.radio.text, fg, bg, false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ WidgetT *wgtTabPage(WidgetT *parent, const char *title) {
|
||||||
|
|
||||||
if (w) {
|
if (w) {
|
||||||
w->as.tabPage.title = title;
|
w->as.tabPage.title = title;
|
||||||
|
w->accelKey = accelParse(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
|
|
@ -79,7 +80,7 @@ void widgetTabControlCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||||
maxPageW = DVX_MAX(maxPageW, c->calcMinW);
|
maxPageW = DVX_MAX(maxPageW, c->calcMinW);
|
||||||
maxPageH = DVX_MAX(maxPageH, c->calcMinH);
|
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;
|
tabHeaderW += labelW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,7 +146,7 @@ void widgetTabControlOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy
|
||||||
continue;
|
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 (vx >= tabX && vx < tabX + tw) {
|
||||||
if (tabIdx != hit->as.tabControl.activeTab) {
|
if (tabIdx != hit->as.tabControl.activeTab) {
|
||||||
|
|
@ -190,7 +191,7 @@ void widgetTabControlPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const B
|
||||||
continue;
|
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);
|
bool isActive = (tabIdx == w->as.tabControl.activeTab);
|
||||||
int32_t ty = isActive ? w->y : w->y + 2;
|
int32_t ty = isActive ? w->y : w->y + 2;
|
||||||
int32_t th = isActive ? tabH + 2 : tabH;
|
int32_t th = isActive ? tabH + 2 : tabH;
|
||||||
|
|
@ -227,8 +228,8 @@ void widgetTabControlPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const B
|
||||||
labelY++;
|
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);
|
c->as.tabPage.title, colors->contentFg, tabFace, true);
|
||||||
|
|
||||||
tabX += tw;
|
tabX += tw;
|
||||||
tabIdx++;
|
tabIdx++;
|
||||||
|
|
|
||||||
353
dvxdemo/demo.c
353
dvxdemo/demo.c
|
|
@ -14,21 +14,35 @@
|
||||||
#define CMD_FILE_OPEN 101
|
#define CMD_FILE_OPEN 101
|
||||||
#define CMD_FILE_SAVE 102
|
#define CMD_FILE_SAVE 102
|
||||||
#define CMD_FILE_EXIT 103
|
#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
|
// Prototypes
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static void onCloseCb(WindowT *win);
|
static void onCloseCb(WindowT *win);
|
||||||
|
static void onCloseMainCb(WindowT *win);
|
||||||
static void onMenuCb(WindowT *win, int32_t menuId);
|
static void onMenuCb(WindowT *win, int32_t menuId);
|
||||||
static void onOkClick(WidgetT *w);
|
static void onOkClick(WidgetT *w);
|
||||||
static void onPaintColor(WindowT *win, RectT *dirtyArea);
|
static void onPaintColor(WindowT *win, RectT *dirtyArea);
|
||||||
static void onToolbarClick(WidgetT *w);
|
|
||||||
static void onPaintPattern(WindowT *win, RectT *dirtyArea);
|
static void onPaintPattern(WindowT *win, RectT *dirtyArea);
|
||||||
static void onPaintText(WindowT *win, RectT *dirtyArea);
|
static void onPaintText(WindowT *win, RectT *dirtyArea);
|
||||||
static void setupWidgetDemo2(AppContextT *ctx);
|
static void onToolbarClick(WidgetT *w);
|
||||||
static void setupWindows(AppContextT *ctx);
|
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
|
// onMenuCb
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -58,8 +85,15 @@ static void onMenuCb(WindowT *win, int32_t menuId) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CMD_VIEW_TERM:
|
||||||
|
setupTerminalWindow(ctx);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMD_VIEW_CTRL:
|
||||||
|
setupControlsWindow(ctx);
|
||||||
|
break;
|
||||||
|
|
||||||
case CMD_HELP_ABOUT:
|
case CMD_HELP_ABOUT:
|
||||||
// Could create an about dialog window here
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -70,7 +104,6 @@ static void onMenuCb(WindowT *win, int32_t menuId) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static void onOkClick(WidgetT *w) {
|
static void onOkClick(WidgetT *w) {
|
||||||
// Find the status label and update it
|
|
||||||
WidgetT *root = w;
|
WidgetT *root = w;
|
||||||
|
|
||||||
while (root->parent) {
|
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
|
// onPaintColor
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -148,7 +161,7 @@ static void onPaintPattern(WindowT *win, RectT *dirtyArea) {
|
||||||
|
|
||||||
const DisplayT *d = dvxGetDisplay(ctx);
|
const DisplayT *d = dvxGetDisplay(ctx);
|
||||||
int32_t bpp = d->format.bytesPerPixel;
|
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 c1 = packColor(d, 255, 255, 255);
|
||||||
uint32_t c2 = packColor(d, 0, 0, 180);
|
uint32_t c2 = packColor(d, 0, 0, 180);
|
||||||
|
|
@ -187,7 +200,6 @@ static void onPaintText(WindowT *win, RectT *dirtyArea) {
|
||||||
const BitmapFontT *font = dvxGetFont(ctx);
|
const BitmapFontT *font = dvxGetFont(ctx);
|
||||||
int32_t bpp = d->format.bytesPerPixel;
|
int32_t bpp = d->format.bytesPerPixel;
|
||||||
|
|
||||||
// Fill white background
|
|
||||||
uint32_t bg = packColor(d, 255, 255, 255);
|
uint32_t bg = packColor(d, 255, 255, 255);
|
||||||
uint32_t fg = packColor(d, 0, 0, 0);
|
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);
|
ops->spanFill(win->contentBuf + y * win->contentPitch, bg, win->contentW);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw text lines directly into the content buffer
|
|
||||||
static const char *lines[] = {
|
static const char *lines[] = {
|
||||||
"DV/X GUI Compositor",
|
"DV/X GUI Compositor",
|
||||||
"",
|
"",
|
||||||
|
|
@ -207,13 +218,11 @@ static void onPaintText(WindowT *win, RectT *dirtyArea) {
|
||||||
" - VESA VBE 2.0+ LFB",
|
" - VESA VBE 2.0+ LFB",
|
||||||
" - Dirty-rect compositing",
|
" - Dirty-rect compositing",
|
||||||
" - Beveled Motif-style chrome",
|
" - Beveled Motif-style chrome",
|
||||||
" - Draggable windows",
|
" - Draggable/resizable windows",
|
||||||
" - Resizable windows",
|
" - Menu bars with accelerators",
|
||||||
" - Menu bars",
|
|
||||||
" - Scrollbars",
|
" - Scrollbars",
|
||||||
" - Widget system",
|
" - Widget system",
|
||||||
"",
|
" - ANSI terminal emulator",
|
||||||
"Press ESC to exit.",
|
|
||||||
NULL
|
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 *colorItems[] = {"Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"};
|
||||||
static const char *sizeItems[] = {"Small", "Medium", "Large", "Extra Large"};
|
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);
|
WindowT *win = dvxCreateWindow(ctx, "Advanced Widgets", 380, 50, 320, 400, true);
|
||||||
|
|
||||||
if (!win) {
|
if (!win) {
|
||||||
|
|
@ -294,31 +323,31 @@ static void setupWidgetDemo2(AppContextT *ctx) {
|
||||||
WidgetT *tabs = wgtTabControl(root);
|
WidgetT *tabs = wgtTabControl(root);
|
||||||
|
|
||||||
// --- Tab 1: Controls ---
|
// --- Tab 1: Controls ---
|
||||||
WidgetT *page1 = wgtTabPage(tabs, "Controls");
|
WidgetT *page1 = wgtTabPage(tabs, "&Controls");
|
||||||
|
|
||||||
WidgetT *ddRow = wgtHBox(page1);
|
WidgetT *ddRow = wgtHBox(page1);
|
||||||
wgtLabel(ddRow, "Color:");
|
wgtLabel(ddRow, "Co&lor:");
|
||||||
WidgetT *dd = wgtDropdown(ddRow);
|
WidgetT *dd = wgtDropdown(ddRow);
|
||||||
wgtDropdownSetItems(dd, colorItems, 6);
|
wgtDropdownSetItems(dd, colorItems, 6);
|
||||||
wgtDropdownSetSelected(dd, 0);
|
wgtDropdownSetSelected(dd, 0);
|
||||||
|
|
||||||
WidgetT *cbRow = wgtHBox(page1);
|
WidgetT *cbRow = wgtHBox(page1);
|
||||||
wgtLabel(cbRow, "Size:");
|
wgtLabel(cbRow, "Si&ze:");
|
||||||
WidgetT *cb = wgtComboBox(cbRow, 32);
|
WidgetT *cb = wgtComboBox(cbRow, 32);
|
||||||
wgtComboBoxSetItems(cb, sizeItems, 4);
|
wgtComboBoxSetItems(cb, sizeItems, 4);
|
||||||
wgtComboBoxSetSelected(cb, 1);
|
wgtComboBoxSetSelected(cb, 1);
|
||||||
|
|
||||||
wgtHSeparator(page1);
|
wgtHSeparator(page1);
|
||||||
|
|
||||||
wgtLabel(page1, "Progress:");
|
wgtLabel(page1, "&Progress:");
|
||||||
WidgetT *pb = wgtProgressBar(page1);
|
WidgetT *pb = wgtProgressBar(page1);
|
||||||
wgtProgressBarSetValue(pb, 65);
|
wgtProgressBarSetValue(pb, 65);
|
||||||
|
|
||||||
wgtLabel(page1, "Volume:");
|
wgtLabel(page1, "&Volume:");
|
||||||
wgtSlider(page1, 0, 100);
|
wgtSlider(page1, 0, 100);
|
||||||
|
|
||||||
// --- Tab 2: Tree ---
|
// --- Tab 2: Tree ---
|
||||||
WidgetT *page2 = wgtTabPage(tabs, "Tree");
|
WidgetT *page2 = wgtTabPage(tabs, "&Tree");
|
||||||
|
|
||||||
WidgetT *tree = wgtTreeView(page2);
|
WidgetT *tree = wgtTreeView(page2);
|
||||||
WidgetT *docs = wgtTreeItem(tree, "Documents");
|
WidgetT *docs = wgtTreeItem(tree, "Documents");
|
||||||
|
|
@ -338,12 +367,12 @@ static void setupWidgetDemo2(AppContextT *ctx) {
|
||||||
wgtTreeItem(config, "palette.dat");
|
wgtTreeItem(config, "palette.dat");
|
||||||
|
|
||||||
// --- Tab 3: Toolbar ---
|
// --- Tab 3: Toolbar ---
|
||||||
WidgetT *page3 = wgtTabPage(tabs, "Toolbar");
|
WidgetT *page3 = wgtTabPage(tabs, "Tool&bar");
|
||||||
|
|
||||||
WidgetT *tb = wgtToolbar(page3);
|
WidgetT *tb = wgtToolbar(page3);
|
||||||
WidgetT *btnNew = wgtButton(tb, "New");
|
WidgetT *btnNew = wgtButton(tb, "&New");
|
||||||
WidgetT *btnOpen = wgtButton(tb, "Open");
|
WidgetT *btnOpen = wgtButton(tb, "&Open");
|
||||||
WidgetT *btnSave = wgtButton(tb, "Save");
|
WidgetT *btnSave = wgtButton(tb, "&Save");
|
||||||
btnNew->onClick = onToolbarClick;
|
btnNew->onClick = onToolbarClick;
|
||||||
btnOpen->onClick = onToolbarClick;
|
btnOpen->onClick = onToolbarClick;
|
||||||
btnSave->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
|
// Window 1: Text information window with menu bar
|
||||||
WindowT *win1 = dvxCreateWindow(ctx, "DV/X Information", 50, 40, 340, 350, true);
|
WindowT *win1 = dvxCreateWindow(ctx, "DV/X Information", 50, 40, 340, 350, true);
|
||||||
|
|
||||||
if (win1) {
|
if (win1) {
|
||||||
win1->userData = ctx;
|
win1->userData = ctx;
|
||||||
win1->onPaint = onPaintText;
|
win1->onPaint = onPaintText;
|
||||||
win1->onClose = onCloseCb;
|
win1->onClose = onCloseMainCb;
|
||||||
win1->onMenu = onMenuCb;
|
win1->onMenu = onMenuCb;
|
||||||
|
|
||||||
MenuBarT *bar = wmAddMenuBar(win1);
|
MenuBarT *bar = wmAddMenuBar(win1);
|
||||||
|
|
||||||
if (bar) {
|
if (bar) {
|
||||||
MenuT *fileMenu = wmAddMenu(bar, "File");
|
MenuT *fileMenu = wmAddMenu(bar, "&File");
|
||||||
|
|
||||||
if (fileMenu) {
|
if (fileMenu) {
|
||||||
wmAddMenuItem(fileMenu, "New", CMD_FILE_NEW);
|
wmAddMenuItem(fileMenu, "&New", CMD_FILE_NEW);
|
||||||
wmAddMenuItem(fileMenu, "Open...", CMD_FILE_OPEN);
|
wmAddMenuItem(fileMenu, "&Open...", CMD_FILE_OPEN);
|
||||||
wmAddMenuItem(fileMenu, "Save", CMD_FILE_SAVE);
|
wmAddMenuItem(fileMenu, "&Save", CMD_FILE_SAVE);
|
||||||
wmAddMenuSeparator(fileMenu);
|
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) {
|
if (helpMenu) {
|
||||||
wmAddMenuItem(helpMenu, "About...", CMD_HELP_ABOUT);
|
wmAddMenuItem(helpMenu, "&About...", CMD_HELP_ABOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wmUpdateContentRect(win1);
|
wmUpdateContentRect(win1);
|
||||||
wmReallocContentBuf(win1, &ctx->display);
|
wmReallocContentBuf(win1, &ctx->display);
|
||||||
|
|
||||||
// Paint initial content
|
|
||||||
RectT fullRect = {0, 0, win1->contentW, win1->contentH};
|
RectT fullRect = {0, 0, win1->contentW, win1->contentH};
|
||||||
win1->onPaint(win1, &fullRect);
|
win1->onPaint(win1, &fullRect);
|
||||||
}
|
}
|
||||||
|
|
@ -431,54 +474,156 @@ static void setupWindows(AppContextT *ctx) {
|
||||||
RectT fullRect = {0, 0, win3->contentW, win3->contentH};
|
RectT fullRect = {0, 0, win3->contentW, win3->contentH};
|
||||||
win3->onPaint(win3, &fullRect);
|
win3->onPaint(win3, &fullRect);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Window 4: Widget demo
|
|
||||||
WindowT *win4 = dvxCreateWindow(ctx, "Widget Demo", 80, 200, 280, 320, true);
|
|
||||||
|
|
||||||
if (win4) {
|
// ============================================================
|
||||||
win4->userData = ctx;
|
// setupTerminalWindow — ANSI terminal widget demo
|
||||||
win4->onClose = onCloseCb;
|
// ============================================================
|
||||||
|
|
||||||
WidgetT *root = wgtInitWindow(ctx, win4);
|
static void setupTerminalWindow(AppContextT *ctx) {
|
||||||
|
WindowT *win = dvxCreateWindow(ctx, "ANSI Terminal", 60, 60, 660, 420, true);
|
||||||
|
|
||||||
// Status label at top
|
if (!win) {
|
||||||
WidgetT *status = wgtLabel(root, "Ready.");
|
return;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.");
|
||||||
|
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);
|
||||||
|
|
||||||
|
WidgetT *row2 = wgtHBox(frame);
|
||||||
|
wgtLabel(row2, "A&ddress:");
|
||||||
|
wgtTextInput(row2, 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);
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
okBtn->onClick = onOkClick;
|
||||||
|
wgtButton(btnRow, "&Cancel");
|
||||||
|
|
||||||
|
wgtInvalidate(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -497,8 +642,12 @@ int main(void) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
setupWindows(&ctx);
|
sCtx = &ctx;
|
||||||
setupWidgetDemo2(&ctx);
|
|
||||||
|
setupMainWindow(&ctx);
|
||||||
|
setupWidgetDemo(&ctx);
|
||||||
|
setupControlsWindow(&ctx);
|
||||||
|
setupTerminalWindow(&ctx);
|
||||||
|
|
||||||
dvxRun(&ctx);
|
dvxRun(&ctx);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue