From 493504f12f79990dae7791efa27366b560151f2c Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Sun, 18 Jun 2023 18:50:39 +0300 Subject: fixed caret scrolling on startup * Since Scintilla no longer automatically scrolls the caret (see 941f48da6dde691a7800290cc729aaaacd051392), the caret wouldn't always end up in the view on startup. * Added teco_interface_refresh() which includes SCI_SCROLLCARET and is invoked on startup. This helps with the Curses backend. It also reduces code redundancies. * On Gtk, the caret cannot be easily scrolled on startup as long as no size is allocated to the window, so we also added a size-allocate callback to the window's event box. Sizes are less often allocated to the event box than to the window itself for some strange reason. --- src/interface-curses/interface.c | 59 +++++++++--------- src/interface-gtk/interface.c | 125 ++++++++++++++++++++------------------- 2 files changed, 93 insertions(+), 91 deletions(-) diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c index e056333..ef3f0c7 100644 --- a/src/interface-curses/interface.c +++ b/src/interface-curses/interface.c @@ -1507,6 +1507,31 @@ teco_interface_is_interrupted(void) #endif +static void +teco_interface_refresh(void) +{ + /* + * Scintilla has been patched to avoid any automatic scrolling since that + * has been benchmarked to be a very costly operation. + * Instead we do it only once after every keypress. + */ + teco_interface_ssm(SCI_SCROLLCARET, 0, 0); + + /* + * Info window is updated very often which is very + * costly, especially when using PDC_set_title(), + * so we redraw it here, where the overhead does + * not matter much. + */ + teco_interface_draw_info(); + wnoutrefresh(teco_interface.info_window); + teco_view_noutrefresh(teco_interface_current_view); + wnoutrefresh(teco_interface.msg_window); + wnoutrefresh(teco_interface.cmdline_window); + teco_curses_info_popup_noutrefresh(&teco_interface.popup); + doupdate(); +} + static gint teco_interface_blocking_getch(void) { @@ -1640,48 +1665,22 @@ teco_interface_event_loop_iter(void) return; } - /* - * Scintilla has been patched to avoid any automatic scrolling since that - * has been benchmarked to be a very costly operation. - * Instead we do it only once after every keypress. - */ - teco_interface_ssm(SCI_SCROLLCARET, 0, 0); - - /* - * Info window is updated very often which is very - * costly, especially when using PDC_set_title(), - * so we redraw it here, where the overhead does - * not matter much. - */ - teco_interface_draw_info(); - wnoutrefresh(teco_interface.info_window); - teco_view_noutrefresh(teco_interface_current_view); - wnoutrefresh(teco_interface.msg_window); - wnoutrefresh(teco_interface.cmdline_window); - teco_curses_info_popup_noutrefresh(&teco_interface.popup); - doupdate(); + teco_interface_refresh(); } gboolean teco_interface_event_loop(GError **error) { - static const teco_cmdline_t empty_cmdline; // FIXME - /* * Initialize Curses for interactive mode */ if (!teco_interface_init_interactive(error)) return FALSE; - /* initial refresh */ - teco_interface_draw_info(); - wnoutrefresh(teco_interface.info_window); - teco_view_noutrefresh(teco_interface_current_view); - teco_interface_msg_clear(); - wnoutrefresh(teco_interface.msg_window); + static const teco_cmdline_t empty_cmdline; // FIXME teco_interface_cmdline_update(&empty_cmdline); - wnoutrefresh(teco_interface.cmdline_window); - doupdate(); + teco_interface_msg_clear(); + teco_interface_refresh(); #ifdef EMCURSES PDC_emscripten_set_handler(teco_interface_event_loop_iter, TRUE); diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index 6301a71..253600a 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -64,6 +64,9 @@ static void teco_interface_cmdline_size_allocate_cb(GtkWidget *widget, gpointer user_data); static void teco_interface_cmdline_commit_cb(GtkIMContext *context, gchar *str, gpointer user_data); +static void teco_interface_size_allocate_cb(GtkWidget *widget, + GdkRectangle *allocation, + gpointer user_data); static gboolean teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data); static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, @@ -308,6 +311,9 @@ teco_interface_init(void) gtk_box_pack_start(GTK_BOX(overlay_vbox), teco_interface.event_box_widget, TRUE, TRUE, 0); + g_signal_connect(teco_interface.event_box_widget, "size-allocate", + G_CALLBACK(teco_interface_size_allocate_cb), NULL); + teco_interface.message_bar_widget = gtk_info_bar_new(); gtk_widget_set_name(teco_interface.message_bar_widget, "sciteco-message-bar"); GtkWidget *message_bar_content = @@ -824,6 +830,52 @@ teco_interface_set_css_variables(teco_view_t *view) gtk_widget_set_size_request(teco_view_get_widget(teco_interface.cmdline_view), -1, text_height); } +static void +teco_interface_refresh(gboolean current_view_changed) +{ + /* + * The styles configured via Scintilla might change + * with every keypress. + */ + if (teco_interface_current_view) + teco_interface_set_css_variables(teco_interface_current_view); + + /* + * The info area is updated very often and setting the + * window title each time it is updated is VERY costly. + * So we set it here once after every keypress even if the + * info line did not change. + * View changes are also only applied here to the GTK + * window even though GDK updates have been frozen since + * the size reallocations are very costly. + */ + teco_interface_refresh_info(); + + if (current_view_changed) { + /* + * The last view's object is not guaranteed to + * still exist. + * However its widget is, due to reference counting. + */ + if (teco_interface.current_view_widget) + gtk_container_remove(GTK_CONTAINER(teco_interface.event_box_widget), + teco_interface.current_view_widget); + + teco_interface.current_view_widget = teco_view_get_widget(teco_interface_current_view); + + gtk_container_add(GTK_CONTAINER(teco_interface.event_box_widget), + teco_interface.current_view_widget); + gtk_widget_show(teco_interface.current_view_widget); + } + + /* + * Scintilla has been patched to avoid any automatic scrolling since that + * has been benchmarked to be a very costly operation. + * Instead we do it only once after every keypress. + */ + teco_interface_ssm(SCI_SCROLLCARET, 0, 0); +} + static void teco_interface_cmdline_commit_cb(GtkIMContext *context, gchar *str, gpointer user_data) { @@ -933,46 +985,7 @@ teco_interface_handle_key_press(GdkEventKey *event, GError **error) } } - /* - * Scintilla has been patched to avoid any automatic scrolling since that - * has been benchmarked to be a very costly operation. - * Instead we do it only once after every keypress. - */ - teco_interface_ssm(SCI_SCROLLCARET, 0, 0); - - /* - * The styles configured via Scintilla might change - * with every keypress. - */ - teco_interface_set_css_variables(teco_interface_current_view); - - /* - * The info area is updated very often and setting the - * window title each time it is updated is VERY costly. - * So we set it here once after every keypress even if the - * info line did not change. - * View changes are also only applied here to the GTK - * window even though GDK updates have been frozen since - * the size reallocations are very costly. - */ - teco_interface_refresh_info(); - - if (teco_interface_current_view != last_view) { - /* - * The last view's object is not guaranteed to - * still exist. - * However its widget is, due to reference counting. - */ - if (teco_interface.current_view_widget) - gtk_container_remove(GTK_CONTAINER(teco_interface.event_box_widget), - teco_interface.current_view_widget); - - teco_interface.current_view_widget = teco_view_get_widget(teco_interface_current_view); - - gtk_container_add(GTK_CONTAINER(teco_interface.event_box_widget), - teco_interface.current_view_widget); - gtk_widget_show(teco_interface.current_view_widget); - } + teco_interface_refresh(teco_interface_current_view != last_view); return TRUE; } @@ -1032,16 +1045,11 @@ teco_interface_event_loop(GError **error) g_list_free_full(icon_list, g_object_unref); #endif - teco_interface_refresh_info(); - /* * Initialize the CSS variable provider and the CSS provider * for the included fallback.css. */ teco_interface.css_var_provider = gtk_css_provider_new(); - if (teco_interface_current_view) - /* set CSS variables initially */ - teco_interface_set_css_variables(teco_interface_current_view); GdkScreen *default_screen = gdk_screen_get_default(); gtk_style_context_add_provider_for_screen(default_screen, GTK_STYLE_PROVIDER(teco_interface.css_var_provider), @@ -1073,22 +1081,7 @@ teco_interface_event_loop(GError **error) GTK_STYLE_PROVIDER(user_css_provider), GTK_STYLE_PROVIDER_PRIORITY_USER); - /* - * When changing views, the new widget is not - * added immediately to avoid flickering in the GUI. - * It is only updated once per key press and only - * if it really changed. - * Therefore we must add the current view to the - * window initially. - * For the same reason, window title updates are - * deferred to once after every key press, so we must - * set the window title initially. - */ - if (teco_interface_current_view) { - teco_interface.current_view_widget = teco_view_get_widget(teco_interface_current_view); - gtk_container_add(GTK_CONTAINER(teco_interface.event_box_widget), - teco_interface.current_view_widget); - } + teco_interface_refresh(TRUE); gtk_widget_show_all(teco_interface.window); /* don't show popup by default */ @@ -1157,6 +1150,16 @@ teco_interface_cmdline_size_allocate_cb(GtkWidget *widget, CARET_SLOP | CARET_EVEN, allocation->width/2); } +static void +teco_interface_size_allocate_cb(GtkWidget *widget, + GdkRectangle *allocation, gpointer user_data) +{ + /* + * This especially ensures that the caret is visible after startup. + */ + teco_interface_ssm(SCI_SCROLLCARET, 0, 0); +} + static gboolean teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { -- cgit v1.2.3