diff --git a/apps/installer.cpp b/apps/installer.cpp index 8f0b9c2..fda669a 100644 --- a/apps/installer.cpp +++ b/apps/installer.cpp @@ -1093,7 +1093,7 @@ void _start() { userNameTextbox->messageUser = UserNameTextboxMessage; EsTextDisplayCreate(table, ES_CELL_H_RIGHT | ES_CELL_V_TOP, ES_STYLE_TEXT_RADIO_GROUP_LABEL, INTERFACE_STRING(InstallerSystemFont)); - EsPanel *fonts = EsPanelCreate(table, ES_CELL_H_LEFT); + EsPanel *fonts = EsPanelCreate(table, ES_CELL_H_LEFT | ES_PANEL_RADIO_GROUP); EsButton *button = EsButtonCreate(fonts, ES_BUTTON_RADIOBOX | ES_CELL_H_EXPAND, 0, INTERFACE_STRING(InstallerFontDefault)); button->userData = (void *) "Inter"; EsButtonOnCommand(button, ButtonFont); diff --git a/desktop/gui.cpp b/desktop/gui.cpp index 2bbcfb4..a38947b 100644 --- a/desktop/gui.cpp +++ b/desktop/gui.cpp @@ -118,6 +118,7 @@ void InspectorNotifyElementContentChanged(EsElement *element); #define UI_STATE_USE_MEASUREMENT_CACHE (1 << 20) #define UI_STATE_CHECK_VISIBLE (1 << 21) #define UI_STATE_INSPECTING (1 << 22) +#define UI_STATE_RADIO_GROUP (1 << 23) struct EsElement : EsElementPublic { EsUICallback messageClass; @@ -197,6 +198,10 @@ struct EsElement : EsElementPublic { return true; } + bool IsTabTraversable() { + return IsFocusable() && (~flags & ES_ELEMENT_NOT_TAB_TRAVERSABLE) && (!parent || ~parent->state & UI_STATE_RADIO_GROUP); + } + bool RefreshStyleState(); // Returns true if any observed bits have changed. void RefreshStyle(UIStyleKey *oldStyleKey = nullptr, bool alreadyRefreshStyleState = false, bool force = false); bool StartAnimating(); @@ -3162,15 +3167,21 @@ int ProcessPanelMessage(EsElement *element, EsMessage *message) { } } else if (message->type == ES_MSG_KEY_DOWN) { if (!(panel->flags & (ES_PANEL_TABLE | ES_PANEL_SWITCHER)) - && panel->window->focused && panel->window->focused->parent == panel - && (panel->flags & ES_PANEL_HORIZONTAL)) { - bool reverse = panel->flags & ES_PANEL_REVERSE, - left = message->keyboard.scancode == ES_SCANCODE_LEFT_ARROW, - right = message->keyboard.scancode == ES_SCANCODE_RIGHT_ARROW; + && panel->window->focused && panel->window->focused->parent == panel) { + bool reverse = panel->flags & ES_PANEL_REVERSE; + bool left = message->keyboard.scancode == ES_SCANCODE_LEFT_ARROW; + bool right = message->keyboard.scancode == ES_SCANCODE_RIGHT_ARROW; + bool up = message->keyboard.scancode == ES_SCANCODE_UP_ARROW; + bool down = message->keyboard.scancode == ES_SCANCODE_DOWN_ARROW; + + if (~panel->flags & ES_PANEL_HORIZONTAL) { + left = up; + right = down; + } + + EsElement *focus = nullptr; if ((left && !reverse) || (right && reverse)) { - EsElement *focus = nullptr; - for (uintptr_t i = 0; i < panel->GetChildCount(); i++) { EsElement *child = panel->GetChild(i); @@ -3184,13 +3195,7 @@ int ProcessPanelMessage(EsElement *element, EsMessage *message) { focus = child; } } - - if (focus) { - EsElementFocus(focus); - } } else if ((left && reverse) || (right && !reverse)) { - EsElement *focus = nullptr; - for (uintptr_t i = panel->GetChildCount(); i > 0; i--) { EsElement *child = panel->GetChild(i - 1); @@ -3204,13 +3209,18 @@ int ProcessPanelMessage(EsElement *element, EsMessage *message) { focus = child; } } - - if (focus) { - EsElementFocus(focus); - } } else { return 0; } + + if (focus) { + EsElementFocus(focus); + + if (panel->flags & ES_PANEL_RADIO_GROUP) { + EsMessage m = { .type = ES_MSG_MOUSE_LEFT_CLICK }; + EsMessageSend(focus, &m); + } + } } else { return 0; } @@ -3322,9 +3332,10 @@ EsPanel *EsPanelCreate(EsElement *parent, uint64_t flags, const EsStyle *style) panel->Initialise(parent, flags, ProcessPanelMessage, style); panel->cName = "panel"; - if (flags & ES_PANEL_Z_STACK) panel->state |= UI_STATE_Z_STACK; - if (flags & ES_PANEL_HORIZONTAL) panel->flags |= ES_ELEMENT_LAYOUT_HINT_HORIZONTAL; - if (flags & ES_PANEL_REVERSE) panel->flags |= ES_ELEMENT_LAYOUT_HINT_REVERSE; + if (flags & ES_PANEL_Z_STACK) panel->state |= UI_STATE_Z_STACK; + if (flags & ES_PANEL_RADIO_GROUP) panel->state |= UI_STATE_RADIO_GROUP, panel->flags |= ES_ELEMENT_FOCUSABLE; + if (flags & ES_PANEL_HORIZONTAL) panel->flags |= ES_ELEMENT_LAYOUT_HINT_HORIZONTAL; + if (flags & ES_PANEL_REVERSE) panel->flags |= ES_ELEMENT_LAYOUT_HINT_REVERSE; panel->scroll.Setup(panel, ((flags & ES_PANEL_H_SCROLL_FIXED) ? SCROLL_MODE_FIXED : (flags & ES_PANEL_H_SCROLL_AUTO) ? SCROLL_MODE_AUTO : SCROLL_MODE_NONE), @@ -3507,6 +3518,20 @@ void EsPanelStartMovementAnimation(EsPanel *panel, float timeMultiplier) { EsElementRelayout(panel); } +EsButton *EsPanelRadioGroupGetChecked(EsPanel *panel) { + EsMessageMutexCheck(); + EsAssert(panel->state & UI_STATE_RADIO_GROUP); + EsAssert(panel->GetChildCount()); + + for (uintptr_t i = 0; i < panel->GetChildCount(); i++) { + if (ES_CHECK_CHECKED == EsButtonGetCheck((EsButton *) panel->GetChild(i))) { + return (EsButton *) panel->GetChild(i); + } + } + + return (EsButton *) panel->GetChild(0); +} + // --------------------------------- Canvas panes. struct EsCanvasPane : EsElement { @@ -6641,8 +6666,13 @@ bool UIHandleKeyMessage(EsWindow *window, EsMessage *message) { EsElement *element = window->focused ?: window; EsElement *start = element; - do element = UITabTraversalDo(element, message->keyboard.modifiers & ES_MODIFIER_SHIFT); - while ((!element->IsFocusable() || (element->flags & ES_ELEMENT_NOT_TAB_TRAVERSABLE)) && element != start); + do { + element = UITabTraversalDo(element, message->keyboard.modifiers & ES_MODIFIER_SHIFT); + } while (!element->IsTabTraversable() && element != start); + + if (element->state & UI_STATE_RADIO_GROUP) { + element = EsPanelRadioGroupGetChecked((EsPanel *) element); + } EsElementFocus(element, ES_ELEMENT_FOCUS_ENSURE_VISIBLE | ES_ELEMENT_FOCUS_FROM_KEYBOARD); return true; diff --git a/desktop/os.header b/desktop/os.header index 35c929f..6805604 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -484,6 +484,8 @@ define ES_PANEL_V_BOTTOM (1 << 21) define ES_PANEL_V_CENTER (1 << 22) define ES_PANEL_V_JUSTIFY (1 << 23) +define ES_PANEL_RADIO_GROUP (1 << 30) // Determines how arrow keys/tabs behave. + define ES_TEXTBOX_MULTILINE (1 << 0) define ES_TEXTBOX_EDIT_BASED (1 << 1) define ES_TEXTBOX_MARGIN (1 << 2) @@ -2432,6 +2434,8 @@ function void EsPanelTableSetChildCells(EsPanel *panel); // Automatically set th function void EsPanelSwitchTo(EsPanel *panel, EsElement *targetChild, EsTransitionType transitionType, uint32_t flags = ES_FLAGS_DEFAULT, float timeMultiplier = 1); // TODO More customization of transitions? function void EsPanelStartMovementAnimation(EsPanel *panel, float timeMultiplier = 1); // TODO More customization. +function EsButton *EsPanelRadioGroupGetChecked(EsPanel *panel); + // Static displays. function EsIconDisplay *EsIconDisplayCreate(EsElement *parent, uint64_t flags = ES_FLAGS_DEFAULT, const EsStyle *style = ES_NULL, uint32_t iconID = 0); diff --git a/desktop/settings.cpp b/desktop/settings.cpp index 590e1ab..88c3703 100644 --- a/desktop/settings.cpp +++ b/desktop/settings.cpp @@ -681,7 +681,7 @@ void SettingsPageTheme(EsElement *element, SettingsPage *page) { }; EsTextDisplayCreate(table, ES_CELL_H_RIGHT, 0, INTERFACE_STRING(DesktopSettingsThemeWindowColor)); - EsPanel *panel = EsPanelCreate(table, ES_CELL_H_LEFT | ES_PANEL_HORIZONTAL); + EsPanel *panel = EsPanelCreate(table, ES_CELL_H_LEFT | ES_PANEL_HORIZONTAL | ES_PANEL_RADIO_GROUP); uint8_t windowColor = EsSystemConfigurationReadInteger(EsLiteral("general"), EsLiteral("window_color")); for (uintptr_t i = 0; i < sizeof(windowColors) / sizeof(windowColors[0]); i++) { diff --git a/util/api_table.ini b/util/api_table.ini index ef6387e..13c1e94 100644 --- a/util/api_table.ini +++ b/util/api_table.ini @@ -455,3 +455,4 @@ _EsWindowGetHandle=453 _EsUISetFont=454 EsWorkQueue=455 EsWorkIsExiting=456 +EsPanelRadioGroupGetChecked=457