Working on keyboard control of GUI.

This commit is contained in:
Scott Duensing 2026-03-12 20:11:28 -05:00
parent cd8da88df7
commit 2fdba061ce
2 changed files with 188 additions and 1 deletions

View file

@ -320,22 +320,30 @@ static bool dispatchAccelKey(AppContextT *ctx, char key) {
if (target) { if (target) {
switch (target->type) { switch (target->type) {
case WidgetButtonE: case WidgetButtonE:
widgetClearFocus(win->widgetRoot);
target->focused = true;
target->as.button.pressed = true; target->as.button.pressed = true;
sAccelPressedBtn = target; sAccelPressedBtn = target;
wgtInvalidate(target); wgtInvalidate(target);
return true; return true;
case WidgetCheckboxE: case WidgetCheckboxE:
widgetClearFocus(win->widgetRoot);
target->focused = true;
widgetCheckboxOnMouse(target); widgetCheckboxOnMouse(target);
wgtInvalidate(target); wgtInvalidate(target);
return true; return true;
case WidgetRadioE: case WidgetRadioE:
widgetClearFocus(win->widgetRoot);
target->focused = true;
widgetRadioOnMouse(target); widgetRadioOnMouse(target);
wgtInvalidate(target); wgtInvalidate(target);
return true; return true;
case WidgetImageButtonE: case WidgetImageButtonE:
widgetClearFocus(win->widgetRoot);
target->focused = true;
if (target->onClick) { if (target->onClick) {
target->onClick(target); target->onClick(target);
} }

View file

@ -121,7 +121,7 @@ void widgetOnKey(WindowT *win, int32_t key, int32_t mod) {
while (top > 0) { while (top > 0) {
WidgetT *w = stack[--top]; WidgetT *w = stack[--top];
if (w->focused && (w->type == WidgetTextInputE || w->type == WidgetComboBoxE || w->type == WidgetDropdownE || w->type == WidgetAnsiTermE || w->type == WidgetTreeViewE || w->type == WidgetListBoxE)) { if (w->focused && (w->type == WidgetTextInputE || w->type == WidgetComboBoxE || w->type == WidgetDropdownE || w->type == WidgetAnsiTermE || w->type == WidgetTreeViewE || w->type == WidgetListBoxE || w->type == WidgetButtonE || w->type == WidgetImageButtonE || w->type == WidgetCheckboxE || w->type == WidgetRadioE || w->type == WidgetSliderE)) {
focus = w; focus = w;
break; break;
} }
@ -156,6 +156,180 @@ void widgetOnKey(WindowT *win, int32_t key, int32_t mod) {
return; return;
} }
// Handle button keyboard activation
if (focus->type == WidgetButtonE) {
if (key == ' ' || key == 0x0D) {
focus->as.button.pressed = true;
wgtInvalidate(focus);
focus->as.button.pressed = false;
if (focus->onClick) {
focus->onClick(focus);
}
wgtInvalidate(focus);
}
return;
}
// Handle image button keyboard activation
if (focus->type == WidgetImageButtonE) {
if (key == ' ' || key == 0x0D) {
focus->as.imageButton.pressed = true;
wgtInvalidate(focus);
focus->as.imageButton.pressed = false;
if (focus->onClick) {
focus->onClick(focus);
}
wgtInvalidate(focus);
}
return;
}
// Handle checkbox keyboard toggle
if (focus->type == WidgetCheckboxE) {
if (key == ' ' || key == 0x0D) {
focus->as.checkbox.checked = !focus->as.checkbox.checked;
if (focus->onChange) {
focus->onChange(focus);
}
wgtInvalidate(focus);
}
return;
}
// Handle radio button keyboard navigation
if (focus->type == WidgetRadioE) {
if (key == ' ' || key == 0x0D) {
// Select this radio
if (focus->parent && focus->parent->type == WidgetRadioGroupE) {
focus->parent->as.radioGroup.selectedIdx = focus->as.radio.index;
if (focus->parent->onChange) {
focus->parent->onChange(focus->parent);
}
}
wgtInvalidate(focus);
} else if (key == (0x50 | 0x100) || key == (0x4D | 0x100)) {
// Down or Right — next radio in group
if (focus->parent && focus->parent->type == WidgetRadioGroupE) {
WidgetT *next = NULL;
for (WidgetT *s = focus->nextSibling; s; s = s->nextSibling) {
if (s->type == WidgetRadioE && s->visible && s->enabled) {
next = s;
break;
}
}
if (next) {
focus->focused = false;
next->focused = true;
next->parent->as.radioGroup.selectedIdx = next->as.radio.index;
if (next->parent->onChange) {
next->parent->onChange(next->parent);
}
wgtInvalidate(next);
}
}
} else if (key == (0x48 | 0x100) || key == (0x4B | 0x100)) {
// Up or Left — previous radio in group
if (focus->parent && focus->parent->type == WidgetRadioGroupE) {
WidgetT *prev = NULL;
for (WidgetT *s = focus->parent->firstChild; s && s != focus; s = s->nextSibling) {
if (s->type == WidgetRadioE && s->visible && s->enabled) {
prev = s;
}
}
if (prev) {
focus->focused = false;
prev->focused = true;
prev->parent->as.radioGroup.selectedIdx = prev->as.radio.index;
if (prev->parent->onChange) {
prev->parent->onChange(prev->parent);
}
wgtInvalidate(prev);
}
}
}
return;
}
// Handle slider keyboard adjustment
if (focus->type == WidgetSliderE) {
int32_t step = 1;
int32_t range = focus->as.slider.maxValue - focus->as.slider.minValue;
if (range > 100) {
step = range / 100;
}
if (focus->as.slider.vertical) {
if (key == (0x48 | 0x100)) {
// Up — decrease value
focus->as.slider.value -= step;
} else if (key == (0x50 | 0x100)) {
// Down — increase value
focus->as.slider.value += step;
} else if (key == (0x47 | 0x100)) {
// Home — minimum
focus->as.slider.value = focus->as.slider.minValue;
} else if (key == (0x4F | 0x100)) {
// End — maximum
focus->as.slider.value = focus->as.slider.maxValue;
} else {
return;
}
} else {
if (key == (0x4B | 0x100)) {
// Left — decrease value
focus->as.slider.value -= step;
} else if (key == (0x4D | 0x100)) {
// Right — increase value
focus->as.slider.value += step;
} else if (key == (0x47 | 0x100)) {
// Home — minimum
focus->as.slider.value = focus->as.slider.minValue;
} else if (key == (0x4F | 0x100)) {
// End — maximum
focus->as.slider.value = focus->as.slider.maxValue;
} else {
return;
}
}
// Clamp
if (focus->as.slider.value < focus->as.slider.minValue) {
focus->as.slider.value = focus->as.slider.minValue;
}
if (focus->as.slider.value > focus->as.slider.maxValue) {
focus->as.slider.value = focus->as.slider.maxValue;
}
if (focus->onChange) {
focus->onChange(focus);
}
wgtInvalidate(focus);
return;
}
// Handle dropdown keyboard navigation // Handle dropdown keyboard navigation
if (focus->type == WidgetDropdownE) { if (focus->type == WidgetDropdownE) {
if (focus->as.dropdown.open) { if (focus->as.dropdown.open) {
@ -655,14 +829,17 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
} }
if (hit->type == WidgetButtonE && hit->enabled) { if (hit->type == WidgetButtonE && hit->enabled) {
hit->focused = true;
widgetButtonOnMouse(hit); widgetButtonOnMouse(hit);
} }
if (hit->type == WidgetCheckboxE && hit->enabled) { if (hit->type == WidgetCheckboxE && hit->enabled) {
hit->focused = true;
widgetCheckboxOnMouse(hit); widgetCheckboxOnMouse(hit);
} }
if (hit->type == WidgetRadioE && hit->enabled) { if (hit->type == WidgetRadioE && hit->enabled) {
hit->focused = true;
widgetRadioOnMouse(hit); widgetRadioOnMouse(hit);
} }
@ -671,6 +848,7 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
} }
if (hit->type == WidgetImageButtonE && hit->enabled) { if (hit->type == WidgetImageButtonE && hit->enabled) {
hit->focused = true;
widgetImageButtonOnMouse(hit); widgetImageButtonOnMouse(hit);
} }
@ -695,6 +873,7 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
} }
if (hit->type == WidgetSliderE && hit->enabled) { if (hit->type == WidgetSliderE && hit->enabled) {
hit->focused = true;
widgetSliderOnMouse(hit, vx, vy); widgetSliderOnMouse(hit, vx, vy);
} }