diff options
| author | Michael Biebl <biebl@debian.org> | 2026-07-03 19:53:23 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2026-07-03 19:53:23 +0200 |
| commit | aa308069bebf2d5a3200728caa69a76137c03d8b (patch) | |
| tree | c0ba2281e801c4720a0d8a5e5ee943688234f088 /src/libnmt-newt | |
| parent | 0a4b2c29da4ccf259bbcea3298d15abebb94348f (diff) | |
| parent | 537bfce2bda471c92caabd388589230200891509 (diff) | |
Update upstream source from tag 'upstream/1.58_rc1'
Update to upstream version '1.58~rc1' with Debian dir 451489c9234e2b6b7c2f41ca6670287ea3ac3efd
Diffstat (limited to 'src/libnmt-newt')
| -rw-r--r-- | src/libnmt-newt/meson.build | 1 | ||||
| -rw-r--r-- | src/libnmt-newt/nmt-newt-form.c | 182 | ||||
| -rw-r--r-- | src/libnmt-newt/nmt-newt-form.h | 5 | ||||
| -rw-r--r-- | src/libnmt-newt/nmt-newt-textbox.c | 2 | ||||
| -rw-r--r-- | src/libnmt-newt/nmt-newt-utils.c | 39 | ||||
| -rw-r--r-- | src/libnmt-newt/nmt-newt-utils.h | 2 |
6 files changed, 206 insertions, 25 deletions
diff --git a/src/libnmt-newt/meson.build b/src/libnmt-newt/meson.build index 9a07fd58..e7a71e9c 100644 --- a/src/libnmt-newt/meson.build +++ b/src/libnmt-newt/meson.build @@ -27,6 +27,7 @@ libnmt_newt = static_library( dependencies: [ libnm_dep, newt_dep, + slang_dep, glib_dep, ], ) 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 diff --git a/src/libnmt-newt/nmt-newt-form.h b/src/libnmt-newt/nmt-newt-form.h index 6a2ff9dc..63b64be6 100644 --- a/src/libnmt-newt/nmt-newt-form.h +++ b/src/libnmt-newt/nmt-newt-form.h @@ -26,6 +26,7 @@ typedef struct { /* signals */ void (*quit)(NmtNewtForm *form); + gboolean (*hotkey)(NmtNewtForm *form, int key); /* methods */ void (*show)(NmtNewtForm *form); @@ -45,4 +46,8 @@ void nmt_newt_form_quit(NmtNewtForm *form); void nmt_newt_form_set_focus(NmtNewtForm *form, NmtNewtWidget *widget); +void nmt_newt_form_add_hotkey(NmtNewtForm *form, int key); + +void nmt_newt_form_set_stable_width(NmtNewtForm *form); + #endif /* NMT_NEWT_FORM_H */ diff --git a/src/libnmt-newt/nmt-newt-textbox.c b/src/libnmt-newt/nmt-newt-textbox.c index bf9b6a294..01e3b1c7 100644 --- a/src/libnmt-newt/nmt-newt-textbox.c +++ b/src/libnmt-newt/nmt-newt-textbox.c @@ -95,7 +95,7 @@ nmt_newt_textbox_set_text(NmtNewtTextbox *textbox, const char *text) priv->width = width; } g_free(lines); - priv->height = NM_MIN(i, 1); + priv->height = NM_MAX(i, 1); g_object_notify(G_OBJECT(textbox), "text"); nmt_newt_widget_needs_rebuild(NMT_NEWT_WIDGET(textbox)); diff --git a/src/libnmt-newt/nmt-newt-utils.c b/src/libnmt-newt/nmt-newt-utils.c index 36a47b2b..1864b4a4 100644 --- a/src/libnmt-newt/nmt-newt-utils.c +++ b/src/libnmt-newt/nmt-newt-utils.c @@ -17,6 +17,7 @@ #include <sys/wait.h> #include "libnm-glib-aux/nm-io-utils.h" +#include "libnm-glib-aux/nm-str-buf.h" static void nmt_newt_dialog_g_log_handler(const char *log_domain, @@ -387,6 +388,44 @@ nmt_newt_text_width(const char *str) } /** + * nmt_newt_text_truncate + * @str: a UTF-8 string + * @max_width: the maximum width in terminal columns + * + * Truncates @str to at most @max_width columns, appending an ellipsis if + * anything was dropped (the ellipsis is counted in @max_width). + * + * Returns: (transfer full): a newly-allocated truncated copy of @str. + */ +char * +nmt_newt_text_truncate(const char *str, int max_width) +{ + nm_auto_str_buf NMStrBuf buf = NM_STR_BUF_INIT(0, FALSE); + const char *p; + int width = 0; + + if (max_width <= 0) + return g_strdup(""); + if (nmt_newt_text_width(str) <= max_width) + return g_strdup(str); + + max_width--; /* reserve one column for the ellipsis */ + + for (p = str; *p; p = g_utf8_next_char(p)) { + gunichar ch = g_utf8_get_char(p); + int w = g_unichar_iszerowidth(ch) ? 0 : (g_unichar_iswide(ch) ? 2 : 1); + + if (width + w > max_width) + break; + width += w; + nm_str_buf_append_len(&buf, p, g_utf8_next_char(p) - p); + } + nm_str_buf_append(&buf, "\xe2\x80\xa6"); /* U+2026 HORIZONTAL ELLIPSIS */ + + return nm_str_buf_dup_str(&buf); +} + +/** * nmt_newt_edit_string: * @data: data to edit * diff --git a/src/libnmt-newt/nmt-newt-utils.h b/src/libnmt-newt/nmt-newt-utils.h index 04089f9f..32237ae2 100644 --- a/src/libnmt-newt/nmt-newt-utils.h +++ b/src/libnmt-newt/nmt-newt-utils.h @@ -23,6 +23,8 @@ char *nmt_newt_locale_from_utf8(const char *str_utf8); int nmt_newt_text_width(const char *str); +char *nmt_newt_text_truncate(const char *str, int max_width); + void nmt_newt_message_dialog(const char *message, ...) _nm_printf(1, 2); int nmt_newt_choice_dialog(const char *button1, const char *button2, const char *message, ...) _nm_printf(3, 4); |