Reimplement widget focus/selection.

Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
Andrew Borodin 2016-07-16 21:00:28 +03:00
parent 599361b3fc
commit 36cc88b5f2
5 changed files with 162 additions and 160 deletions

View File

@ -73,14 +73,6 @@ const global_keymap_t *dialog_map = NULL;
/*** file scope type declarations ****************************************************************/
/** What to do if the requested widget doesn't take focus */
typedef enum
{
SELECT_NEXT, /* go the the next widget */
SELECT_PREV, /* go the the previous widget */
SELECT_EXACT /* use current widget */
} select_dir_t;
/* Control widget positions in dialog */
typedef struct
{
@ -125,6 +117,28 @@ dlg_get_next_or_prev_of (const GList * list, gboolean next)
return l;
}
/* --------------------------------------------------------------------------------------------- */
static void
dlg_select_next_or_prev (WDialog * h, gboolean next)
{
if (h->widgets != NULL && h->current != NULL)
{
GList *l = h->current;
Widget *w;
do
{
l = dlg_get_next_or_prev_of (l, next);
w = WIDGET (l->data);
}
while ((widget_get_state (w, WST_DISABLED) || !widget_get_options (w, WOP_SELECTABLE))
&& l != h->current);
widget_select (l->data);
}
}
/* --------------------------------------------------------------------------------------------- */
/**
* broadcast a message to all the widgets in a dialog that have
@ -185,24 +199,6 @@ dlg_read_history (WDialog * h)
/* --------------------------------------------------------------------------------------------- */
static gboolean
dlg_unfocus (WDialog * h)
{
/* we can unfocus disabled widget */
if (h->current != NULL)
{
Widget *wh = WIDGET (h);
if ((widget_get_state (wh, WST_CONSTRUCT) || widget_get_state (wh, WST_ACTIVE))
&& widget_set_state (WIDGET (h->current->data), WST_FOCUSED, FALSE) == MSG_HANDLED)
return TRUE;
}
return FALSE;
}
/* --------------------------------------------------------------------------------------------- */
static int
dlg_find_widget_callback (const void *a, const void *b)
{
@ -214,72 +210,6 @@ dlg_find_widget_callback (const void *a, const void *b)
/* --------------------------------------------------------------------------------------------- */
/**
* Put widget on top or bottom of Z-order.
*/
static void
dlg_reorder_widgets (GList * l, gboolean set_top)
{
WDialog *h = WIDGET (l->data)->owner;
h->widgets = g_list_remove_link (h->widgets, l);
if (set_top)
h->widgets = g_list_concat (h->widgets, l);
else
h->widgets = g_list_concat (l, h->widgets);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Try to select another widget. If forward is set, follow tab order.
* Otherwise go to the previous widget.
*/
static void
do_select_widget (WDialog * h, GList * w, select_dir_t dir)
{
Widget *w0 = WIDGET (h->current->data);
if (!dlg_unfocus (h))
return;
h->current = w;
do
{
if (dlg_focus (h))
break;
switch (dir)
{
case SELECT_EXACT:
h->current = g_list_find (h->widgets, w0);
if (dlg_focus (h))
return;
/* try find another widget that can take focus */
dir = SELECT_NEXT;
/* fallthrough */
case SELECT_NEXT:
dlg_set_current_widget_next (h);
break;
case SELECT_PREV:
dlg_set_current_widget_prev (h);
break;
default:
break;
}
}
while (h->current != w);
if (widget_get_options (WIDGET (h->current->data), WOP_TOP_SELECT))
dlg_reorder_widgets (h->current, TRUE);
if (widget_overlapped (w0, WIDGET (h->current->data)))
widget_set_state (WIDGET (h->current->data), WST_FOCUSED, TRUE);
}
/* --------------------------------------------------------------------------------------------- */
static void
refresh_cmd (void)
{
@ -508,7 +438,7 @@ dlg_try_hotkey (WDialog * h, int d_key)
}
if (handled == MSG_HANDLED)
do_select_widget (h, hot_cur, SELECT_EXACT);
widget_select (WIDGET (hot_cur->data));
return handled;
}
@ -1005,6 +935,8 @@ del_widget (void *w)
dlg_set_current_widget_next (h);
h->widgets = g_list_remove_link (h->widgets, d);
if (h->widgets == NULL)
h->current = NULL;
send_message (d->data, NULL, MSG_DESTROY, 0, NULL);
g_free (d->data);
g_list_free_1 (d);
@ -1013,7 +945,7 @@ del_widget (void *w)
if (widget_get_state (WIDGET (h), WST_ACTIVE))
{
dlg_redraw (h);
dlg_focus (h);
dlg_select_current_widget (h);
}
}
@ -1051,30 +983,6 @@ dlg_broadcast_msg (WDialog * h, widget_msg_t msg)
dlg_broadcast_msg_to (h, msg, FALSE, 0);
}
/* --------------------------------------------------------------------------------------------- */
gboolean
dlg_focus (WDialog * h)
{
/* cannot focus disabled widget */
if (h->current != NULL)
{
Widget *wh = WIDGET (h);
if (widget_get_state (wh, WST_CONSTRUCT) || widget_get_state (wh, WST_ACTIVE))
{
Widget *current = WIDGET (h->current->data);
if (widget_get_options (current, WOP_SELECTABLE)
&& !widget_get_state (current, WST_DISABLED)
&& widget_set_state (current, WST_FOCUSED, TRUE) == MSG_HANDLED)
return TRUE;
}
}
return FALSE;
}
/* --------------------------------------------------------------------------------------------- */
/** Find the widget with the given callback in the dialog h */
@ -1088,6 +996,14 @@ find_widget_type (const WDialog * h, widget_cb_fn callback)
return (w == NULL) ? NULL : WIDGET (w->data);
}
/* --------------------------------------------------------------------------------------------- */
GList *
dlg_find (const WDialog * h, const Widget * w)
{
return (w->owner == NULL || w->owner != h) ? NULL : g_list_find (h->widgets, w);
}
/* --------------------------------------------------------------------------------------------- */
/** Find the widget with the given id */
@ -1113,40 +1029,13 @@ dlg_select_by_id (const WDialog * h, unsigned long id)
widget_select (w);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Try to select widget in the dialog.
*/
void
widget_select (Widget * w)
{
WDialog *h = w->owner;
do_select_widget (h, g_list_find (h->widgets, w), SELECT_EXACT);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Set widget at bottom of widget list.
*/
void
widget_set_bottom (Widget * w)
{
WDialog *h = w->owner;
dlg_reorder_widgets (g_list_find (h->widgets, w), FALSE);
}
/* --------------------------------------------------------------------------------------------- */
/** Try to select previous widget in the tab order */
void
dlg_select_prev_widget (WDialog * h)
{
if (h->widgets != NULL)
do_select_widget (h, dlg_get_widget_prev_of (h->current), SELECT_PREV);
dlg_select_next_or_prev (h, FALSE);
}
/* --------------------------------------------------------------------------------------------- */
@ -1155,8 +1044,7 @@ dlg_select_prev_widget (WDialog * h)
void
dlg_select_next_widget (WDialog * h)
{
if (h->widgets != NULL)
do_select_widget (h, dlg_get_widget_next_of (h->current), SELECT_NEXT);
dlg_select_next_or_prev (h, TRUE);
}
/* --------------------------------------------------------------------------------------------- */
@ -1247,15 +1135,15 @@ dlg_init (WDialog * h)
dlg_read_history (h);
}
/* Select the first widget that takes focus */
while (h->current != NULL && !widget_get_options (WIDGET (h->current->data), WOP_SELECTABLE)
&& !widget_get_state (WIDGET (h->current->data), WST_DISABLED))
dlg_set_current_widget_next (h);
widget_set_state (wh, WST_ACTIVE, TRUE);
/* first send MSG_DRAW to dialog itself and all widgets... */
dlg_redraw (h);
/* ...then send MSG_FOCUS to select the first widget that can take focus */
while (h->current != NULL && !dlg_focus (h))
h->current = dlg_get_widget_next_of (h->current);
/* focus found widget */
widget_set_state (WIDGET (h->current->data), WST_FOCUSED, TRUE);
h->ret_value = 0;
}

View File

@ -147,12 +147,10 @@ void dlg_erase (WDialog * h);
void dlg_stop (WDialog * h);
/* Widget selection */
void widget_select (Widget * w);
void widget_set_bottom (void *w);
void dlg_select_prev_widget (WDialog * h);
void dlg_select_next_widget (WDialog * h);
gboolean dlg_focus (WDialog * h);
Widget *find_widget_type (const WDialog * h, widget_cb_fn callback);
GList *dlg_find (const WDialog * h, const Widget * w);
Widget *dlg_find_by_id (const WDialog * h, unsigned long id);
void dlg_select_by_id (const WDialog * h, unsigned long id);
@ -180,4 +178,19 @@ dlg_get_current_widget_id (const WDialog * h)
/* --------------------------------------------------------------------------------------------- */
/**
* Select current widget in the Dialog.
*
* @param h WDialog object
*/
static inline void
dlg_select_current_widget (WDialog * h)
{
if (h->current != NULL)
widget_select (WIDGET (h->current->data));
}
/* --------------------------------------------------------------------------------------------- */
#endif /* MC__DIALOG_H */

View File

@ -53,7 +53,63 @@
/*** file scope variables ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
static void
widget_do_focus (Widget * w, gboolean enable)
{
if (w != NULL && widget_get_state (WIDGET (w->owner), WST_FOCUSED))
widget_set_state (w, WST_FOCUSED, enable);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Focus specified widget in it's owner.
*
* @param w widget to be focused.
*/
static void
widget_focus (Widget * w)
{
WDialog *h = DIALOG (w->owner);
if (h == NULL)
return;
if (WIDGET (h->current->data) != w)
{
widget_do_focus (WIDGET (h->current->data), FALSE);
/* Test if focus lost was allowed and focus has really been loose */
if (h->current == NULL || !widget_get_state (WIDGET (h->current->data), WST_FOCUSED))
{
widget_do_focus (w, TRUE);
h->current = dlg_find (h, w);
}
}
else if (!widget_get_state (w, WST_FOCUSED))
widget_do_focus (w, TRUE);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Put widget on top or bottom of Z-order.
*/
static void
widget_reorder (GList * l, gboolean set_top)
{
WDialog *h = WIDGET (l->data)->owner;
h->widgets = g_list_remove_link (h->widgets, l);
if (set_top)
h->widgets = g_list_concat (h->widgets, l);
else
h->widgets = g_list_concat (l, h->widgets);
}
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
@ -387,9 +443,52 @@ widget_replace (Widget * old_w, Widget * new_w)
if (should_focus)
widget_select (new_w);
/* draw inactive widget */
if (!widget_get_state (new_w, WST_FOCUSED))
widget_redraw (new_w);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Select specified widget in it's owner.
*
* @param w widget to be selected
*/
void
widget_select (Widget * w)
{
WDialog *h;
if (!widget_get_options (w, WOP_SELECTABLE))
return;
h = w->owner;
if (h != NULL)
{
if (widget_get_options (w, WOP_TOP_SELECT))
{
GList *l;
l = dlg_find (h, w);
widget_reorder (l, TRUE);
}
widget_focus (w);
}
}
/* --------------------------------------------------------------------------------------------- */
/**
* Set widget at bottom of widget list.
*/
void
widget_set_bottom (Widget * w)
{
widget_reorder (dlg_find (w->owner, w), FALSE);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Check whether two widgets are overlapped or not.

View File

@ -187,6 +187,8 @@ void widget_erase (Widget * w);
gboolean widget_is_active (const void *w);
gboolean widget_overlapped (const Widget * a, const Widget * b);
void widget_replace (Widget * old, Widget * new);
void widget_select (Widget * w);
void widget_set_bottom (Widget * w);
/* get mouse pointer location within widget */
Gpm_Event mouse_get_local (const Gpm_Event * global, const Widget * w);

View File

@ -400,7 +400,7 @@ do_enter_key (WDialog * h, int f_pos)
ch_flags[f_pos + 6] = '+';
update_ownership ();
}
dlg_focus (h);
dlg_select_current_widget (h);
if (ok)
print_flags ();
}