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);
static void gtk_tree_view_search_preedit_changed (GtkIMContext *im_context,
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,
GtkTreeView *tree_view);
static gboolean gtk_tree_view_real_search_enable_popdown(gpointer data);
@ -5987,73 +5990,73 @@ gtk_tree_view_key_press (GtkWidget *widget,
return FALSE;
}
/* We pass the event to the search_entry. If its text changes, then we start
* the typeahead find capabilities. */
/* Initially, before the search window is visible, we pass the event to the
* 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))
&& tree_view->priv->enable_search
&& !tree_view->priv->search_custom_entry_set
&& !gtk_tree_view_search_key_cancels_search (event->keyval))
{
GdkEvent *new_event;
char *old_text;
const char *new_text;
gboolean retval;
GdkScreen *screen;
gboolean text_modified;
gulong popup_menu_id;
GtkWidget *search_window;
gtk_tree_view_ensure_interactive_directory (tree_view);
/* Make a copy of the current text */
old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
new_event = gdk_event_copy ((GdkEvent *) event);
g_object_unref (((GdkEventKey *) new_event)->window);
((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
gtk_widget_realize (tree_view->priv->search_window);
search_window = tree_view->priv->search_window;
if (!gtk_widget_is_visible (search_window))
{
GtkIMContext *im_context =
_gtk_entry_get_im_context (GTK_ENTRY (tree_view->priv->search_entry));
popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
"popup-menu", G_CALLBACK (gtk_true),
NULL);
tree_view->priv->imcontext_changed = FALSE;
gtk_im_context_filter_keypress (im_context, event);
/* Move the entry off screen */
screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
gdk_screen_get_width (screen) + 1,
gdk_screen_get_height (screen) + 1);
gtk_widget_show (tree_view->priv->search_window);
if (tree_view->priv->imcontext_changed)
{
GdkDevice *device;
/* Send the event to the window. If the preedit_changed signal is emitted
* during this event, we will set priv->imcontext_changed */
tree_view->priv->imcontext_changed = FALSE;
retval = gtk_widget_event (tree_view->priv->search_window, new_event);
gdk_event_free (new_event);
gtk_widget_hide (tree_view->priv->search_window);
device = gdk_event_get_device ((GdkEvent *) event);
if (gtk_tree_view_real_start_interactive_search (tree_view,
device,
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;
}
}
}
else
{
GdkEvent *new_event;
gulong popup_menu_id;
g_signal_handler_disconnect (tree_view->priv->search_entry,
popup_menu_id);
new_event = gdk_event_copy ((GdkEvent *) event);
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
* the text has changed.
*/
new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
text_modified = strcmp (old_text, new_text) != 0;
g_free (old_text);
if (tree_view->priv->imcontext_changed || /* we're in a preedit */
(retval && text_modified)) /* ...or the text was modified */
{
if (gtk_tree_view_real_start_interactive_search (tree_view,
gdk_event_get_device ((GdkEvent *) event),
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;
}
}
popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
"popup-menu", G_CALLBACK (gtk_true),
NULL);
/* Because we keep the focus on the treeview, we need to forward the
* key events to the entry, when it is visible. */
gtk_widget_event (search_window, new_event);
gdk_event_free (new_event);
g_signal_handler_disconnect (tree_view->priv->search_entry,
popup_menu_id);
}
}
return FALSE;
@ -11072,8 +11075,11 @@ gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view)
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_transient_for (GTK_WINDOW (tree_view->priv->search_window),
GTK_WINDOW (toplevel));
g_signal_connect (tree_view->priv->search_window, "delete-event",
G_CALLBACK (gtk_tree_view_search_delete_event),
tree_view);
@ -11111,6 +11117,10 @@ gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view)
"preedit-changed",
G_CALLBACK (gtk_tree_view_search_preedit_changed),
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),
tree_view->priv->search_entry);
@ -11176,6 +11186,10 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
/* done, show it */
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);
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);
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_change (tree_view->priv->search_entry, device, TRUE);