Working on keyboard control of GUI.
This commit is contained in:
parent
2fdba061ce
commit
1e5c085ef0
3 changed files with 227 additions and 0 deletions
160
dvx/dvxApp.c
160
dvx/dvxApp.c
|
|
@ -1382,6 +1382,79 @@ static void pollKeyboard(AppContextT *ctx) {
|
||||||
ascii = 0;
|
ascii = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read shift state (INT 16h, AH=12h — enhanced shift flags)
|
||||||
|
memset(&r, 0, sizeof(r));
|
||||||
|
r.x.ax = 0x1200;
|
||||||
|
__dpmi_int(0x16, &r);
|
||||||
|
int32_t shiftFlags = r.x.ax & 0xFF;
|
||||||
|
bool shiftHeld = (shiftFlags & 0x03) != 0; // left or right shift
|
||||||
|
|
||||||
|
// Alt+Tab / Shift+Alt+Tab — cycle windows
|
||||||
|
// Alt+Tab: scancode=0xA5, ascii=0x00
|
||||||
|
if (ascii == 0 && scancode == 0xA5) {
|
||||||
|
if (ctx->stack.count > 1) {
|
||||||
|
if (shiftHeld) {
|
||||||
|
// Shift+Alt+Tab — focus the bottom window, raise it
|
||||||
|
wmRaiseWindow(&ctx->stack, &ctx->dirty, 0);
|
||||||
|
wmSetFocus(&ctx->stack, &ctx->dirty, ctx->stack.count - 1);
|
||||||
|
} else {
|
||||||
|
// Alt+Tab — send top window to bottom, focus new top
|
||||||
|
WindowT *top = ctx->stack.windows[ctx->stack.count - 1];
|
||||||
|
dirtyListAdd(&ctx->dirty, top->x, top->y, top->w, top->h);
|
||||||
|
|
||||||
|
// Shift all windows up
|
||||||
|
for (int32_t i = ctx->stack.count - 1; i > 0; i--) {
|
||||||
|
ctx->stack.windows[i] = ctx->stack.windows[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->stack.windows[0] = top;
|
||||||
|
top->focused = false;
|
||||||
|
|
||||||
|
// Focus the new top window
|
||||||
|
wmSetFocus(&ctx->stack, &ctx->dirty, ctx->stack.count - 1);
|
||||||
|
dirtyListAdd(&ctx->dirty, ctx->stack.windows[ctx->stack.count - 1]->x,
|
||||||
|
ctx->stack.windows[ctx->stack.count - 1]->y,
|
||||||
|
ctx->stack.windows[ctx->stack.count - 1]->w,
|
||||||
|
ctx->stack.windows[ctx->stack.count - 1]->h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alt+F4 — close focused window
|
||||||
|
if (ascii == 0 && scancode == 0x6B) {
|
||||||
|
if (ctx->stack.focusedIdx >= 0) {
|
||||||
|
WindowT *win = ctx->stack.windows[ctx->stack.focusedIdx];
|
||||||
|
|
||||||
|
if (win->onClose) {
|
||||||
|
win->onClose(win);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// F10 — activate menu bar
|
||||||
|
if (ascii == 0 && scancode == 0x44) {
|
||||||
|
if (ctx->stack.focusedIdx >= 0) {
|
||||||
|
WindowT *win = ctx->stack.windows[ctx->stack.focusedIdx];
|
||||||
|
|
||||||
|
if (win->menuBar && win->menuBar->menuCount > 0) {
|
||||||
|
if (ctx->popup.active) {
|
||||||
|
// F10 again closes the menu
|
||||||
|
dirtyListAdd(&ctx->dirty, ctx->popup.popupX, ctx->popup.popupY,
|
||||||
|
ctx->popup.popupW, ctx->popup.popupH);
|
||||||
|
ctx->popup.active = false;
|
||||||
|
} else {
|
||||||
|
dispatchAccelKey(ctx, win->menuBar->menus[0].accelKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Keyboard move/resize mode — intercept all keys
|
// Keyboard move/resize mode — intercept all keys
|
||||||
if (ctx->kbMoveResize.mode != KbModeNoneE) {
|
if (ctx->kbMoveResize.mode != KbModeNoneE) {
|
||||||
WindowT *kbWin = findWindowById(ctx, ctx->kbMoveResize.windowId);
|
WindowT *kbWin = findWindowById(ctx, ctx->kbMoveResize.windowId);
|
||||||
|
|
@ -1790,6 +1863,93 @@ static void pollKeyboard(AppContextT *ctx) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tab / Shift-Tab — cycle focus between widgets
|
||||||
|
// Tab: scancode=0x0F, ascii=0x09
|
||||||
|
// Shift-Tab: scancode=0x0F, ascii=0x00
|
||||||
|
if (scancode == 0x0F && (ascii == 0x09 || ascii == 0)) {
|
||||||
|
if (ctx->stack.focusedIdx >= 0) {
|
||||||
|
WindowT *win = ctx->stack.windows[ctx->stack.focusedIdx];
|
||||||
|
|
||||||
|
if (win->widgetRoot) {
|
||||||
|
// Find currently focused widget
|
||||||
|
WidgetT *current = NULL;
|
||||||
|
WidgetT *fstack[64];
|
||||||
|
int32_t ftop = 0;
|
||||||
|
fstack[ftop++] = win->widgetRoot;
|
||||||
|
|
||||||
|
while (ftop > 0) {
|
||||||
|
WidgetT *w = fstack[--ftop];
|
||||||
|
|
||||||
|
if (w->focused && widgetIsFocusable(w->type)) {
|
||||||
|
// Don't tab out of the terminal — it swallows Tab
|
||||||
|
if (w->type == WidgetAnsiTermE) {
|
||||||
|
current = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = w;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
|
||||||
|
if (c->visible && ftop < 64) {
|
||||||
|
fstack[ftop++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminal swallowed Tab — send to widget system instead
|
||||||
|
if (current == NULL) {
|
||||||
|
// Check if a terminal is focused
|
||||||
|
ftop = 0;
|
||||||
|
fstack[ftop++] = win->widgetRoot;
|
||||||
|
bool termFocused = false;
|
||||||
|
|
||||||
|
while (ftop > 0) {
|
||||||
|
WidgetT *w = fstack[--ftop];
|
||||||
|
|
||||||
|
if (w->focused && w->type == WidgetAnsiTermE) {
|
||||||
|
termFocused = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
|
||||||
|
if (c->visible && ftop < 64) {
|
||||||
|
fstack[ftop++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (termFocused) {
|
||||||
|
// Terminal has focus — send Tab to it
|
||||||
|
if (win->onKey) {
|
||||||
|
win->onKey(win, ascii ? ascii : (scancode | 0x100), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetT *next;
|
||||||
|
|
||||||
|
if (ascii == 0x09) {
|
||||||
|
next = widgetFindNextFocusable(win->widgetRoot, current);
|
||||||
|
} else {
|
||||||
|
next = widgetFindPrevFocusable(win->widgetRoot, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
sOpenPopup = NULL;
|
||||||
|
widgetClearFocus(win->widgetRoot);
|
||||||
|
next->focused = true;
|
||||||
|
wgtInvalidate(win->widgetRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Send to focused window
|
// Send to focused window
|
||||||
if (ctx->stack.focusedIdx >= 0) {
|
if (ctx->stack.focusedIdx >= 0) {
|
||||||
WindowT *win = ctx->stack.windows[ctx->stack.focusedIdx];
|
WindowT *win = ctx->stack.windows[ctx->stack.focusedIdx];
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,72 @@ WidgetT *widgetFindNextFocusable(WidgetT *root, WidgetT *after) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// widgetFindPrevFocusable
|
||||||
|
// ============================================================
|
||||||
|
//
|
||||||
|
// Depth-first walk collecting all visible+enabled focusable widgets,
|
||||||
|
// then returns the one before 'before'. Wraps around if needed.
|
||||||
|
|
||||||
|
WidgetT *widgetFindPrevFocusable(WidgetT *root, WidgetT *before) {
|
||||||
|
WidgetT *list[128];
|
||||||
|
int32_t count = 0;
|
||||||
|
|
||||||
|
// Collect all focusable widgets via depth-first traversal
|
||||||
|
WidgetT *stack[64];
|
||||||
|
int32_t top = 0;
|
||||||
|
stack[top++] = root;
|
||||||
|
|
||||||
|
while (top > 0) {
|
||||||
|
WidgetT *w = stack[--top];
|
||||||
|
|
||||||
|
if (!w->visible || !w->enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widgetIsFocusable(w->type) && count < 128) {
|
||||||
|
list[count++] = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push children in reverse order so first child is processed first
|
||||||
|
WidgetT *children[64];
|
||||||
|
int32_t childCount = 0;
|
||||||
|
|
||||||
|
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
|
||||||
|
if (childCount < 64) {
|
||||||
|
children[childCount++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32_t i = childCount - 1; i >= 0; i--) {
|
||||||
|
if (top < 64) {
|
||||||
|
stack[top++] = children[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find 'before' in the list
|
||||||
|
int32_t idx = -1;
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < count; i++) {
|
||||||
|
if (list[i] == before) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx <= 0) {
|
||||||
|
return list[count - 1]; // Wrap to last
|
||||||
|
}
|
||||||
|
|
||||||
|
return list[idx - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// widgetFrameBorderWidth
|
// widgetFrameBorderWidth
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ 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 *widgetFindByAccel(WidgetT *root, char key);
|
||||||
WidgetT *widgetFindNextFocusable(WidgetT *root, WidgetT *after);
|
WidgetT *widgetFindNextFocusable(WidgetT *root, WidgetT *after);
|
||||||
|
WidgetT *widgetFindPrevFocusable(WidgetT *root, WidgetT *before);
|
||||||
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 widgetIsFocusable(WidgetTypeE type);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue