Hello,
this version is a prototype of the idea, Sam suggested -- I hope I
undestood it correctly.
This is a remarkable change to mconf in it's behavior; mainly it now
differentiates two focuses and operates differently, depending on the
focus. When the focus is on the menu, mconf is in i-search mode, for
a detailed description see 1/1.
As stated earlier, this is just a prototype, documentation is missing
and the code is not ready for a real commit.
Dirk
Dirk Gouders (1):
Isearch functionality for mconf
scripts/kconfig/lxdialog/dialog.h | 3 +
scripts/kconfig/lxdialog/menubox.c | 207 +++++++++++++++++++++++++++++++++----
2 files changed, 189 insertions(+), 21 deletions(-)
--
2.16.4
This patch prototypes isearch functionality for mconf based on an idea of
Sam Ravnborg:
* mconf now distinguishes if the focus is on the menu items or the
buttons below it.
* At startup focus is on the menu items and alphanumeric
characters or space entered are used to form a string that is
searched for.
* BACKSPACE is used to remove the last character of the search string.
* ENTER can be used to enter a submenu.
* Horizontal arrow keys put focus on the buttons with the known
behavior.
* The TAB key is now exclusively used to toggle the focus.
* Vertical arrow keys work anywhere.
* When the focus is on the buttons, all keys (e.g. hotkeys) work the
same as before this change.
* '\' can be used to search for other occurences of an already entered
string.
* To use y|n|m on an item, focus has to be on the buttons.
An example to navigate into the USB support menu under Device Drivers:
1) de # Navigates to the item "Device Drivers"
2) ENTER # Enter the submenu
3) USB # Navigate to the item "USB support"
4) ENTER # Enter the submenu
---
scripts/kconfig/lxdialog/dialog.h | 3 +
scripts/kconfig/lxdialog/menubox.c | 207 +++++++++++++++++++++++++++++++++----
2 files changed, 189 insertions(+), 21 deletions(-)
diff --git a/scripts/kconfig/lxdialog/dialog.h b/scripts/kconfig/lxdialog/dialog.h
index 0b00be5abaa6..02b036435919 100644
--- a/scripts/kconfig/lxdialog/dialog.h
+++ b/scripts/kconfig/lxdialog/dialog.h
@@ -50,6 +50,8 @@
#define TR(params) _tracef params
#define KEY_ESC 27
+#define KEY_CTRL_S 19
+#define KEY_ENTR 10
#define TAB 9
#define MAX_LEN 2048
#define BUF_SIZE (10*1024)
@@ -238,6 +240,7 @@ int dialog_checklist(const char *title, const char *prompt, int height,
int width, int list_height);
int dialog_inputbox(const char *title, const char *prompt, int height,
int width, const char *init);
+int do_isearch(char *str, int choice, int scroll);
/*
* This is the base for fictitious keys, which activate
diff --git a/scripts/kconfig/lxdialog/menubox.c b/scripts/kconfig/lxdialog/menubox.c
index d70cab36137e..e49280115b52 100644
--- a/scripts/kconfig/lxdialog/menubox.c
+++ b/scripts/kconfig/lxdialog/menubox.c
@@ -56,10 +56,17 @@
* fscanf would read in 'scroll', and eventually that value would get used.
*/
+#include <string.h>
#include "dialog.h"
+#define ISEARCH_LEN 32
+#define ISEARCH_INDICATOR_LEN (ISEARCH_LEN + 8)
+static char isearch_str[ISEARCH_LEN] = "";
+
static int menu_width, item_x;
+static int focus_on_buttons;
+
/*
* Print menu item
*/
@@ -85,7 +92,10 @@ static void do_print_item(WINDOW * win, const char *item, int line_y,
#else
wclrtoeol(win);
#endif
- wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
+ if (focus_on_buttons)
+ wattrset(win, selected ? A_UNDERLINE : dlg.item.atr);
+ else
+ wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
mvwaddstr(win, line_y, item_x, menu_item);
if (hotkey) {
wattrset(win, selected ? dlg.tag_key_selected.atr
@@ -105,6 +115,32 @@ do { \
do_print_item(menu, item_str(), choice, selected, !item_is_tag(':')); \
} while (0)
+
+/*
+* Print the isearch indicator.
+*/
+static void print_isearch(WINDOW * win, int y, int x, int height, bool isearch)
+{
+ unsigned char i = 0;
+
+ wmove(win, y, x);
+
+ y = y + height + 1;
+ wmove(win, y, x);
+
+ if (isearch) {
+ wattrset(win, dlg.button_key_inactive.atr);
+ waddstr(win, "isearch: ");
+ waddstr(win, isearch_str);
+ i = ISEARCH_INDICATOR_LEN - strlen(isearch_str);
+ }
+
+ wattrset(win, dlg.menubox_border.atr);
+
+ for ( ; i < ISEARCH_INDICATOR_LEN; i++ )
+ waddch(win, ACS_HLINE);
+}
+
/*
* Print the scroll indicators.
*/
@@ -157,6 +193,9 @@ static void print_buttons(WINDOW * win, int height, int width, int selected)
int x = width / 2 - 28;
int y = height - 2;
+ if (!focus_on_buttons)
+ selected = -1;
+
print_button(win, "Select", y, x, selected == 0);
print_button(win, " Exit ", y, x + 12, selected == 1);
print_button(win, " Help ", y, x + 24, selected == 2);
@@ -178,6 +217,32 @@ static void do_scroll(WINDOW *win, int *scroll, int n)
wrefresh(win);
}
+/*
+ * Incremental search for text in dialog menu entries.
+ * The search operates as a ring search, continuing at the top after
+ * the last entry has been visited.
+ *
+ * Returned is -1 if no match was found, else the absolute index of
+ * the matching item.
+ */
+int do_isearch(char *str, int choice, int scroll)
+{
+ int found = 0;
+ int i;
+
+ for (i = 0; i < item_count(); i++) {
+ item_set((choice + scroll + i)%item_count());
+ if (strcasestr(item_str(), str)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ return (choice + scroll + i)%item_count();
+ return -1;
+}
+
/*
* Display a menu for choosing among a number of options
*/
@@ -275,6 +340,7 @@ int dialog_menu(const char *title, const char *prompt,
print_arrows(dialog, item_count(), scroll,
box_y, box_x + item_x + 1, menu_height);
+ print_isearch(dialog, box_y, box_x + item_x + 5, menu_height, !focus_on_buttons);
print_buttons(dialog, height, width, 0);
wmove(menu, choice, item_x + 1);
wrefresh(menu);
@@ -285,22 +351,27 @@ int dialog_menu(const char *title, const char *prompt,
if (key < 256 && isalpha(key))
key = tolower(key);
- if (strchr("ynmh", key))
- i = max_choice;
- else {
- for (i = choice + 1; i < max_choice; i++) {
- item_set(scroll + i);
- j = first_alpha(item_str(), "YyNnMmHh");
- if (key == tolower(item_str()[j]))
- break;
- }
- if (i == max_choice)
- for (i = 0; i < max_choice; i++) {
+ if (focus_on_buttons) {
+ /*
+ * Find item matching hot key.
+ */
+ if (strchr("ynmh", key))
+ i = max_choice;
+ else {
+ for (i = choice + 1; i < max_choice; i++) {
item_set(scroll + i);
j = first_alpha(item_str(), "YyNnMmHh");
if (key == tolower(item_str()[j]))
break;
}
+ if (i == max_choice)
+ for (i = 0; i < max_choice; i++) {
+ item_set(scroll + i);
+ j = first_alpha(item_str(), "YyNnMmHh");
+ if (key == tolower(item_str()[j]))
+ break;
+ }
+ }
}
if (item_count() != 0 &&
@@ -370,16 +441,117 @@ int dialog_menu(const char *title, const char *prompt,
continue; /* wait for another key press */
}
+ if (!focus_on_buttons) {
+ i = -1;
+
+ if (key == '\n') {
+ /* save scroll info */
+ *s_scroll = scroll;
+ delwin(menu);
+ delwin(dialog);
+ item_set(scroll + choice);
+ item_set_selected(1);
+ isearch_str[0] = '\0';
+ return 0; /* 0 means first button "Select" */
+ }
+
+ if ( key == KEY_BACKSPACE && isearch_str[0] ) {
+ isearch_str[i = (strlen(isearch_str) - 1)] = '\0';
+ i = -1;
+ }
+
+ if (key < 256 && (isalnum(key) || key == ' ')) {
+ if (strlen(isearch_str) < ISEARCH_LEN - 1) {
+ isearch_str[i = strlen(isearch_str)] = key;
+ isearch_str[i+1] = '\0';
+ }
+
+ /* Remove highligt of current item */
+ print_item(scroll + choice, choice, FALSE);
+ i = do_isearch(isearch_str, choice, scroll);
+ }
+
+ if (key == '\\') {
+ /* Remove highligt of current item */
+ print_item(scroll + choice, choice, FALSE);
+ i = do_isearch(isearch_str, choice + 1, scroll);
+ }
+
+ /*
+ * Handle matches
+ */
+ if (i >= 0) {
+ i -= scroll;
+
+ if (i >= max_choice)
+ /*
+ * Handle matches below the currently visible menu entries.
+ */
+ while (i >= max_choice) {
+ do_scroll(menu, &scroll, 1);
+ i--;
+ print_item(max_choice + scroll - 1, max_choice - 1, false);
+ }
+ else if (i < 0)
+ /*
+ * Handle matches higher in the menu (ring search).
+ */
+ while (i < 0) {
+ do_scroll(menu, &scroll, -1);
+ i++;
+ print_item(scroll, 0, false);
+ }
+ choice = i;
+ } else {
+ i = choice;
+ goto a;
+ }
+
+ print_item(scroll + choice, choice, TRUE);
+ print_isearch(dialog, box_y, box_x + item_x + 5, menu_height, true);
+ print_arrows(dialog, item_count(), scroll,
+ box_y, box_x + item_x + 1, menu_height);
+
+ wnoutrefresh(dialog);
+ wrefresh(menu);
+ i = max_choice;
+ continue;
+ }
+ a:
switch (key) {
- case KEY_LEFT:
case TAB:
+ focus_on_buttons = 1 - focus_on_buttons;
+ print_isearch(dialog, box_y, box_x + item_x + 5, menu_height, !focus_on_buttons);
+
+ print_item(scroll + choice, choice, TRUE);
+ print_buttons(dialog, height, width, button);
+ wrefresh(menu);
+ break;
+ case KEY_LEFT:
case KEY_RIGHT:
+ focus_on_buttons = 1;
button = ((key == KEY_LEFT ? --button : ++button) < 0)
? 4 : (button > 4 ? 0 : button);
+ print_isearch(dialog, box_y, box_x + item_x + 5, menu_height, !focus_on_buttons);
+ print_item(scroll + choice, choice, TRUE);
print_buttons(dialog, height, width, button);
wrefresh(menu);
break;
+ case KEY_ESC:
+ key = on_key_esc(menu);
+ break;
+ case KEY_RESIZE:
+ on_key_resize();
+ delwin(menu);
+ delwin(dialog);
+ goto do_resize;
+ }
+
+ /*
+ * Focus is on buttons, handle keys appropriately
+ */
+ switch (key) {
case ' ':
case 's':
case 'y':
@@ -390,6 +562,7 @@ int dialog_menu(const char *title, const char *prompt,
case '?':
case 'z':
case '\n':
+ isearch_str[0] = '\0';
/* save scroll info */
*s_scroll = scroll;
delwin(menu);
@@ -421,14 +594,6 @@ int dialog_menu(const char *title, const char *prompt,
case 'x':
key = KEY_ESC;
break;
- case KEY_ESC:
- key = on_key_esc(menu);
- break;
- case KEY_RESIZE:
- on_key_resize();
- delwin(menu);
- delwin(dialog);
- goto do_resize;
}
}
delwin(menu);
--
2.16.4
Hi Dirk.
On Fri, Jun 08, 2018 at 08:46:05PM +0200, Dirk Gouders wrote:
> Hello,
>
> this version is a prototype of the idea, Sam suggested -- I hope I
> undestood it correctly.
>
> This is a remarkable change to mconf in it's behavior; mainly it now
> differentiates two focuses and operates differently, depending on the
> focus. When the focus is on the menu, mconf is in i-search mode, for
> a detailed description see 1/1.
>
> As stated earlier, this is just a prototype, documentation is missing
> and the code is not ready for a real commit.
When trying this out it started to work as envisioned.
But somehw I manage to get stuck in the menus and cannot navigate to
the buttons.
Consider to remove the old short-cut for the menus.
(Most/all the first_alph thingy in menubox.c)
That will avoid two confliting navigations principles and likely
make the code simpler to read.
I am not sure, but maybe you can get rid of all the hotkey
support in print_item with this change too.
Sam
On 06/08/2018 11:46 AM, Dirk Gouders wrote:
> This patch prototypes isearch functionality for mconf based on an idea of
> Sam Ravnborg:
>
> * mconf now distinguishes if the focus is on the menu items or the
> buttons below it.
>
> * At startup focus is on the menu items and alphanumeric
> characters or space entered are used to form a string that is
> searched for.
>
> * BACKSPACE is used to remove the last character of the search string.
>
> * ENTER can be used to enter a submenu.
>
> * Horizontal arrow keys put focus on the buttons with the known
> behavior.
>
> * The TAB key is now exclusively used to toggle the focus.
>
> * Vertical arrow keys work anywhere.
>
> * When the focus is on the buttons, all keys (e.g. hotkeys) work the
> same as before this change.
>
> * '\' can be used to search for other occurences of an already entered
> string.
>
> * To use y|n|m on an item, focus has to be on the buttons.
>
> An example to navigate into the USB support menu under Device Drivers:
>
> 1) de # Navigates to the item "Device Drivers"
> 2) ENTER # Enter the submenu
> 3) USB # Navigate to the item "USB support"
> 4) ENTER # Enter the submenu
Hi Dirk,
Much like Sam, I got locked in isearch mode. The TAB key would not switch
into the buttons focus. I had to kill it.
--
~Randy
Sam Ravnborg <[email protected]> writes:
> Hi Dirk.
>
> On Fri, Jun 08, 2018 at 08:46:05PM +0200, Dirk Gouders wrote:
>> Hello,
>>
>> this version is a prototype of the idea, Sam suggested -- I hope I
>> undestood it correctly.
>>
>> This is a remarkable change to mconf in it's behavior; mainly it now
>> differentiates two focuses and operates differently, depending on the
>> focus. When the focus is on the menu, mconf is in i-search mode, for
>> a detailed description see 1/1.
>>
>> As stated earlier, this is just a prototype, documentation is missing
>> and the code is not ready for a real commit.
>
> When trying this out it started to work as envisioned.
> But somehw I manage to get stuck in the menus and cannot navigate to
> the buttons.
>
> Consider to remove the old short-cut for the menus.
> (Most/all the first_alph thingy in menubox.c)
> That will avoid two confliting navigations principles and likely
> make the code simpler to read.
>
> I am not sure, but maybe you can get rid of all the hotkey
> support in print_item with this change too.
Hi Sam,
thank you (and also to Randy) for having a look at it.
Next, I will prepare a patch, more thorougly tested,
documentation included and with hotkey support removed.
You are right, trying to support two navigation principles makes
everything too complex and I was too eager to keep as much of today's
functionality as possible.
Dirk