From f809b80a688b8cd0a5a8bcb578d4cce1562fff5e Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sat, 31 Oct 2020 14:18:09 +0200 Subject: Preferences: Basic key bindings UI One can now bind keys in Preferences. The configured keys aren't yet saved, though. --- src/ui/bindingswidget.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ui/bindingswidget.h | 28 +++++++ src/ui/keys.c | 123 +++++++++++++++++++++--------- src/ui/keys.h | 20 +++-- src/ui/listwidget.c | 14 ++++ src/ui/listwidget.h | 2 + src/ui/sidebarwidget.c | 2 - src/ui/util.c | 27 +++++-- src/ui/util.h | 1 + 9 files changed, 363 insertions(+), 47 deletions(-) create mode 100644 src/ui/bindingswidget.c create mode 100644 src/ui/bindingswidget.h (limited to 'src/ui') diff --git a/src/ui/bindingswidget.c b/src/ui/bindingswidget.c new file mode 100644 index 00000000..4ce6ea4d --- /dev/null +++ b/src/ui/bindingswidget.c @@ -0,0 +1,193 @@ +/* Copyright 2020 Jaakko Keränen + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "bindingswidget.h" +#include "listwidget.h" +#include "keys.h" +#include "command.h" +#include "util.h" +#include "app.h" + +iDeclareType(BindingItem) +typedef iListItemClass iBindingItemClass; + +struct Impl_BindingItem { + iListItem listItem; + iString label; + iString key; + int id; + iBool isWaitingForEvent; +}; + +void init_BindingItem(iBindingItem *d) { + init_ListItem(&d->listItem); + init_String(&d->label); + init_String(&d->key); + d->id = 0; + d->isWaitingForEvent = iFalse; +} + +void deinit_BindingItem(iBindingItem *d) { + deinit_String(&d->key); + deinit_String(&d->label); +} + +static void setKey_BindingItem_(iBindingItem *d, int key, int mods) { + setKey_Binding(d->id, key, mods); + clear_String(&d->key); + toString_Sym(key, mods, &d->key); +} + +static void draw_BindingItem_(const iBindingItem *d, iPaint *p, iRect itemRect, + const iListWidget *list); + +iBeginDefineSubclass(BindingItem, ListItem) + .draw = (iAny *) draw_BindingItem_, +iEndDefineSubclass(BindingItem) + +iDefineObjectConstruction(BindingItem) + +/*----------------------------------------------------------------------------------------------*/ + +struct Impl_BindingsWidget { + iWidget widget; + iListWidget *list; + size_t activePos; +}; + +iDefineObjectConstruction(BindingsWidget) + +static int cmpId_BindingItem_(const iListItem **item1, const iListItem **item2) { + const iBindingItem *d = (const iBindingItem *) *item1, *other = (const iBindingItem *) *item2; + return iCmp(d->id, other->id); +} + +static void updateItems_BindingsWidget_(iBindingsWidget *d) { + clear_ListWidget(d->list); + iConstForEach(PtrArray, i, list_Keys()) { + const iBinding *bind = i.ptr; + if (isEmpty_String(&bind->label)) { + /* Only the ones with label are user-changeable. */ + continue; + } + iBindingItem *item = new_BindingItem(); + item->id = bind->id; + set_String(&item->label, &bind->label); + toString_Sym(bind->key, bind->mods, &item->key); + addItem_ListWidget(d->list, item); + } + sort_ListWidget(d->list, cmpId_BindingItem_); + updateVisible_ListWidget(d->list); + invalidate_ListWidget(d->list); +} + +void init_BindingsWidget(iBindingsWidget *d) { + iWidget *w = as_Widget(d); + init_Widget(w); + setFlags_Widget(w, resizeChildren_WidgetFlag, iTrue); + d->activePos = iInvalidPos; + d->list = new_ListWidget(); + setItemHeight_ListWidget(d->list, lineHeight_Text(uiLabel_FontId) * 1.5f); + addChild_Widget(w, iClob(d->list)); + updateItems_BindingsWidget_(d); +} + +void deinit_BindingsWidget(iBindingsWidget *d) { + /* nothing to do */ + iUnused(d); +} + +static void setActiveItem_BindingsWidget_(iBindingsWidget *d, size_t pos) { + if (d->activePos != iInvalidPos) { + iBindingItem *item = item_ListWidget(d->list, d->activePos); + item->isWaitingForEvent = iFalse; + invalidateItem_ListWidget(d->list, d->activePos); + } + d->activePos = pos; + if (d->activePos != iInvalidPos) { + iBindingItem *item = item_ListWidget(d->list, d->activePos); + item->isWaitingForEvent = iTrue; + invalidateItem_ListWidget(d->list, d->activePos); + } +} + +static iBool processEvent_BindingsWidget_(iBindingsWidget *d, const SDL_Event *ev) { + iWidget * w = as_Widget(d); + const char *cmd = command_UserEvent(ev); + if (isCommand_Widget(w, ev, "list.clicked")) { + setActiveItem_BindingsWidget_(d, arg_Command(cmd)); + return iTrue; + } + /* Waiting for a keypress? */ + if (d->activePos != iInvalidPos) { + if (ev->type == SDL_KEYDOWN && !isMod_Sym(ev->key.keysym.sym)) { + setKey_BindingItem_(item_ListWidget(d->list, d->activePos), + ev->key.keysym.sym, + keyMods_Sym(ev->key.keysym.mod)); + setActiveItem_BindingsWidget_(d, iInvalidPos); + postCommand_App("bindings.changed"); + return iTrue; + } + } + return processEvent_Widget(w, ev); +} + +static void draw_BindingsWidget_(const iBindingsWidget *d) { + const iWidget *w = constAs_Widget(d); + drawChildren_Widget(w); + drawBackground_Widget(w); /* kludge to allow drawing a top border over the list */ +} + +static void draw_BindingItem_(const iBindingItem *d, iPaint *p, iRect itemRect, + const iListWidget *list) { + const int font = uiLabel_FontId; + const int itemHeight = height_Rect(itemRect); + const int line = lineHeight_Text(font); + int fg = uiText_ColorId; + const iBool isPressing = isMouseDown_ListWidget(list) || d->isWaitingForEvent; + const iBool isHover = (isHover_Widget(constAs_Widget(list)) && + constHoverItem_ListWidget(list) == d); + if (isHover || isPressing) { + fg = isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId; + fillRect_Paint(p, + itemRect, + isPressing ? uiBackgroundPressed_ColorId + : uiBackgroundFramelessHover_ColorId); + } + const int y = top_Rect(itemRect) + (itemHeight - line) / 2; + drawRange_Text(font, + init_I2(left_Rect(itemRect) + 3 * gap_UI, y), + fg, + range_String(&d->label)); + drawAlign_Text(d->isWaitingForEvent ? uiContent_FontId : font, + init_I2(right_Rect(itemRect) - 3 * gap_UI, + y - (lineHeight_Text(uiContent_FontId) - line) / 2), + fg, + right_Alignment, + "%s", + d->isWaitingForEvent ? "\U0001F449 \u2328" : cstr_String(&d->key)); +} + +iBeginDefineSubclass(BindingsWidget, Widget) + .processEvent = (iAny *) processEvent_BindingsWidget_, + .draw = (iAny *) draw_BindingsWidget_, +iEndDefineSubclass(BindingsWidget) diff --git a/src/ui/bindingswidget.h b/src/ui/bindingswidget.h new file mode 100644 index 00000000..e1ed402c --- /dev/null +++ b/src/ui/bindingswidget.h @@ -0,0 +1,28 @@ +/* Copyright 2020 Jaakko Keränen + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#pragma once + +#include "widget.h" + +iDeclareWidgetClass(BindingsWidget) +iDeclareObjectConstruction(BindingsWidget) diff --git a/src/ui/keys.c b/src/ui/keys.c index 85304ef7..cc7dabef 100644 --- a/src/ui/keys.c +++ b/src/ui/keys.c @@ -24,12 +24,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "util.h" #include "app.h" -#include +#include iDeclareType(Keys) -static int cmp_Binding_(const void *a, const void *b) { - const iBinding *d = a, *other = b; +static int cmpPtr_Binding_(const void *a, const void *b) { + const iBinding *d = *(const void **) a, *other = *(const void **) b; const int cmp = iCmp(d->key, other->key); if (cmp == 0) { return iCmp(d->mods, other->mods); @@ -37,14 +37,17 @@ static int cmp_Binding_(const void *a, const void *b) { return cmp; } +/*----------------------------------------------------------------------------------------------*/ + struct Impl_Keys { - iSortedArray bindings; + iArray bindings; + iPtrSet lookup; /* quick key/mods lookup */ }; static iKeys keys_; static void clear_Keys_(iKeys *d) { - iForEach(Array, i, &d->bindings.values) { + iForEach(Array, i, &d->bindings) { iBinding *bind = i.value; deinit_String(&bind->command); deinit_String(&bind->label); @@ -52,27 +55,41 @@ static void clear_Keys_(iKeys *d) { } static void bindDefaults_(void) { - bind_Keys("scroll.top", SDLK_HOME, 0); - bind_Keys("scroll.bottom", SDLK_END, 0); - bind_Keys("scroll.step arg:-1", SDLK_UP, 0); - bind_Keys("scroll.step arg:1", SDLK_DOWN, 0); - bind_Keys("scroll.page arg:-1", SDLK_PAGEUP, 0); - bind_Keys("scroll.page arg:1", SDLK_PAGEDOWN, 0); - bind_Keys("scroll.page arg:-1", SDLK_SPACE, KMOD_SHIFT); - bind_Keys("scroll.page arg:1", SDLK_SPACE, 0); + /* TODO: This indirection could be used for localization, although all UI strings + would need to be similarly handled. */ + bindLabel_Keys(1, "scroll.top", SDLK_HOME, 0, "Jump to top"); + bindLabel_Keys(2, "scroll.bottom", SDLK_END, 0, "Jump to bottom"); + bindLabel_Keys(10, "scroll.step arg:-1", SDLK_UP, 0, "Scroll up"); + bindLabel_Keys(11, "scroll.step arg:1", SDLK_DOWN, 0, "Scroll down"); + bindLabel_Keys(20, "scroll.page arg:-1", SDLK_PAGEUP, 0, "Scroll up half a page"); + bindLabel_Keys(21, "scroll.page arg:1", SDLK_PAGEDOWN, 0, "Scroll down half a page"); + /* The following cannot currently be changed (built-in duplicates). */ + bind_Keys(1000, "scroll.page arg:-1", SDLK_SPACE, KMOD_SHIFT); + bind_Keys(1001, "scroll.page arg:1", SDLK_SPACE, 0); } static iBinding *find_Keys_(iKeys *d, int key, int mods) { size_t pos; - if (locate_SortedArray(&d->bindings, &(iBinding){ .key = key, .mods = mods }, &pos)) { - return at_SortedArray(&d->bindings, pos); + const iBinding elem = { .key = key, .mods = mods }; + if (locate_PtrSet(&d->lookup, &elem, &pos)) { + return at_PtrSet(&d->lookup, pos); + } + return NULL; +} + +static iBinding *findId_Keys_(iKeys *d, int id) { + iForEach(Array, i, &d->bindings) { + iBinding *bind = i.value; + if (bind->id == id) { + return bind; + } } return NULL; } static iBinding *findCommand_Keys_(iKeys *d, const char *command) { /* Note: O(n) */ - iForEach(Array, i, &d->bindings.values) { + iForEach(Array, i, &d->bindings) { iBinding *bind = i.value; if (!cmp_String(&bind->command, command)) { return bind; @@ -81,18 +98,37 @@ static iBinding *findCommand_Keys_(iKeys *d, const char *command) { return NULL; } +static void updateLookup_Keys_(iKeys *d) { + clear_PtrSet(&d->lookup); + iConstForEach(Array, i, &d->bindings) { + insert_PtrSet(&d->lookup, i.value); + } +} + +void setKey_Binding(int id, int key, int mods) { + iBinding *bind = findId_Keys_(&keys_, id); + if (bind) { + bind->key = key; + bind->mods = mods; + updateLookup_Keys_(&keys_); + } +} + /*----------------------------------------------------------------------------------------------*/ void init_Keys(void) { iKeys *d = &keys_; - init_SortedArray(&d->bindings, sizeof(iBinding), cmp_Binding_); + init_Array(&d->bindings, sizeof(iBinding)); + initCmp_PtrSet(&d->lookup, cmpPtr_Binding_); bindDefaults_(); + updateLookup_Keys_(d); } void deinit_Keys(void) { iKeys *d = &keys_; clear_Keys_(d); - deinit_SortedArray(&d->bindings); + deinit_PtrSet(&d->lookup); + deinit_Array(&d->bindings); } void load_Keys(const char *saveDir) { @@ -103,34 +139,42 @@ void save_Keys(const char *saveDir) { } -void bind_Keys(const char *command, int key, int mods) { +void bind_Keys(int id, const char *command, int key, int mods) { iKeys *d = &keys_; - iBinding *bind = find_Keys_(d, key, mods); - if (bind) { - setCStr_String(&bind->command, command); + iBinding *bind = findId_Keys_(d, id); + if (!bind) { + iBinding elem = { .id = id, .key = key, .mods = mods }; + initCStr_String(&elem.command, command); + init_String(&elem.label); + pushBack_Array(&d->bindings, &elem); } else { - iBinding bind; - bind.key = key; - bind.mods = mods; - initCStr_String(&bind.command, command); - init_String(&bind.label); - insert_SortedArray(&d->bindings, &bind); + setCStr_String(&bind->command, command); + bind->key = key; + bind->mods = mods; } } -void setLabel_Keys(const char *command, const char *label) { - iBinding *bind = findCommand_Keys_(&keys_, command); +void setLabel_Keys(int id, const char *label) { + iBinding *bind = findId_Keys_(&keys_, id); if (bind) { setCStr_String(&bind->label, label); } } -//const iString *label_Keys(const char *command) { - -//} - -//const char *shortcutLabel_Keys(const char *command) {} +#if 0 +const iString *label_Keys(const char *command) { + iKeys *d = &keys_; + /* TODO: A hash wouldn't hurt here. */ + iConstForEach(PtrSet, i, &d->bindings) { + const iBinding *bind = *i.value; + if (!cmp_String(&bind->command, command) && !isEmpty_String(&bind->label)) { + return &bind->label; + } + } + return collectNew_String(); +} +#endif iBool processEvent_Keys(const SDL_Event *ev) { iKeys *d = &keys_; @@ -147,3 +191,12 @@ iBool processEvent_Keys(const SDL_Event *ev) { const iBinding *findCommand_Keys(const char *command) { return findCommand_Keys_(&keys_, command); } + +const iPtrArray *list_Keys(void) { + iKeys *d = &keys_; + iPtrArray *list = collectNew_PtrArray(); + iConstForEach(Array, i, &d->bindings) { + pushBack_PtrArray(list, i.value); + } + return list; +} diff --git a/src/ui/keys.h b/src/ui/keys.h index 0892bd81..a4c8f348 100644 --- a/src/ui/keys.h +++ b/src/ui/keys.h @@ -23,6 +23,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include +#include #include #if defined (iPlatformApple) @@ -46,23 +47,32 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ iDeclareType(Binding) struct Impl_Binding { + int id; int key; int mods; iString command; iString label; }; +void setKey_Binding (int id, int key, int mods); + +/*----------------------------------------------------------------------------------------------*/ + void init_Keys (void); void deinit_Keys (void); void load_Keys (const char *saveDir); void save_Keys (const char *saveDir); -void bind_Keys (const char *command, int key, int mods); -void setLabel_Keys (const char *command, const char *label); -const iBinding *findCommand_Keys (const char *command); +void bind_Keys (int id, const char *command, int key, int mods); +void setLabel_Keys (int id, const char *label); -//const iString * label_Keys (const char *command); -//const char * shortcutLabel_Keys (const char *command); +iLocalDef void bindLabel_Keys(int id, const char *command, int key, int mods, const char *label) { + bind_Keys(id, command, key, mods); + setLabel_Keys(id, label); +} + +const iBinding *findCommand_Keys (const char *command); iBool processEvent_Keys (const SDL_Event *); +const iPtrArray *list_Keys (void); diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c index fb328c2f..02e1c728 100644 --- a/src/ui/listwidget.c +++ b/src/ui/listwidget.c @@ -120,6 +120,9 @@ void updateVisible_ListWidget(iListWidget *d) { const int contentSize = size_PtrArray(&d->items) * d->itemHeight; const iRect bounds = innerBounds_Widget(as_Widget(d)); const iBool wasVisible = isVisible_Widget(d->scroll); + if (area_Rect(bounds) == 0) { + return; + } setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_ListWidget_(d) }); setThumb_ScrollWidget(d->scroll, d->scrollY, @@ -245,11 +248,21 @@ void updateMouseHover_ListWidget(iListWidget *d) { setHoverItem_ListWidget_(d, itemIndex_ListWidget(d, mouse)); } +void sort_ListWidget(iListWidget *d, int (*cmp)(const iListItem **item1, const iListItem **item2)) { + sort_Array(&d->items, (iSortedArrayCompareElemFunc) cmp); +} + static void redrawHoverItem_ListWidget_(iListWidget *d) { insert_IntSet(&d->invalidItems, d->hoverItem); refresh_Widget(as_Widget(d)); } +static void sizeChanged_ListWidget_(iListWidget *d) { + printf("ListWidget %p size changed: %d x %d\n", d, d->widget.rect.size.x, d->widget.rect.size.y); fflush(stdout); + updateVisible_ListWidget(d); + invalidate_ListWidget(d); +} + static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { iWidget *w = as_Widget(d); if (isCommand_SDLEvent(ev)) { @@ -391,4 +404,5 @@ iBool isMouseDown_ListWidget(const iListWidget *d) { iBeginDefineSubclass(ListWidget, Widget) .processEvent = (iAny *) processEvent_ListWidget_, .draw = (iAny *) draw_ListWidget_, + .sizeChanged = (iAny *) sizeChanged_ListWidget_, iEndDefineSubclass(ListWidget) diff --git a/src/ui/listwidget.h b/src/ui/listwidget.h index da6303e9..11f1672e 100644 --- a/src/ui/listwidget.h +++ b/src/ui/listwidget.h @@ -64,6 +64,8 @@ void scrollOffset_ListWidget (iListWidget *, int offset); void updateVisible_ListWidget (iListWidget *); void updateMouseHover_ListWidget (iListWidget *); +void sort_ListWidget (iListWidget *, int (*cmp)(const iListItem **item1, const iListItem **item2)); + iAnyObject * item_ListWidget (iListWidget *, size_t index); iAnyObject * hoverItem_ListWidget (iListWidget *); diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index d6292f5b..c8571589 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -485,8 +485,6 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) /* Handle commands. */ if (isResize_UserEvent(ev)) { checkModeButtonLayout_SidebarWidget_(d); - updateVisible_ListWidget(d->list); - invalidate_ListWidget(d->list); } else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { const char *cmd = command_UserEvent(ev); diff --git a/src/ui/util.c b/src/ui/util.c index 44f7e089..ceab01b8 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "gmutil.h" #include "labelwidget.h" #include "inputwidget.h" +#include "bindingswidget.h" #include "keys.h" #include "widget.h" #include "text.h" @@ -105,6 +106,11 @@ void toString_Sym(int key, int kmods, iString *str) { } } +iBool isMod_Sym(int key) { + return key == SDLK_LALT || key == SDLK_RALT || key == SDLK_LCTRL || key == SDLK_RCTRL || + key == SDLK_LGUI || key == SDLK_RGUI || key == SDLK_LSHIFT || key == SDLK_RSHIFT; +} + int keyMods_Sym(int kmods) { kmods &= (KMOD_SHIFT | KMOD_ALT | KMOD_CTRL | KMOD_GUI); /* Don't treat left/right modifiers differently. */ @@ -920,12 +926,22 @@ iWidget *makeToggle_Widget(const char *id) { return toggle; } +static void appendFramelessTabPage_(iWidget *tabs, iWidget *page, const char *title, int shortcut, + int kmods) { + appendTabPage_Widget(tabs, page, title, shortcut, kmods); + setFlags_Widget( + (iWidget *) back_ObjectList(children_Widget(findChild_Widget(tabs, "tabs.buttons"))), + frameless_WidgetFlag, + iTrue); +} + static iWidget *appendTwoColumnPage_(iWidget *tabs, const char *title, int shortcut, iWidget **headings, iWidget **values) { iWidget *page = new_Widget(); setFlags_Widget(page, arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag | resizeHeightOfChildren_WidgetFlag | borderTop_WidgetFlag, iTrue); addChildFlags_Widget(page, iClob(new_Widget()), expand_WidgetFlag); + setPadding_Widget(page, 0, gap_UI, 0, gap_UI); iWidget *columns = new_Widget(); addChildFlags_Widget(page, iClob(columns), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); *headings = addChildFlags_Widget( @@ -933,11 +949,7 @@ static iWidget *appendTwoColumnPage_(iWidget *tabs, const char *title, int short *values = addChildFlags_Widget( columns, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag); addChildFlags_Widget(page, iClob(new_Widget()), expand_WidgetFlag); - appendTabPage_Widget(tabs, page, title, shortcut, shortcut ? KMOD_PRIMARY : 0); - setFlags_Widget( - (iWidget *) back_ObjectList(children_Widget(findChild_Widget(tabs, "tabs.buttons"))), - frameless_WidgetFlag, - iTrue); + appendFramelessTabPage_(tabs, page, title, shortcut, shortcut ? KMOD_PRIMARY : 0); return page; } @@ -1080,6 +1092,11 @@ iWidget *makePreferences_Widget(void) { addChild_Widget(headings, iClob(makeHeading_Widget("HTTP proxy:"))); setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.proxy.http"); } + /* Keybindings. */ { + iBindingsWidget *bind = new_BindingsWidget(); + setFlags_Widget(as_Widget(bind), borderTop_WidgetFlag, iTrue); + appendFramelessTabPage_(tabs, iClob(bind), "Bindings", '5', KMOD_PRIMARY); + } resizeToLargestPage_Widget(tabs); arrange_Widget(dlg); /* Set input field sizes. */ { diff --git a/src/ui/util.h b/src/ui/util.h index 9796b387..c0e3a04c 100644 --- a/src/ui/util.h +++ b/src/ui/util.h @@ -48,6 +48,7 @@ iLocalDef iBool isResize_UserEvent(const SDL_Event *d) { # define KMOD_SECONDARY KMOD_GUI #endif +iBool isMod_Sym (int key); int keyMods_Sym (int kmods); /* shift, alt, control, or gui */ void toString_Sym (int key, int kmods, iString *str); -- cgit v1.2.3