GtkTreeView: Rework the search window hack so it also works on Wayland

The search window of a tree view was implemented by showing without
making it visible by by positioning it outside the screen edge. This is
not possible on Wayland, so implement another method for being able to
enter text into a non-visible entry.

The new method is implemented by, before showing the window, pass the
key event directly to the IM context backing the entry. If the key
event triggered the context to commit new text or change the preedit
content, the search window is shown, and from that point the key events
are forwarded directly to the entry widget.

https://bugzilla.gnome.org/show_bug.cgi?id=756780
This commit is contained in:
Jonas Ådahl 2015-10-18 21:23:12 +08:00
parent bcb28adba3
commit aedd193c69

View File

@ -801,6 +801,9 @@ static void gtk_tree_view_search_disable_popdown (GtkEntry *entry
gpointer data); gpointer data);
static void gtk_tree_view_search_preedit_changed (GtkIMContext *im_context, static void gtk_tree_view_search_preedit_changed (GtkIMContext *im_context,
GtkTreeView *tree_view); GtkTreeView *tree_view);
static void gtk_tree_view_search_commit (GtkIMContext *im_context,
gchar *buf,
GtkTreeView *tree_view);
static void gtk_tree_view_search_activate (GtkEntry *entry, static void gtk_tree_view_search_activate (GtkEntry *entry,
GtkTreeView *tree_view); GtkTreeView *tree_view);
static gboolean gtk_tree_view_real_search_enable_popdown(gpointer data); static gboolean gtk_tree_view_real_search_enable_popdown(gpointer data);
@ -5987,73 +5990,73 @@ gtk_tree_view_key_press (GtkWidget *widget,
return FALSE; return FALSE;
} }
/* We pass the event to the search_entry. If its text changes, then we start /* Initially, before the search window is visible, we pass the event to the
* the typeahead find capabilities. */ * IM context of the search entry box. If it triggers a commit or a preedit,
* we then show the search window without loosing tree view focus.
* If the seach window is already visible, we forward the events to it,
* keeping the focus on the tree view.
*/
if (gtk_widget_has_focus (GTK_WIDGET (tree_view)) if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
&& tree_view->priv->enable_search && tree_view->priv->enable_search
&& !tree_view->priv->search_custom_entry_set && !tree_view->priv->search_custom_entry_set
&& !gtk_tree_view_search_key_cancels_search (event->keyval)) && !gtk_tree_view_search_key_cancels_search (event->keyval))
{ {
GdkEvent *new_event; GtkWidget *search_window;
char *old_text;
const char *new_text;
gboolean retval;
GdkScreen *screen;
gboolean text_modified;
gulong popup_menu_id;
gtk_tree_view_ensure_interactive_directory (tree_view); gtk_tree_view_ensure_interactive_directory (tree_view);
/* Make a copy of the current text */ search_window = tree_view->priv->search_window;
old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry))); if (!gtk_widget_is_visible (search_window))
new_event = gdk_event_copy ((GdkEvent *) event); {
g_object_unref (((GdkEventKey *) new_event)->window); GtkIMContext *im_context =
((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window)); _gtk_entry_get_im_context (GTK_ENTRY (tree_view->priv->search_entry));
gtk_widget_realize (tree_view->priv->search_window);
popup_menu_id = g_signal_connect (tree_view->priv->search_entry, tree_view->priv->imcontext_changed = FALSE;
"popup-menu", G_CALLBACK (gtk_true), gtk_im_context_filter_keypress (im_context, event);
NULL);
/* Move the entry off screen */ if (tree_view->priv->imcontext_changed)
screen = gtk_widget_get_screen (GTK_WIDGET (tree_view)); {
gtk_window_move (GTK_WINDOW (tree_view->priv->search_window), GdkDevice *device;
gdk_screen_get_width (screen) + 1,
gdk_screen_get_height (screen) + 1);
gtk_widget_show (tree_view->priv->search_window);
/* Send the event to the window. If the preedit_changed signal is emitted device = gdk_event_get_device ((GdkEvent *) event);
* during this event, we will set priv->imcontext_changed */ if (gtk_tree_view_real_start_interactive_search (tree_view,
tree_view->priv->imcontext_changed = FALSE; device,
retval = gtk_widget_event (tree_view->priv->search_window, new_event); FALSE))
gdk_event_free (new_event); {
gtk_widget_hide (tree_view->priv->search_window); gtk_widget_grab_focus (GTK_WIDGET (tree_view));
return TRUE;
}
else
{
gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
return FALSE;
}
}
}
else
{
GdkEvent *new_event;
gulong popup_menu_id;
g_signal_handler_disconnect (tree_view->priv->search_entry, new_event = gdk_event_copy ((GdkEvent *) event);
popup_menu_id); g_object_unref (((GdkEventKey *) new_event)->window);
((GdkEventKey *) new_event)->window =
g_object_ref (gtk_widget_get_window (search_window));
gtk_widget_realize (search_window);
/* We check to make sure that the entry tried to handle the text, and that popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
* the text has changed. "popup-menu", G_CALLBACK (gtk_true),
*/ NULL);
new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
text_modified = strcmp (old_text, new_text) != 0; /* Because we keep the focus on the treeview, we need to forward the
g_free (old_text); * key events to the entry, when it is visible. */
if (tree_view->priv->imcontext_changed || /* we're in a preedit */ gtk_widget_event (search_window, new_event);
(retval && text_modified)) /* ...or the text was modified */ gdk_event_free (new_event);
{
if (gtk_tree_view_real_start_interactive_search (tree_view, g_signal_handler_disconnect (tree_view->priv->search_entry,
gdk_event_get_device ((GdkEvent *) event), popup_menu_id);
FALSE))
{ }
gtk_widget_grab_focus (GTK_WIDGET (tree_view));
return TRUE;
}
else
{
gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
return FALSE;
}
}
} }
return FALSE; return FALSE;
@ -11072,8 +11075,11 @@ gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view)
GTK_WINDOW (tree_view->priv->search_window)); GTK_WINDOW (tree_view->priv->search_window));
gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window), gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
GDK_WINDOW_TYPE_HINT_UTILITY); GDK_WINDOW_TYPE_HINT_UTILITY);
gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE); gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
gtk_window_set_transient_for (GTK_WINDOW (tree_view->priv->search_window),
GTK_WINDOW (toplevel));
g_signal_connect (tree_view->priv->search_window, "delete-event", g_signal_connect (tree_view->priv->search_window, "delete-event",
G_CALLBACK (gtk_tree_view_search_delete_event), G_CALLBACK (gtk_tree_view_search_delete_event),
tree_view); tree_view);
@ -11111,6 +11117,10 @@ gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view)
"preedit-changed", "preedit-changed",
G_CALLBACK (gtk_tree_view_search_preedit_changed), G_CALLBACK (gtk_tree_view_search_preedit_changed),
tree_view); tree_view);
g_signal_connect (_gtk_entry_get_im_context (GTK_ENTRY (tree_view->priv->search_entry)),
"commit",
G_CALLBACK (gtk_tree_view_search_commit),
tree_view);
gtk_container_add (GTK_CONTAINER (vbox), gtk_container_add (GTK_CONTAINER (vbox),
tree_view->priv->search_entry); tree_view->priv->search_entry);
@ -11176,6 +11186,10 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
/* done, show it */ /* done, show it */
tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data); tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
/* Grab focus without selecting all the text. */
gtk_entry_grab_focus_without_selecting (GTK_ENTRY (tree_view->priv->search_entry));
gtk_widget_show (tree_view->priv->search_window); gtk_widget_show (tree_view->priv->search_window);
if (tree_view->priv->search_entry_changed_id == 0) if (tree_view->priv->search_entry_changed_id == 0)
{ {
@ -11191,9 +11205,6 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
tree_view); tree_view);
g_source_set_name_by_id (tree_view->priv->typeselect_flush_timeout, "[gtk+] gtk_tree_view_search_entry_flush_timeout"); g_source_set_name_by_id (tree_view->priv->typeselect_flush_timeout, "[gtk+] gtk_tree_view_search_entry_flush_timeout");
/* Grab focus without selecting all the text. */
_gtk_entry_grab_focus (GTK_ENTRY (tree_view->priv->search_entry), FALSE);
/* send focus-in event */ /* send focus-in event */
send_focus_change (tree_view->priv->search_entry, device, TRUE); send_focus_change (tree_view->priv->search_entry, device, TRUE);