diff options
Diffstat (limited to 'src/libnmt-newt/nmt-newt-form.c')
| -rw-r--r-- | src/libnmt-newt/nmt-newt-form.c | 182 |
1 files changed, 158 insertions, 24 deletions
diff --git a/src/libnmt-newt/nmt-newt-form.c b/src/libnmt-newt/nmt-newt-form.c index 3565799c..3f87d0cf 100644 --- a/src/libnmt-newt/nmt-newt-form.c +++ b/src/libnmt-newt/nmt-newt-form.c @@ -14,6 +14,8 @@ #include "libnm-client-aux-extern/nm-default-client.h" #include <fcntl.h> +#include <signal.h> +#include <slang.h> #include <unistd.h> #include "nmt-newt-form.h" @@ -31,13 +33,17 @@ typedef struct { NmtNewtWidget *content; guint x, y, width, height; + guint form_width_max; guint padding; gboolean fixed_x, fixed_y; gboolean fixed_width, fixed_height; + gboolean stable_width; + gboolean fullscreen_vertical, fullscreen_horizontal; char *title_lc; gboolean dirty; NmtNewtWidget *focus; + GArray *hotkeys; #ifdef HAVE_NEWTFORMGETSCROLLPOSITION int scroll_position = 0; #endif @@ -60,6 +66,7 @@ enum { enum { QUIT, + HOTKEY, LAST_SIGNAL }; @@ -118,6 +125,7 @@ nmt_newt_form_finalize(GObject *object) g_free(priv->title_lc); g_clear_object(&priv->focus); + nm_clear_pointer(&priv->hotkeys, g_array_unref); G_OBJECT_CLASS(nmt_newt_form_parent_class)->finalize(object); } @@ -181,6 +189,15 @@ nmt_newt_form_build(NmtNewtForm *form) nmt_newt_widget_size_request(priv->content, &form_width, &form_height); newtGetScreenSize(&screen_width, &screen_height); + if (priv->stable_width) { + /* Never let the form get narrower across rebuilds. Otherwise content + * that shrinks (eg, a filtered list) makes the window recenter and + * "jump". Only forms that opt in get this; see + * nmt_newt_form_set_stable_width(). */ + priv->form_width_max = NM_MAX(priv->form_width_max, (guint) NM_MAX(form_width, 0)); + form_width = priv->form_width_max; + } + if (!priv->fixed_width) priv->width = NM_MIN(form_width + 2 * ((gint64) priv->padding), screen_width - 2); if (!priv->fixed_height) @@ -191,6 +208,20 @@ nmt_newt_form_build(NmtNewtForm *form) if (!priv->fixed_y) priv->y = (screen_height - form_height) / 2; + if (priv->fullscreen_horizontal) { + priv->x = 2; + priv->width = screen_width - 4; + } + if (priv->fullscreen_vertical) { + priv->y = 2; + priv->height = screen_height - 4; + } + + if ((int) priv->x < 0) + priv->x = 0; + if ((int) priv->y < 0) + priv->y = 0; + nmt_newt_widget_size_allocate(priv->content, priv->padding, priv->padding, @@ -211,6 +242,12 @@ nmt_newt_form_build(NmtNewtForm *form) priv->form = newtForm(NULL, NULL, NEWT_FLAG_NOF12); newtFormAddHotKey(priv->form, NEWT_KEY_ESCAPE); + if (priv->hotkeys) { + guint h; + + for (h = 0; h < priv->hotkeys->len; h++) + newtFormAddHotKey(priv->form, nm_g_array_index(priv->hotkeys, int, h)); + } cos = nmt_newt_widget_get_components(priv->content); for (i = 0; cos[i]; i++) @@ -270,6 +307,17 @@ nmt_newt_form_iterate(NmtNewtForm *form) newtFormSetTimer(priv->form, 1); newtFormRun(priv->form, &es); + if (es.reason == NEWT_EXIT_HOTKEY) { + gboolean handled = FALSE; + + /* Give listeners (eg, the connection-list search) a chance to consume the + * key. Esc is included, so a listener can make it cancel a transient mode + * instead of closing the form. */ + g_signal_emit(form, signals[HOTKEY], 0, (int) es.u.key, &handled); + if (handled) + return; + } + if (es.reason == NEWT_EXIT_HOTKEY || es.reason == NEWT_EXIT_ERROR) { /* The user hit Esc or there was an error. */ g_clear_object(&priv->focus); @@ -301,6 +349,7 @@ nmt_newt_form_iterate(NmtNewtForm *form) */ static GSList *form_stack; static GSource *keypress_source; +static GSource *winch_source; static gboolean nmt_newt_form_keypress_callback(int fd, GIOCondition condition, gpointer user_data) @@ -311,6 +360,48 @@ nmt_newt_form_keypress_callback(int fd, GIOCondition condition, gpointer user_da return TRUE; } +/* On SIGWINCH, refit every form in the stack to the new terminal size. The + * windows must be popped top-to-bottom (newt's window stack is LIFO) and rebuilt + * bottom-to-top to restore the original z-order. + */ +static void +nmt_newt_form_resize(void) +{ + GSList *bottom_up; + GSList *iter; + + for (iter = form_stack; iter; iter = iter->next) { + NmtNewtForm *form = iter->data; + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(form); + + if (priv->form) + nmt_newt_form_destroy(form); + } + + /* newtResizeScreen() updates SLtt's screen size but skips the + * SLsmg_reinit_smg() that resizes the screen buffer (commented out + * upstream), so rows newly exposed by a taller terminal go unpainted. Do + * it ourselves, then repaint the root. */ + SLtt_get_screen_size(); + SLsmg_reinit_smg(); + newtCls(); + + bottom_up = g_slist_reverse(g_slist_copy(form_stack)); + for (iter = bottom_up; iter; iter = iter->next) + nmt_newt_form_build(iter->data); + g_slist_free(bottom_up); + + nmt_newt_form_redraw(form_stack->data); +} + +static gboolean +nmt_newt_form_winch_callback(gpointer user_data) +{ + if (form_stack) + nmt_newt_form_resize(); + return G_SOURCE_CONTINUE; +} + static gboolean nmt_newt_form_timeout_callback(gpointer user_data) { @@ -338,6 +429,8 @@ nmt_newt_form_real_show(NmtNewtForm *form) g_source_set_can_recurse(keypress_source, TRUE); g_source_attach(keypress_source, NULL); } + if (!winch_source) + winch_source = nm_g_unix_signal_add_source(SIGWINCH, nmt_newt_form_winch_callback, NULL); nmt_newt_form_build(form); form_stack = g_slist_prepend(form_stack, g_object_ref(form)); @@ -403,8 +496,10 @@ nmt_newt_form_quit(NmtNewtForm *form) if (form_stack) nmt_newt_form_iterate(form_stack->data); - else + else { nm_clear_g_source_inst(&keypress_source); + nm_clear_g_source_inst(&winch_source); + } g_signal_emit(form, signals[QUIT], 0); g_object_unref(form); @@ -434,11 +529,47 @@ nmt_newt_form_set_focus(NmtNewtForm *form, NmtNewtWidget *widget) g_object_ref(priv->focus); } +/** + * nmt_newt_form_add_hotkey: + * @form: an #NmtNewtForm + * @key: a key code (eg, a character, or an %NEWT_KEY_ value) + * + * Registers @key as a hotkey on @form. When the user presses it, the + * #NmtNewtForm::hotkey signal is emitted; a handler returning %TRUE consumes + * the key, otherwise the form's default handling applies. + */ +void +nmt_newt_form_add_hotkey(NmtNewtForm *form, int key) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(form); + + if (!priv->hotkeys) + priv->hotkeys = g_array_new(FALSE, FALSE, sizeof(int)); + g_array_append_val(priv->hotkeys, key); + + if (priv->form) + newtFormAddHotKey(priv->form, key); +} + +/** + * nmt_newt_form_set_stable_width: + * @form: an #NmtNewtForm + * + * Stops @form from getting narrower across rebuilds: its width grows to fit + * content but never shrinks. Use for forms whose content shrinks in place (eg, + * a list filtered by a search box) where recentering would make the window + * "jump". + */ +void +nmt_newt_form_set_stable_width(NmtNewtForm *form) +{ + NMT_NEWT_FORM_GET_PRIVATE(form)->stable_width = TRUE; +} + static void nmt_newt_form_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(object); - int screen_width, screen_height; switch (prop_id) { case PROP_TITLE: @@ -448,32 +579,13 @@ nmt_newt_form_set_property(GObject *object, guint prop_id, const GValue *value, priv->title_lc = NULL; break; case PROP_FULLSCREEN: - if (g_value_get_boolean(value)) { - newtGetScreenSize(&screen_width, &screen_height); - priv->x = priv->y = 2; - priv->fixed_x = priv->fixed_y = TRUE; - priv->width = screen_width - 4; - priv->height = screen_height - 4; - priv->fixed_width = priv->fixed_height = TRUE; - } + priv->fullscreen_horizontal = priv->fullscreen_vertical = g_value_get_boolean(value); break; case PROP_FULLSCREEN_VERTICAL: - if (g_value_get_boolean(value)) { - newtGetScreenSize(&screen_width, &screen_height); - priv->y = 2; - priv->fixed_y = TRUE; - priv->height = screen_height - 4; - priv->fixed_height = TRUE; - } + priv->fullscreen_vertical = g_value_get_boolean(value); break; case PROP_FULLSCREEN_HORIZONTAL: - if (g_value_get_boolean(value)) { - newtGetScreenSize(&screen_width, &screen_height); - priv->x = 2; - priv->fixed_x = TRUE; - priv->width = screen_width - 4; - priv->fixed_width = TRUE; - } + priv->fullscreen_horizontal = g_value_get_boolean(value); break; case PROP_X: if (g_value_get_uint(value)) { @@ -580,6 +692,28 @@ nmt_newt_form_class_init(NmtNewtFormClass *form_class) 0); /** + * NmtNewtForm::hotkey: + * @form: the #NmtNewtForm + * @key: the key that was pressed + * + * Emitted when a key registered via nmt_newt_form_add_hotkey() (or Esc) is + * pressed. A handler returning %TRUE consumes the key; otherwise the form's + * default handling applies (Esc closes the form). + * + * Returns: %TRUE if the key was handled. + */ + signals[HOTKEY] = g_signal_new("hotkey", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(NmtNewtFormClass, hotkey), + g_signal_accumulator_true_handled, + NULL, + NULL, + G_TYPE_BOOLEAN, + 1, + G_TYPE_INT); + + /** * NmtNewtForm:title: * * The form's title. If non-%NULL, this will be displayed above |