diff --git a/dvx/dvxApp.c b/dvx/dvxApp.c index ce92592..2d77b58 100644 --- a/dvx/dvxApp.c +++ b/dvx/dvxApp.c @@ -320,22 +320,30 @@ static bool dispatchAccelKey(AppContextT *ctx, char key) { if (target) { switch (target->type) { case WidgetButtonE: + widgetClearFocus(win->widgetRoot); + target->focused = true; target->as.button.pressed = true; sAccelPressedBtn = target; wgtInvalidate(target); return true; case WidgetCheckboxE: + widgetClearFocus(win->widgetRoot); + target->focused = true; widgetCheckboxOnMouse(target); wgtInvalidate(target); return true; case WidgetRadioE: + widgetClearFocus(win->widgetRoot); + target->focused = true; widgetRadioOnMouse(target); wgtInvalidate(target); return true; case WidgetImageButtonE: + widgetClearFocus(win->widgetRoot); + target->focused = true; if (target->onClick) { target->onClick(target); } diff --git a/dvx/widgets/widgetEvent.c b/dvx/widgets/widgetEvent.c index 25876cf..a97f092 100644 --- a/dvx/widgets/widgetEvent.c +++ b/dvx/widgets/widgetEvent.c @@ -121,7 +121,7 @@ void widgetOnKey(WindowT *win, int32_t key, int32_t mod) { while (top > 0) { 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; break; } @@ -156,6 +156,180 @@ void widgetOnKey(WindowT *win, int32_t key, int32_t mod) { 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 if (focus->type == WidgetDropdownE) { 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) { + hit->focused = true; widgetButtonOnMouse(hit); } if (hit->type == WidgetCheckboxE && hit->enabled) { + hit->focused = true; widgetCheckboxOnMouse(hit); } if (hit->type == WidgetRadioE && hit->enabled) { + hit->focused = true; 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) { + hit->focused = true; 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) { + hit->focused = true; widgetSliderOnMouse(hit, vx, vy); }