diff --git a/plugins/highlight-word-selection/highlight-word-selection.vala b/plugins/highlight-word-selection/highlight-word-selection.vala index 2bf05f422a..9306321f2e 100644 --- a/plugins/highlight-word-selection/highlight-word-selection.vala +++ b/plugins/highlight-word-selection/highlight-word-selection.vala @@ -48,12 +48,8 @@ public class Scratch.Plugins.HighlightSelectedWords : Peas.ExtensionBase, Peas.A }); } - public void on_selection_changed (ref Gtk.TextIter start, ref Gtk.TextIter end) { - var window_search_context = main_window != null ? main_window.search_bar.search_context : null; - - if (window_search_context == null || - window_search_context.settings.search_text == "" || - window_search_context.get_occurrences_count () == 0) { + public void on_selection_changed (ref Gtk.TextIter start, ref Gtk.TextIter end) requires (main_window != null) { + if (!main_window.has_successful_search ()) { // Perform plugin selection when there is no ongoing and successful search current_search_context = new Gtk.SourceSearchContext ( (Gtk.SourceBuffer)current_source.buffer, diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 95f15c8195..078184b668 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -43,7 +43,6 @@ namespace Scratch { // Widgets public Scratch.HeaderBar toolbar; - private Gtk.Revealer search_revealer; public Scratch.Widgets.SearchBar search_bar; private Code.WelcomeView welcome_view; private Code.Terminal terminal; @@ -89,7 +88,6 @@ namespace Scratch { public const string ACTION_REVERT = "action-revert"; public const string ACTION_SAVE = "action-save"; public const string ACTION_SAVE_AS = "action-save-as"; - public const string ACTION_SHOW_FIND = "action-show-find"; public const string ACTION_TEMPLATES = "action-templates"; public const string ACTION_SHOW_REPLACE = "action-show-replace"; public const string ACTION_TO_LOWER_CASE = "action-to-lower-case"; @@ -101,6 +99,7 @@ namespace Scratch { public const string ACTION_ZOOM_IN = "action-zoom-in"; public const string ACTION_ZOOM_OUT = "action-zoom-out"; public const string ACTION_TOGGLE_COMMENT = "action-toggle-comment"; + public const string ACTION_TOGGLE_SHOW_FIND = "action-toggle_show-find"; public const string ACTION_TOGGLE_SIDEBAR = "action-toggle-sidebar"; public const string ACTION_TOGGLE_OUTLINE = "action-toggle-outline"; public const string ACTION_TOGGLE_TERMINAL = "action-toggle-terminal"; @@ -127,7 +126,7 @@ namespace Scratch { private Services.GitManager git_manager; private const ActionEntry[] ACTION_ENTRIES = { - { ACTION_FIND, action_fetch, "s" }, + { ACTION_FIND, action_find, "s"}, { ACTION_FIND_NEXT, action_find_next }, { ACTION_FIND_PREVIOUS, action_find_previous }, { ACTION_FIND_GLOBAL, action_find_global, "s" }, @@ -139,7 +138,7 @@ namespace Scratch { { ACTION_REVERT, action_revert }, { ACTION_SAVE, action_save }, { ACTION_SAVE_AS, action_save_as }, - { ACTION_SHOW_FIND, action_show_fetch, null, "false" }, + { ACTION_TOGGLE_SHOW_FIND, action_toggle_show_find, null, "false" }, { ACTION_TEMPLATES, action_templates }, { ACTION_GO_TO, action_go_to }, { ACTION_SORT_LINES, action_sort_lines }, @@ -378,7 +377,7 @@ namespace Scratch { private void update_toolbar_button (string name, bool new_state) { switch (name) { - case ACTION_SHOW_FIND: + case ACTION_TOGGLE_SHOW_FIND: if (new_state) { toolbar.find_button.tooltip_markup = Granite.markup_accel_tooltip ( {"Escape"}, @@ -391,7 +390,7 @@ namespace Scratch { ); } - search_revealer.set_reveal_child (new_state); + search_bar.reveal (new_state); break; case ACTION_TOGGLE_SIDEBAR: @@ -444,20 +443,6 @@ namespace Scratch { // SearchBar search_bar = new Scratch.Widgets.SearchBar (this); - search_revealer = new Gtk.Revealer (); - search_revealer.add (search_bar); - - search_bar.map.connect_after ((w) => { /* signalled when reveal child */ - set_search_text (); - }); - search_bar.search_entry.unmap.connect_after (() => { /* signalled when reveal child */ - search_bar.search_entry.text = ""; - search_bar.highlight_none (); - }); - search_bar.search_empty.connect (() => { - folder_manager_view.clear_badges (); - }); - welcome_view = new Code.WelcomeView (this); document_view = new Scratch.Widgets.DocumentView (this); // Handle Drag-and-drop for files functionality on welcome screen @@ -524,7 +509,7 @@ namespace Scratch { var view_grid = new Gtk.Grid () { orientation = Gtk.Orientation.VERTICAL }; - view_grid.add (search_revealer); + view_grid.add (search_bar); view_grid.add (document_view); content_stack = new Gtk.Stack () { @@ -563,8 +548,6 @@ namespace Scratch { size_group.add_widget (sidebar.headerbar); size_group.add_widget (toolbar); - search_revealer.set_reveal_child (false); - realize.connect (() => { Scratch.saved_state.bind ("sidebar-visible", sidebar, "visible", SettingsBindFlags.DEFAULT); Scratch.saved_state.bind ("outline-visible", document_view , "outline_visible", SettingsBindFlags.DEFAULT); @@ -707,13 +690,12 @@ namespace Scratch { }); } - // private bool on_key_pressed (Gdk.EventKey event) { private bool on_key_pressed (uint keyval, uint keycode, Gdk.ModifierType state) { switch (Gdk.keyval_name (keyval)) { case "Escape": - if (search_revealer.get_child_revealed ()) { - var fetch_action = Utils.action_from_group (ACTION_SHOW_FIND, actions); - fetch_action.set_state (false); + if (search_bar.is_revealed) { + var action = Utils.action_from_group (ACTION_TOGGLE_SHOW_FIND, actions); + action.set_state (false); document_view.current_document.source_view.grab_focus (); } @@ -732,7 +714,7 @@ namespace Scratch { // Set sensitive property for 'delicate' Widgets/GtkActions while private void set_widgets_sensitive (bool val) { // SearchManager's stuffs - Utils.action_from_group (ACTION_SHOW_FIND, actions).set_enabled (val); + Utils.action_from_group (ACTION_TOGGLE_SHOW_FIND, actions).set_enabled (val); Utils.action_from_group (ACTION_GO_TO, actions).set_enabled (val); Utils.action_from_group (ACTION_SHOW_REPLACE, actions).set_enabled (val); // Toolbar Actions @@ -755,6 +737,26 @@ namespace Scratch { return document_view.current_document; } + // If selected text covers more than one line return just the first. + public void set_selected_text_for_search () { + var doc = get_current_document (); + var selected_text = doc != null ? doc.get_selected_text (false) : ""; + var search_term = ""; + if (selected_text.contains ("\n")) { + search_term = selected_text.split ("\n", 2)[0]; + } else { + search_term = selected_text; + } + + if (search_term != "") { + search_bar.set_search_entry_text (search_term); + } + } + + public bool has_successful_search () { + return search_bar.search_occurrences > 0; + } + public void open_folder (File folder) { var foldermanager_file = new FolderManager.File (folder.get_path ()); folder_manager_view.open_folder (foldermanager_file); @@ -1168,32 +1170,38 @@ namespace Scratch { } /** Not a toggle action - linked to keyboard short cut (Ctrl-f). **/ - private string current_search_term = ""; - private void action_fetch (SimpleAction action, Variant? param) { - current_search_term = param != null ? param.get_string () : ""; - if (!search_revealer.child_revealed) { - var show_find_action = Utils.action_from_group (ACTION_SHOW_FIND, actions); + private void action_find (SimpleAction action, Variant? param) { + find (param != null ? param.get_string () : ""); + } + + private void find (string search_term = "") { + if (!search_bar.is_revealed) { + var show_find_action = Utils.action_from_group (ACTION_TOGGLE_SHOW_FIND, actions); if (show_find_action.enabled) { - /* Toggling the fetch action causes this function to be called again but the search_revealer child - * is still not revealed so nothing more happens. We use the map signal on the search entry - * to set it up once it has been revealed. */ - show_find_action.set_state (true); + show_find_action.activate (new Variant ("b", true)); } + } + + if (search_term != "") { + search_bar.set_search_entry_text (search_term); } else { - set_search_text (); + set_selected_text_for_search (); } + + search_bar.search (); } private void action_show_replace (SimpleAction action) { - action_fetch (action, null); + find (); // May have to wait for the search bar to be revealed before we can grab focus - if (search_revealer.child_revealed) { - search_bar.replace_entry.grab_focus (); + + if (search_bar.is_revealed) { + search_bar.focus_replace_entry (); } else { - ulong map_handler = 0; - map_handler = search_bar.map.connect_after (() => { - search_bar.replace_entry.grab_focus (); - search_bar.disconnect (map_handler); + search_bar.reveal (true); + Idle.add (() => { + search_bar.focus_replace_entry (); + return Source.REMOVE; }); } } @@ -1207,29 +1215,11 @@ namespace Scratch { } private void action_find_global (SimpleAction action, Variant? param) { - var selected_text = ""; - var search_path = ""; - - var current_doc = get_current_document (); - if (current_doc != null) { - selected_text = current_doc.get_selected_text (false); - } - - if (selected_text != "") { - selected_text = selected_text.split ("\n", 2)[0]; - } - // If search entry focused use its text for search term, else use selected text - var term = search_bar.search_entry.has_focus ? - search_bar.search_entry.text : selected_text; - - // If no focused selected text fallback to search entry text if visible - if (term == "" && - !search_bar.search_entry.has_focus && - search_revealer.reveal_child) { - - term = search_bar.search_entry.text; + if (!search_bar.is_focused || search_bar.entry_text == "") { + set_selected_text_for_search (); } + var search_path = ""; if (param != null && param.get_string () != "") { search_path = param.get_string (); } else { @@ -1237,12 +1227,14 @@ namespace Scratch { } if (search_path != "") { - folder_manager_view.search_global (search_path, term); + folder_manager_view.search_global (search_path, search_bar.entry_text); } else { // Fallback to standard search warning ("Unable to perform global search - search document instead"); - action_fetch (action, param); + find (); } + + // No need to reveal searchbar - handled by subsequent find action. } private void update_find_actions () { @@ -1250,7 +1242,7 @@ namespace Scratch { Idle.add (() => { var is_current_doc = get_current_document () != null; Utils.action_from_group (ACTION_FIND, actions).set_enabled (is_current_doc); - Utils.action_from_group (ACTION_SHOW_FIND, actions).set_enabled (is_current_doc); + Utils.action_from_group (ACTION_TOGGLE_SHOW_FIND, actions).set_enabled (is_current_doc); Utils.action_from_group (ACTION_FIND_NEXT, actions).set_enabled (is_current_doc); Utils.action_from_group (ACTION_FIND_PREVIOUS, actions).set_enabled (is_current_doc); var can_global_search = is_current_doc || git_manager.active_project_path != null; @@ -1260,38 +1252,18 @@ namespace Scratch { }); } - private void set_search_text () { - if (current_search_term != "") { - search_bar.search_entry.text = current_search_term; - search_bar.search_entry.grab_focus (); - search_bar.search_next (); - } else if (search_bar.search_entry.text != "") { - // Always search on what is showing in search entry - current_search_term = search_bar.search_entry.text; - search_bar.search_entry.grab_focus (); - } else { - var current_doc = get_current_document (); - // This is also called when all documents are closed. - if (current_doc != null) { - var selected_text = current_doc.get_selected_text (false); - if (selected_text != "" && selected_text.length < MAX_SEARCH_TEXT_LENGTH) { - current_search_term = selected_text.split ("\n", 2)[0]; - search_bar.search_entry.text = current_search_term; - } - - search_bar.search_entry.grab_focus (); /* causes loss of document selection */ + /** Toggle action - linked to toolbar togglebutton. **/ + private void action_toggle_show_find () { + var action = Utils.action_from_group (ACTION_TOGGLE_SHOW_FIND, actions); + var to_show = !action.get_state ().get_boolean (); + action.set_state (to_show); + search_bar.reveal (to_show); + if (to_show) { + search_bar.focus_search_entry (); + if (search_bar.entry_text == "") { + set_selected_text_for_search (); } } - - if (current_search_term != "") { - search_bar.search_next (); /* this selects the next match (if any) */ - } - } - - /** Toggle action - linked to toolbar togglebutton. **/ - private void action_show_fetch () { - var fetch_action = Utils.action_from_group (ACTION_SHOW_FIND, actions); - fetch_action.set_state (!fetch_action.get_state ().get_boolean ()); } private void action_go_to () { diff --git a/src/Widgets/HeaderBar.vala b/src/Widgets/HeaderBar.vala index 791361442e..55e3ce2f0b 100644 --- a/src/Widgets/HeaderBar.vala +++ b/src/Widgets/HeaderBar.vala @@ -114,7 +114,7 @@ public class Scratch.HeaderBar : Hdy.HeaderBar { font_size_box.add (zoom_in_button); find_button = new Gtk.ToggleButton () { - action_name = MainWindow.ACTION_PREFIX + MainWindow.ACTION_SHOW_FIND, + action_name = MainWindow.ACTION_PREFIX + MainWindow.ACTION_TOGGLE_SHOW_FIND, image = new Gtk.Image.from_icon_name ("edit-find-on-page-symbolic", Gtk.IconSize.MENU) }; find_button.tooltip_markup = Granite.markup_accel_tooltip ( diff --git a/src/Widgets/SearchBar.vala b/src/Widgets/SearchBar.vala index 5f43f00155..56a718631d 100644 --- a/src/Widgets/SearchBar.vala +++ b/src/Widgets/SearchBar.vala @@ -20,7 +20,7 @@ */ namespace Scratch.Widgets { - public class SearchBar : Gtk.FlowBox { + public class SearchBar : Gtk.Box { //TODO In Gtk4 use a BinLayout Widget enum CaseSensitiveMode { NEVER, MIXED, @@ -28,6 +28,7 @@ namespace Scratch.Widgets { } public weak MainWindow window { get; construct; } + private Gtk.Button tool_arrow_up; private Gtk.Button tool_arrow_down; @@ -40,32 +41,59 @@ namespace Scratch.Widgets { private Gtk.ComboBoxText case_sensitive_search_button; private Granite.SwitchModelButton regex_search_button; private Granite.SwitchModelButton whole_word_search_button; - public Gtk.SearchEntry search_entry; - public Gtk.SearchEntry replace_entry; - + private Gtk.SearchEntry search_entry; + private Gtk.SearchEntry replace_entry; private Gtk.Label search_occurence_count_label; - private Gtk.Button replace_tool_button; private Gtk.Button replace_all_tool_button; - private Scratch.Widgets.SourceView? text_view = null; private Gtk.TextBuffer? text_buffer = null; - public Gtk.SourceSearchContext? search_context { get; private set; default = null; } + private Gtk.SourceSearchContext? search_context; + private uint update_search_label_timeout_id = 0; + private Gtk.Revealer revealer; - public signal void search_empty (); + public bool is_focused { + get { + return search_entry.has_focus || replace_entry.has_focus; + } + } - private uint update_search_label_timeout_id = 0; + public bool is_revealed { + get { + return revealer.child_revealed; + } + } + + public string entry_text { + get { + return search_entry.text; + } + } + + public uint search_occurrences { + get { + if (search_context == null || + search_context.settings.search_text == "") { + + return 0; + } else { + return search_context.get_occurrences_count (); + } + } + } + + public uint transition_time_msec { + get { + return revealer.transition_duration + 10; + } + } - /** - * Create a new SearchBar widget. - * - * following actions : Fetch, ShowGoTo, ShowReplace, or null. - **/ public SearchBar (MainWindow window) { Object (window: window); } construct { + this.orientation = HORIZONTAL; search_entry = new Gtk.SearchEntry () { hexpand = true, placeholder_text = _("Find") @@ -137,10 +165,10 @@ namespace Scratch.Widgets { }; search_menubutton.add (search_buttonbox); - cycle_search_button.toggled.connect (on_search_entry_text_changed); - case_sensitive_search_button.changed.connect (on_search_entry_text_changed); - whole_word_search_button.toggled.connect (on_search_entry_text_changed); - regex_search_button.toggled.connect (on_search_entry_text_changed); + cycle_search_button.toggled.connect (on_search_parameters_changed); + case_sensitive_search_button.changed.connect (on_search_parameters_changed); + whole_word_search_button.toggled.connect (on_search_parameters_changed); + regex_search_button.toggled.connect (on_search_parameters_changed); Scratch.settings.bind ("cyclic-search", cycle_search_button, "active", SettingsBindFlags.DEFAULT); Scratch.settings.bind ("wholeword-search", whole_word_search_button, "active", SettingsBindFlags.DEFAULT); @@ -190,10 +218,9 @@ namespace Scratch.Widgets { replace_flow_box_child.add (replace_grid); // Connecting to some signals - search_entry.changed.connect (on_search_entry_text_changed); + search_entry.changed.connect (on_search_parameters_changed); search_entry.key_press_event.connect (on_search_entry_key_press); search_entry.focus_in_event.connect (on_search_entry_focused_in); - search_entry.search_changed.connect (update_search_widgets); search_entry.icon_release.connect ((p0, p1) => { if (p0 == Gtk.EntryIconPosition.PRIMARY) { search_next (); @@ -209,13 +236,21 @@ namespace Scratch.Widgets { entry_context.set_path (entry_path); entry_context.add_class ("entry"); - selection_mode = Gtk.SelectionMode.NONE; - column_spacing = 6; - max_children_per_line = 2; - get_style_context ().add_class ("search-bar"); - add (search_flow_box_child); - add (replace_flow_box_child); + var flowbox = new Gtk.FlowBox () { + selection_mode = Gtk.SelectionMode.NONE, + column_spacing = 6, + max_children_per_line = 2 + }; + flowbox.get_style_context ().add_class ("search-bar"); + flowbox.add (search_flow_box_child); + flowbox.add (replace_flow_box_child); + revealer = new Gtk.Revealer () { + child = flowbox, + reveal_child = false + }; + + add (revealer); update_search_widgets (); } @@ -233,11 +268,9 @@ namespace Scratch.Widgets { return; } else if (this.text_buffer != null) { this.text_buffer.changed.disconnect (on_text_buffer_changed); - this.text_view.selection_changed.disconnect (on_selection_changed); } this.text_view = text_view; - this.text_view.selection_changed.connect (on_selection_changed); this.text_buffer = text_view.get_buffer (); this.text_buffer.changed.connect (on_text_buffer_changed); this.search_context = new Gtk.SourceSearchContext (text_buffer as Gtk.SourceBuffer, null); @@ -251,21 +284,6 @@ namespace Scratch.Widgets { update_search_widgets (); } - private void on_selection_changed () { - - var selected_text = text_view.get_selected_text (); - bool clear_required; - if (search_context.settings.case_sensitive) { - clear_required = selected_text != search_entry.text; - } else { - clear_required = selected_text.down () != search_entry.text.down (); - } - - if (clear_required) { - search_entry.text = ""; - } - } - private void on_replace_entry_activate () { if (text_buffer == null) { warning ("No valid buffer to replace"); @@ -307,12 +325,8 @@ namespace Scratch.Widgets { this.window.get_current_document ().toggle_changed_handlers (true); } - private void on_search_entry_text_changed () { - if (search_context == null) { // This can happen during start up - debug ("search entry changed with null context"); - return; - } - + // Called when one of the settings buttons or the search term changes + private void on_search_parameters_changed () requires (search_context != null) { var search_string = search_entry.text; search_context.settings.search_text = search_string; var case_mode = (CaseSensitiveMode)(case_sensitive_search_button.active); @@ -334,10 +348,6 @@ namespace Scratch.Widgets { search_context.settings.regex_enabled = regex_search_button.active; update_search_widgets (); - - if (search_entry.text == "") { - search_empty (); - } } private bool on_search_entry_focused_in (Gdk.EventFocus event) { @@ -350,6 +360,7 @@ namespace Scratch.Widgets { } public bool search () { + search_entry.grab_focus (); if (search_context == null) { return false; } @@ -477,6 +488,26 @@ namespace Scratch.Widgets { } } + public void focus_search_entry () { + search_entry.grab_focus (); + } + + public void focus_replace_entry () { + replace_entry.grab_focus (); + } + + public void reveal (bool to_reveal) { + revealer.reveal_child = to_reveal; + // Clear entry when searchbar is hidden + if (is_revealed && !to_reveal) { + set_search_entry_text (""); + } + } + + public void set_search_entry_text (string text) { + search_entry.text = text; + } + private bool on_search_entry_key_press (Gdk.EventKey event) { /* We don't need to perform search if there is nothing to search... */ if (search_entry.text == "") {