From ce59112fd13b8c7fd4625043dc9a776321f43602 Mon Sep 17 00:00:00 2001 From: JosephMcc Date: Mon, 25 Sep 2017 07:01:27 -0700 Subject: [PATCH] Minimap (#175) * Add a minimap Add support for GtkSourceViews SourceMap object. Add both a default setting for new files to the preferences dialog and a menu item to allow temporarily disabling it in any view. * Require gtk and gtksourceview 3.18 * load custom css We can use this to make small tweaks to the interface so every theme doesn't need to add specific support. Make use of this new css to clean up the styling of the new overview map frame a bit. --- configure.ac | 4 +- data/org.x.editor.gschema.xml.in | 6 ++ xed/resources/css/xed-style.css | 7 +++ xed/resources/ui/xed-preferences-dialog.ui | 45 +++++++++++++++ xed/resources/ui/xed-ui.xml | 1 + xed/resources/ui/xed-view-frame.ui | 33 +++++++++-- xed/resources/xed.gresource.xml | 1 + xed/xed-app.c | 66 ++++++++++++++-------- xed/xed-commands-view.c | 21 ++++++- xed/xed-commands.h | 1 + xed/xed-preferences-dialog.c | 9 +++ xed/xed-settings.h | 1 + xed/xed-ui.h | 5 +- xed/xed-view-frame.c | 29 ++++++++++ xed/xed-view-frame.h | 2 + xed/xed-window-private.h | 1 + xed/xed-window.c | 44 ++++++++++++++- 17 files changed, 241 insertions(+), 35 deletions(-) create mode 100644 xed/resources/css/xed-style.css diff --git a/configure.ac b/configure.ac index 4a86719..2ca807b 100644 --- a/configure.ac +++ b/configure.ac @@ -149,8 +149,8 @@ PKG_CHECK_MODULES(XED, [ $GMODULE_ADD gthread-2.0 >= 2.13.0 gio-2.0 >= 2.40.0 - gtk+-3.0 >= 3.14.0 - gtksourceview-3.0 >= 3.14.0 + gtk+-3.0 >= 3.18.0 + gtksourceview-3.0 >= 3.18.0 libpeas-1.0 >= 1.12.0 libpeas-gtk-1.0 >= 1.12.0 ]) diff --git a/data/org.x.editor.gschema.xml.in b/data/org.x.editor.gschema.xml.in index 84e5d2a..ebaf070 100644 --- a/data/org.x.editor.gschema.xml.in +++ b/data/org.x.editor.gschema.xml.in @@ -201,6 +201,12 @@ Whether you can change active tabs by scrolling. + + false + Minimap is visible + Whether the minimap for the document should be visible. + + 5 Maximum Recent Files diff --git a/xed/resources/css/xed-style.css b/xed/resources/css/xed-style.css new file mode 100644 index 0000000..984ca8a --- /dev/null +++ b/xed/resources/css/xed-style.css @@ -0,0 +1,7 @@ +.xed-map-frame:dir(ltr) { + border-width: 0 0 0 1px; +} + +.xed-map-frame:dir(rtl) { + border-width: 0 1px 0 0; +} \ No newline at end of file diff --git a/xed/resources/ui/xed-preferences-dialog.ui b/xed/resources/ui/xed-preferences-dialog.ui index 7194dd4..5975d36 100755 --- a/xed/resources/ui/xed-preferences-dialog.ui +++ b/xed/resources/ui/xed-preferences-dialog.ui @@ -545,6 +545,51 @@ 4 + + + True + False + vertical + 6 + + + True + False + Overview Map + 0 + + + + + + False + True + 0 + + + + + Display overview map + True + True + False + 12 + 0 + True + + + False + True + 1 + + + + + True + True + 5 + + diff --git a/xed/resources/ui/xed-ui.xml b/xed/resources/ui/xed-ui.xml index 4da5c7a..10d48bf 100644 --- a/xed/resources/ui/xed-ui.xml +++ b/xed/resources/ui/xed-ui.xml @@ -52,6 +52,7 @@ + diff --git a/xed/resources/ui/xed-view-frame.ui b/xed/resources/ui/xed-view-frame.ui index 81fe13e..af7efa0 100644 --- a/xed/resources/ui/xed-view-frame.ui +++ b/xed/resources/ui/xed-view-frame.ui @@ -7,16 +7,37 @@ False False - + True - True - False - False - none - + True True + False + False + none + True + True + + + True + True + + + + + + + True + + + + True + view + + diff --git a/xed/resources/xed.gresource.xml b/xed/resources/xed.gresource.xml index f960a0d..b02bde6 100644 --- a/xed/resources/xed.gresource.xml +++ b/xed/resources/xed.gresource.xml @@ -7,5 +7,6 @@ ui/xed-print-preferences.ui ui/xed-searchbar.ui ui/xed-view-frame.ui + css/xed-style.css \ No newline at end of file diff --git a/xed/xed-app.c b/xed/xed-app.c index 1ecc587..785e162 100644 --- a/xed/xed-app.c +++ b/xed/xed-app.c @@ -234,6 +234,9 @@ xed_app_startup (GApplication *application) const gchar *cache_dir; gchar *metadata_filename; #endif + GError *error = NULL; + GFile *css_file; + GtkCssProvider *provider; G_APPLICATION_CLASS (xed_app_parent_class)->startup (application); @@ -259,36 +262,51 @@ xed_app_startup (GApplication *application) g_free (metadata_filename); #endif - /* Load settings */ - app->priv->settings = xed_settings_new (); - app->priv->window_settings = g_settings_new ("org.x.editor.state.window"); - app->priv->editor_settings = g_settings_new ("org.x.editor.preferences.editor"); + /* Load settings */ + app->priv->settings = xed_settings_new (); + app->priv->window_settings = g_settings_new ("org.x.editor.state.window"); + app->priv->editor_settings = g_settings_new ("org.x.editor.preferences.editor"); - set_initial_theme_style (app); + set_initial_theme_style (app); - /* - * We use the default gtksourceview style scheme manager so that plugins - * can obtain it easily without a xed specific api, but we need to - * add our search path at startup before the manager is actually used. - */ - manager = gtk_source_style_scheme_manager_get_default (); - gtk_source_style_scheme_manager_append_search_path (manager, xed_dirs_get_user_styles_dir ()); + /* Load custom css */ + css_file = g_file_new_for_uri ("resource:///org/x/editor/css/xed-style.css"); + provider = gtk_css_provider_new (); + if (gtk_css_provider_load_from_file (provider, css_file, &error)) + { + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } + else + { + g_warning ("Could not load css provider: %s", error->message); + g_error_free (error); + } - app->priv->engine = xed_plugins_engine_get_default (); - app->priv->extensions = peas_extension_set_new (PEAS_ENGINE (app->priv->engine), - XED_TYPE_APP_ACTIVATABLE, - "app", app, - NULL); + /* + * We use the default gtksourceview style scheme manager so that plugins + * can obtain it easily without a xed specific api, but we need to + * add our search path at startup before the manager is actually used. + */ + manager = gtk_source_style_scheme_manager_get_default (); + gtk_source_style_scheme_manager_append_search_path (manager, xed_dirs_get_user_styles_dir ()); - g_signal_connect (app->priv->extensions, "extension-added", - G_CALLBACK (extension_added), app); + app->priv->engine = xed_plugins_engine_get_default (); + app->priv->extensions = peas_extension_set_new (PEAS_ENGINE (app->priv->engine), + XED_TYPE_APP_ACTIVATABLE, + "app", app, + NULL); - g_signal_connect (app->priv->extensions, "extension-removed", - G_CALLBACK (extension_removed), app); + g_signal_connect (app->priv->extensions, "extension-added", + G_CALLBACK (extension_added), app); - peas_extension_set_foreach (app->priv->extensions, - (PeasExtensionSetForeachFunc) extension_added, - app); + g_signal_connect (app->priv->extensions, "extension-removed", + G_CALLBACK (extension_removed), app); + + peas_extension_set_foreach (app->priv->extensions, + (PeasExtensionSetForeachFunc) extension_added, + app); } static gboolean diff --git a/xed/xed-commands-view.c b/xed/xed-commands-view.c index a09b401..f692e55 100644 --- a/xed/xed-commands-view.c +++ b/xed/xed-commands-view.c @@ -41,7 +41,7 @@ #include "xed-window.h" #include "xed-window-private.h" #include "xed-paned.h" - +#include "xed-view-frame.h" void _xed_cmd_view_show_toolbar (GtkAction *action, @@ -144,6 +144,25 @@ _xed_cmd_view_show_bottom_pane (GtkAction *action, } } +void +_xed_cmd_view_toggle_overview_map (GtkAction *action, + XedWindow *window) +{ + XedTab *tab; + XedViewFrame *frame; + GtkFrame *map_frame; + gboolean visible; + + xed_debug (DEBUG_COMMANDS); + + tab = xed_window_get_active_tab (window); + frame = XED_VIEW_FRAME (_xed_tab_get_view_frame (tab)); + map_frame = xed_view_frame_get_map_frame (frame); + visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + + gtk_widget_set_visible (GTK_WIDGET (map_frame), visible); +} + void _xed_cmd_view_toggle_fullscreen_mode (GtkAction *action, XedWindow *window) diff --git a/xed/xed-commands.h b/xed/xed-commands.h index 1977ddc..1ff5ae9 100644 --- a/xed/xed-commands.h +++ b/xed/xed-commands.h @@ -45,6 +45,7 @@ void _xed_cmd_view_show_toolbar (GtkAction *action, XedWindow *window); void _xed_cmd_view_show_statusbar (GtkAction *action, XedWindow *window); void _xed_cmd_view_show_side_pane (GtkAction *action, XedWindow *window); void _xed_cmd_view_show_bottom_pane (GtkAction *action, XedWindow *window); +void _xed_cmd_view_toggle_overview_map (GtkAction *action, XedWindow *window); void _xed_cmd_view_toggle_fullscreen_mode (GtkAction *action, XedWindow *window); void _xed_cmd_view_toggle_word_wrap (GtkAction *action, XedWindow *window); void _xed_cmd_view_leave_fullscreen_mode (GtkAction *action, XedWindow *window); diff --git a/xed/xed-preferences-dialog.c b/xed/xed-preferences-dialog.c index 79efe1d..2e3ad43 100755 --- a/xed/xed-preferences-dialog.c +++ b/xed/xed-preferences-dialog.c @@ -123,6 +123,9 @@ struct _XedPreferencesDialogPrivate /* Highlight matching bracket */ GtkWidget *bracket_matching_checkbutton; + /* Minimap */ + GtkWidget *mini_map_checkbutton; + /* Right margin */ GtkWidget *right_margin_checkbutton; GtkWidget *right_margin_position_spinbutton; @@ -355,6 +358,11 @@ setup_view_page (XedPreferencesDialog *dlg) dlg->priv->right_margin_position_spinbutton, "value", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + g_settings_bind (dlg->priv->ui, + XED_SETTINGS_MINIMAP_VISIBLE, + dlg->priv->mini_map_checkbutton, + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); g_signal_connect (dlg->priv->wrap_text_checkbutton, "toggled", G_CALLBACK (wrap_mode_checkbutton_toggled), dlg); g_signal_connect (dlg->priv->split_checkbutton, "toggled", @@ -1121,6 +1129,7 @@ xed_preferences_dialog_init (XedPreferencesDialog *dlg) dlg->priv->display_line_numbers_checkbutton = GTK_WIDGET (gtk_builder_get_object (builder, "display_line_numbers_checkbutton")); dlg->priv->highlight_current_line_checkbutton = GTK_WIDGET (gtk_builder_get_object (builder, "highlight_current_line_checkbutton")); dlg->priv->bracket_matching_checkbutton = GTK_WIDGET (gtk_builder_get_object (builder, "bracket_matching_checkbutton")); + dlg->priv->mini_map_checkbutton = GTK_WIDGET (gtk_builder_get_object (builder, "mini_map_checkbutton")); dlg->priv->wrap_text_checkbutton = GTK_WIDGET (gtk_builder_get_object (builder, "wrap_text_checkbutton")); dlg->priv->split_checkbutton = GTK_WIDGET (gtk_builder_get_object (builder, "split_checkbutton")); dlg->priv->right_margin_checkbutton = GTK_WIDGET (gtk_builder_get_object (builder, "right_margin_checkbutton")); diff --git a/xed/xed-settings.h b/xed/xed-settings.h index f65c01f..9114a33 100644 --- a/xed/xed-settings.h +++ b/xed/xed-settings.h @@ -98,6 +98,7 @@ void xed_settings_set_list (GSettings *settings, #define XED_SETTINGS_STATUSBAR_VISIBLE "statusbar-visible" #define XED_SETTINGS_SIDE_PANEL_VISIBLE "side-panel-visible" #define XED_SETTINGS_BOTTOM_PANEL_VISIBLE "bottom-panel-visible" +#define XED_SETTINGS_MINIMAP_VISIBLE "minimap-visible" #define XED_SETTINGS_MAX_RECENTS "max-recents" #define XED_SETTINGS_PRINT_SYNTAX_HIGHLIGHTING "print-syntax-highlighting" #define XED_SETTINGS_PRINT_HEADER "print-header" diff --git a/xed/xed-ui.h b/xed/xed-ui.h index acefb5e..24e97b5 100644 --- a/xed/xed-ui.h +++ b/xed/xed-ui.h @@ -159,7 +159,10 @@ static const GtkToggleActionEntry xed_always_sensitive_toggle_menu_entries[] = G_CALLBACK (_xed_cmd_view_toggle_fullscreen_mode), FALSE }, { "ViewWordWrap", NULL, N_("_Word wrap"), NULL, N_("Set word wrap for the current document"), - G_CALLBACK (_xed_cmd_view_toggle_word_wrap), FALSE } + G_CALLBACK (_xed_cmd_view_toggle_word_wrap), FALSE }, + { "ViewOverviewMap", NULL, N_("_Overview Map"), NULL, + N_("Show or hide the overview map for the current view"), + G_CALLBACK (_xed_cmd_view_toggle_overview_map), FALSE } }; /* separate group, should be always sensitive except when there are no panes */ diff --git a/xed/xed-view-frame.c b/xed/xed-view-frame.c index eba0299..7db49f0 100644 --- a/xed/xed-view-frame.c +++ b/xed/xed-view-frame.c @@ -28,6 +28,7 @@ #include "xed-marshal.h" #include "xed-debug.h" #include "xed-utils.h" +#include "xed-settings.h" #include #include @@ -40,6 +41,10 @@ struct _XedViewFramePrivate { GtkWidget *view; + GtkFrame *map_frame; + GtkSourceMap *map; + + GSettings *ui_settings; GtkTextMark *start_mark; @@ -96,6 +101,8 @@ xed_view_frame_dispose (GObject *object) gtk_source_file_set_mount_operation_factory (file, NULL, NULL, NULL); } + g_clear_object (&frame->priv->ui_settings); + G_OBJECT_CLASS (xed_view_frame_parent_class)->dispose (object); } @@ -499,6 +506,8 @@ xed_view_frame_class_init (XedViewFrameClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/x/editor/ui/xed-view-frame.ui"); gtk_widget_class_bind_template_child_private (widget_class, XedViewFrame, view); + gtk_widget_class_bind_template_child_private (widget_class, XedViewFrame, map_frame); + gtk_widget_class_bind_template_child_private (widget_class, XedViewFrame, map); gtk_widget_class_bind_template_child_private (widget_class, XedViewFrame, revealer); gtk_widget_class_bind_template_child_private (widget_class, XedViewFrame, search_entry); } @@ -519,11 +528,23 @@ xed_view_frame_init (XedViewFrame *frame) XedDocument *doc; GtkSourceFile *file; GdkRGBA transparent = {0, 0, 0, 0}; + PangoFontDescription *font_desc; frame->priv = xed_view_frame_get_instance_private (frame); gtk_widget_init_template (GTK_WIDGET (frame)); + frame->priv->ui_settings = g_settings_new ("org.x.editor.preferences.ui"); + g_settings_bind (frame->priv->ui_settings, + XED_SETTINGS_MINIMAP_VISIBLE, + frame->priv->map_frame, + "visible", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_NO_SENSITIVITY); + + font_desc = pango_font_description_from_string ("Monospace 2"); + g_object_set (frame->priv->map, "font-desc", font_desc, NULL); + pango_font_description_free (font_desc); + gtk_widget_override_background_color (GTK_WIDGET (frame), 0, &transparent); doc = xed_view_frame_get_document (frame); @@ -559,6 +580,14 @@ xed_view_frame_get_view (XedViewFrame *frame) return XED_VIEW (frame->priv->view); } +GtkFrame * +xed_view_frame_get_map_frame (XedViewFrame *frame) +{ + g_return_val_if_fail (XED_IS_VIEW_FRAME (frame), NULL); + + return frame->priv->map_frame; +} + void xed_view_frame_popup_goto_line (XedViewFrame *frame) { diff --git a/xed/xed-view-frame.h b/xed/xed-view-frame.h index 1cb6d12..6e1c7fe 100644 --- a/xed/xed-view-frame.h +++ b/xed/xed-view-frame.h @@ -61,6 +61,8 @@ XedDocument *xed_view_frame_get_document (XedViewFrame *frame); XedView *xed_view_frame_get_view (XedViewFrame *frame); +GtkFrame *xed_view_frame_get_map_frame (XedViewFrame *frame); + void xed_view_frame_popup_goto_line (XedViewFrame *frame); gboolean xed_view_frame_get_search_popup_visible (XedViewFrame *frame); diff --git a/xed/xed-window-private.h b/xed/xed-window-private.h index c7ec07a..2e3b5c1 100644 --- a/xed/xed-window-private.h +++ b/xed/xed-window-private.h @@ -79,6 +79,7 @@ struct _XedWindowPrivate guint spaces_instead_of_tabs_id; guint language_changed_id; guint use_word_wrap_id; + guint show_overview_map_id; /* Menus & Toolbars */ GtkUIManager *manager; diff --git a/xed/xed-window.c b/xed/xed-window.c index 7a7496d..9d8e369 100644 --- a/xed/xed-window.c +++ b/xed/xed-window.c @@ -2170,6 +2170,28 @@ word_wrap_changed (GObject *object, g_signal_handlers_unblock_by_func (action, G_CALLBACK (_xed_cmd_view_toggle_word_wrap), window); } +static void +show_overview_map_changed (GObject *object, + GParamSpec *pspec, + XedWindow *window) +{ + GtkFrame *map_frame; + GtkAction *action; + gboolean overveiw_map_visible = FALSE; + + map_frame = GTK_FRAME (object); + action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, "ViewOverviewMap"); + + if (gtk_widget_get_visible (map_frame)) + { + overveiw_map_visible = TRUE; + } + + g_signal_handlers_block_by_func (action, G_CALLBACK (_xed_cmd_view_toggle_overview_map), window); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), overveiw_map_visible); + g_signal_handlers_unblock_by_func (action, G_CALLBACK (_xed_cmd_view_toggle_overview_map), window); +} + static void notebook_switch_page (GtkNotebook *book, GtkWidget *pg, @@ -2177,6 +2199,7 @@ notebook_switch_page (GtkNotebook *book, XedWindow *window) { XedView *view; + GtkFrame *map_frame; XedTab *tab; GtkAction *action; gchar *action_name; @@ -2184,7 +2207,7 @@ notebook_switch_page (GtkNotebook *book, /* CHECK: I don't know why but it seems notebook_switch_page is called two times every time the user change the active tab */ - tab = XED_TAB(gtk_notebook_get_nth_page (book, page_num)); + tab = XED_TAB (gtk_notebook_get_nth_page (book, page_num)); if (tab == window->priv->active_tab) { return; @@ -2209,6 +2232,12 @@ notebook_switch_page (GtkNotebook *book, g_signal_handler_disconnect (xed_tab_get_view (window->priv->active_tab), window->priv->use_word_wrap_id); window->priv->use_word_wrap_id = 0; } + + if (window->priv->show_overview_map_id) + { + g_signal_handler_disconnect (xed_view_frame_get_map_frame (_xed_tab_get_view_frame (window->priv->active_tab)), window->priv->show_overview_map_id); + window->priv->show_overview_map_id = 0; + } } /* set the active tab */ @@ -2236,6 +2265,7 @@ notebook_switch_page (GtkNotebook *book, update_languages_menu (window); view = xed_tab_get_view (tab); + map_frame = xed_view_frame_get_map_frame (_xed_tab_get_view_frame (tab)); /* sync the statusbar */ update_cursor_position_statusbar (GTK_TEXT_BUFFER (xed_tab_get_document (tab)), window); @@ -2256,11 +2286,15 @@ notebook_switch_page (GtkNotebook *book, window->priv->use_word_wrap_id = g_signal_connect (view, "notify::wrap-mode", G_CALLBACK (word_wrap_changed), window); + window->priv->show_overview_map_id = g_signal_connect (map_frame, "notify::visible", + G_CALLBACK (show_overview_map_changed), window); + /* call it for the first time */ tab_width_changed (G_OBJECT (view), NULL, window); spaces_instead_of_tabs_changed (G_OBJECT (view), NULL, window); language_changed (G_OBJECT (xed_tab_get_document (tab)), NULL, window); word_wrap_changed (G_OBJECT (view), NULL, window); + show_overview_map_changed (G_OBJECT (map_frame), NULL, window); g_signal_emit (G_OBJECT (window), signals[ACTIVE_TAB_CHANGED], 0, window->priv->active_tab); } @@ -3025,6 +3059,7 @@ notebook_tab_removed (XedNotebook *notebook, XedWindow *window) { XedView *view; + XedViewFrame *frame; XedDocument *doc; xed_debug (DEBUG_WINDOW); @@ -3034,6 +3069,7 @@ notebook_tab_removed (XedNotebook *notebook, --window->priv->num_tabs; view = xed_tab_get_view (tab); + frame = _xed_tab_get_view_frame (tab); doc = xed_tab_get_document (tab); g_signal_handlers_disconnect_by_func (tab, G_CALLBACK (sync_name), window); @@ -3074,6 +3110,12 @@ notebook_tab_removed (XedNotebook *notebook, window->priv->use_word_wrap_id = 0; } + if (window->priv->show_overview_map_id && tab == xed_window_get_active_tab (window)) + { + g_signal_handler_disconnect (xed_view_frame_get_map_frame (frame), window->priv->show_overview_map_id); + window->priv->show_overview_map_id = 0; + } + g_return_if_fail(window->priv->num_tabs >= 0); if (window->priv->num_tabs == 0) {