1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2013 Red Hat, Inc.
3 *
4 * Authors:
5 * - Bastien Nocera <bnocera@redhat.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21/*
22 * Modified by the GTK+ Team and others 2013. See the AUTHORS
23 * file for a list of people on the GTK+ Team. See the ChangeLog
24 * files for a list of changes. These files are distributed with
25 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
26 */
27
28#include "config.h"
29
30#include "gtksearchbar.h"
31
32#include "gtkbinlayout.h"
33#include "gtkbuildable.h"
34#include "gtkbutton.h"
35#include "gtkcenterbox.h"
36#include "gtkentryprivate.h"
37#include "gtkeventcontrollerkey.h"
38#include "gtkintl.h"
39#include "gtkprivate.h"
40#include "gtkrevealer.h"
41#include "gtksearchentryprivate.h"
42#include "gtksnapshot.h"
43#include "gtkwidgetprivate.h"
44
45/**
46 * GtkSearchBar:
47 *
48 * `GtkSearchBar` is a container made to have a search entry.
49 *
50 * ![An example GtkSearchBar](search-bar.png)
51 *
52 * It can also contain additional widgets, such as drop-down menus,
53 * or buttons. The search bar would appear when a search is started
54 * through typing on the keyboard, or the application’s search mode
55 * is toggled on.
56 *
57 * For keyboard presses to start a search, the search bar must be told
58 * of a widget to capture key events from through
59 * [method@Gtk.SearchBar.set_key_capture_widget]. This widget will
60 * typically be the top-level window, or a parent container of the
61 * search bar. Common shortcuts such as Ctrl+F should be handled as an
62 * application action, or through the menu items.
63 *
64 * You will also need to tell the search bar about which entry you
65 * are using as your search entry using [method@Gtk.SearchBar.connect_entry].
66 *
67 * ## Creating a search bar
68 *
69 * The following example shows you how to create a more complex search
70 * entry.
71 *
72 * [A simple example](https://gitlab.gnome.org/GNOME/gtk/tree/main/examples/search-bar.c)
73 *
74 * # CSS nodes
75 *
76 * ```
77 * searchbar
78 * ╰── revealer
79 * ╰── box
80 * ├── [child]
81 * ╰── [button.close]
82 * ```
83 *
84 * `GtkSearchBar` has a main CSS node with name searchbar. It has a child
85 * node with name revealer that contains a node with name box. The box node
86 * contains both the CSS node of the child widget as well as an optional button
87 * node which gets the .close style class applied.
88 *
89 * # Accessibility
90 *
91 * `GtkSearchBar` uses the %GTK_ACCESSIBLE_ROLE_SEARCH role.
92 */
93
94typedef struct _GtkSearchBarClass GtkSearchBarClass;
95
96struct _GtkSearchBar
97{
98 GtkWidget parent;
99
100 GtkWidget *child;
101 GtkWidget *revealer;
102 GtkWidget *box_center;
103 GtkWidget *close_button;
104
105 GtkWidget *entry;
106 gboolean reveal_child;
107
108 GtkWidget *capture_widget;
109 GtkEventController *capture_widget_controller;
110};
111
112struct _GtkSearchBarClass
113{
114 GtkWidgetClass parent_class;
115};
116
117static void gtk_search_bar_buildable_iface_init (GtkBuildableIface *iface);
118
119G_DEFINE_TYPE_WITH_CODE (GtkSearchBar, gtk_search_bar, GTK_TYPE_WIDGET,
120 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
121 gtk_search_bar_buildable_iface_init))
122
123enum {
124 PROP_0,
125 PROP_SEARCH_MODE_ENABLED,
126 PROP_SHOW_CLOSE_BUTTON,
127 PROP_CHILD,
128 PROP_KEY_CAPTURE_WIDGET,
129 LAST_PROPERTY
130};
131
132static GParamSpec *widget_props[LAST_PROPERTY] = { NULL, };
133
134static GtkBuildableIface *parent_buildable_iface;
135
136static void
137gtk_search_bar_buildable_add_child (GtkBuildable *buildable,
138 GtkBuilder *builder,
139 GObject *child,
140 const char *type)
141{
142 if (GTK_IS_WIDGET (child))
143 gtk_search_bar_set_child (GTK_SEARCH_BAR (buildable), GTK_WIDGET (child));
144 else
145 parent_buildable_iface->add_child (buildable, builder, child, type);
146}
147
148static void
149gtk_search_bar_buildable_iface_init (GtkBuildableIface *iface)
150{
151 parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface);
152
153 iface->add_child = gtk_search_bar_buildable_add_child;
154}
155
156static void
157stop_search_cb (GtkWidget *entry,
158 GtkSearchBar *bar)
159{
160 gtk_revealer_set_reveal_child (GTK_REVEALER (bar->revealer), FALSE);
161}
162
163static void
164reveal_child_changed_cb (GObject *object,
165 GParamSpec *pspec,
166 GtkSearchBar *bar)
167{
168 gboolean reveal_child;
169
170 g_object_get (object, first_property_name: "reveal-child", &reveal_child, NULL);
171
172 if (reveal_child == bar->reveal_child)
173 return;
174
175 bar->reveal_child = reveal_child;
176
177 if (bar->entry)
178 {
179 if (reveal_child && GTK_IS_ENTRY (bar->entry))
180 gtk_entry_grab_focus_without_selecting (GTK_ENTRY (bar->entry));
181 else if (reveal_child && GTK_IS_SEARCH_ENTRY (bar->entry))
182 gtk_widget_grab_focus (widget: bar->entry);
183 else
184 gtk_editable_set_text (GTK_EDITABLE (bar->entry), text: "");
185 }
186
187 g_object_notify_by_pspec (G_OBJECT (bar), pspec: widget_props[PROP_SEARCH_MODE_ENABLED]);
188}
189
190static void
191close_button_clicked_cb (GtkWidget *button,
192 GtkSearchBar *bar)
193{
194 gtk_revealer_set_reveal_child (GTK_REVEALER (bar->revealer), FALSE);
195}
196
197static void
198gtk_search_bar_set_property (GObject *object,
199 guint prop_id,
200 const GValue *value,
201 GParamSpec *pspec)
202{
203 GtkSearchBar *bar = GTK_SEARCH_BAR (object);
204
205 switch (prop_id)
206 {
207 case PROP_SEARCH_MODE_ENABLED:
208 gtk_search_bar_set_search_mode (bar, search_mode: g_value_get_boolean (value));
209 break;
210 case PROP_SHOW_CLOSE_BUTTON:
211 gtk_search_bar_set_show_close_button (bar, visible: g_value_get_boolean (value));
212 break;
213 case PROP_CHILD:
214 gtk_search_bar_set_child (bar, child: g_value_get_object (value));
215 break;
216 case PROP_KEY_CAPTURE_WIDGET:
217 gtk_search_bar_set_key_capture_widget (bar, widget: g_value_get_object (value));
218 break;
219 default:
220 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
221 break;
222 }
223}
224
225static void
226gtk_search_bar_get_property (GObject *object,
227 guint prop_id,
228 GValue *value,
229 GParamSpec *pspec)
230{
231 GtkSearchBar *bar = GTK_SEARCH_BAR (object);
232
233 switch (prop_id)
234 {
235 case PROP_SEARCH_MODE_ENABLED:
236 g_value_set_boolean (value, v_boolean: gtk_search_bar_get_search_mode (bar));
237 break;
238 case PROP_SHOW_CLOSE_BUTTON:
239 g_value_set_boolean (value, v_boolean: gtk_search_bar_get_show_close_button (bar));
240 break;
241 case PROP_CHILD:
242 g_value_set_object (value, v_object: gtk_search_bar_get_child (bar));
243 break;
244 case PROP_KEY_CAPTURE_WIDGET:
245 g_value_set_object (value, v_object: gtk_search_bar_get_key_capture_widget (bar));
246 break;
247 default:
248 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
249 break;
250 }
251}
252
253static void gtk_search_bar_set_entry (GtkSearchBar *bar,
254 GtkEditable *editable);
255
256static void
257gtk_search_bar_dispose (GObject *object)
258{
259 GtkSearchBar *bar = GTK_SEARCH_BAR (object);
260
261 gtk_search_bar_set_key_capture_widget (bar, NULL);
262 gtk_search_bar_set_entry (bar, NULL);
263
264 g_clear_pointer (&bar->revealer, gtk_widget_unparent);
265
266 bar->child = NULL;
267 bar->box_center = NULL;
268 bar->close_button = NULL;
269
270 G_OBJECT_CLASS (gtk_search_bar_parent_class)->dispose (object);
271}
272
273static void
274gtk_search_bar_compute_expand (GtkWidget *widget,
275 gboolean *hexpand,
276 gboolean *vexpand)
277{
278 GtkSearchBar *bar = GTK_SEARCH_BAR (widget);
279
280 if (bar->child)
281 {
282 *hexpand = gtk_widget_compute_expand (widget: bar->child, orientation: GTK_ORIENTATION_HORIZONTAL);
283 *vexpand = gtk_widget_compute_expand (widget: bar->child, orientation: GTK_ORIENTATION_VERTICAL);
284 }
285 else
286 {
287 *hexpand = FALSE;
288 *vexpand = FALSE;
289 }
290}
291
292static void
293gtk_search_bar_class_init (GtkSearchBarClass *klass)
294{
295 GObjectClass *object_class = G_OBJECT_CLASS (klass);
296 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
297
298 object_class->dispose = gtk_search_bar_dispose;
299 object_class->set_property = gtk_search_bar_set_property;
300 object_class->get_property = gtk_search_bar_get_property;
301
302 widget_class->compute_expand = gtk_search_bar_compute_expand;
303 widget_class->focus = gtk_widget_focus_child;
304
305 /**
306 * GtkSearchBar:search-mode-enabled: (attributes org.gtk.Property.get=gtk_search_bar_get_search_mode org.gtk.Property.set=gtk_search_bar_set_search_mode)
307 *
308 * Whether the search mode is on and the search bar shown.
309 */
310 widget_props[PROP_SEARCH_MODE_ENABLED] = g_param_spec_boolean (name: "search-mode-enabled",
311 P_("Search Mode Enabled"),
312 P_("Whether the search mode is on and the search bar shown"),
313 FALSE,
314 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
315
316 /**
317 * GtkSearchBar:show-close-button: (attributes org.gtk.Property.get=gtk_search_bar_get_show_close_button org.gtk.Property.set=gtk_search_bar_set_show_close_button)
318 *
319 * Whether to show the close button in the search bar.
320 */
321 widget_props[PROP_SHOW_CLOSE_BUTTON] = g_param_spec_boolean (name: "show-close-button",
322 P_("Show Close Button"),
323 P_("Whether to show the close button in the toolbar"),
324 FALSE,
325 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY);
326
327 /**
328 * GtkSearchBar:child: (attributes org.gtk.Property.get=gtk_search_bar_get_child org.gtk.Property.set=gtk_search_bar_set_child)
329 *
330 * The child widget.
331 */
332 widget_props[PROP_CHILD] = g_param_spec_object (name: "child",
333 P_("Child"),
334 P_("The child widget"),
335 GTK_TYPE_WIDGET,
336 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY);
337
338 /**
339 * GtkSearchBar:key-capture-widget: (attributes org.gtk.Property.get=gtk_search_bar_get_key_capture_widget org.gtk.Property.set=gtk_search_bar_set_key_capture_widget)
340 *
341 * The key capture widget.
342 */
343 widget_props[PROP_KEY_CAPTURE_WIDGET]
344 = g_param_spec_object (name: "key-capture-widget",
345 P_("Key Capture Widget"),
346 P_("Key Capture Widget"),
347 GTK_TYPE_WIDGET,
348 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY);
349
350 g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROPERTY, pspecs: widget_props);
351
352 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
353 gtk_widget_class_set_css_name (widget_class, I_("searchbar"));
354 gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_SEARCH);
355}
356
357static void
358gtk_search_bar_init (GtkSearchBar *bar)
359{
360 bar->revealer = gtk_revealer_new ();
361 gtk_revealer_set_reveal_child (GTK_REVEALER (bar->revealer), FALSE);
362 gtk_widget_set_hexpand (widget: bar->revealer, TRUE);
363 gtk_widget_set_parent (widget: bar->revealer, GTK_WIDGET (bar));
364
365 bar->box_center = gtk_center_box_new ();
366 gtk_widget_set_hexpand (widget: bar->box_center, TRUE);
367
368 bar->close_button = gtk_button_new_from_icon_name (icon_name: "window-close-symbolic");
369 gtk_widget_set_valign (widget: bar->close_button, align: GTK_ALIGN_CENTER);
370 gtk_widget_add_css_class (widget: bar->close_button, css_class: "close");
371 gtk_center_box_set_end_widget (GTK_CENTER_BOX (bar->box_center), child: bar->close_button);
372 gtk_widget_hide (widget: bar->close_button);
373
374 gtk_revealer_set_child (GTK_REVEALER (bar->revealer), child: bar->box_center);
375
376 g_signal_connect (bar->revealer, "notify::reveal-child",
377 G_CALLBACK (reveal_child_changed_cb), bar);
378
379 g_signal_connect (bar->close_button, "clicked",
380 G_CALLBACK (close_button_clicked_cb), bar);
381}
382
383/**
384 * gtk_search_bar_new:
385 *
386 * Creates a `GtkSearchBar`.
387 *
388 * You will need to tell it about which widget is going to be your text
389 * entry using [method@Gtk.SearchBar.connect_entry].
390 *
391 * Returns: a new `GtkSearchBar`
392 */
393GtkWidget *
394gtk_search_bar_new (void)
395{
396 return g_object_new (GTK_TYPE_SEARCH_BAR, NULL);
397}
398
399static void
400gtk_search_bar_set_entry (GtkSearchBar *bar,
401 GtkEditable *entry)
402{
403 if (bar->entry != NULL)
404 {
405 if (GTK_IS_SEARCH_ENTRY (bar->entry))
406 {
407 gtk_search_entry_set_key_capture_widget (GTK_SEARCH_ENTRY (bar->entry), NULL);
408 g_signal_handlers_disconnect_by_func (bar->entry, stop_search_cb, bar);
409 }
410 g_object_remove_weak_pointer (G_OBJECT (bar->entry), weak_pointer_location: (gpointer *) &bar->entry);
411 }
412
413 bar->entry = GTK_WIDGET (entry);
414
415 if (bar->entry != NULL)
416 {
417 g_object_add_weak_pointer (G_OBJECT (bar->entry), weak_pointer_location: (gpointer *) &bar->entry);
418 if (GTK_IS_SEARCH_ENTRY (bar->entry))
419 {
420 g_signal_connect (bar->entry, "stop-search",
421 G_CALLBACK (stop_search_cb), bar);
422 gtk_search_entry_set_key_capture_widget (GTK_SEARCH_ENTRY (bar->entry),
423 GTK_WIDGET (bar));
424 }
425
426 }
427}
428
429/**
430 * gtk_search_bar_connect_entry:
431 * @bar: a `GtkSearchBar`
432 * @entry: a `GtkEditable`
433 *
434 * Connects the `GtkEditable widget passed as the one to be used in
435 * this search bar.
436 *
437 * The entry should be a descendant of the search bar. Calling this
438 * function manually is only required if the entry isn’t the direct
439 * child of the search bar (as in our main example).
440 */
441void
442gtk_search_bar_connect_entry (GtkSearchBar *bar,
443 GtkEditable *entry)
444{
445 g_return_if_fail (GTK_IS_SEARCH_BAR (bar));
446 g_return_if_fail (entry == NULL || GTK_IS_EDITABLE (entry));
447
448 gtk_search_bar_set_entry (bar, entry);
449}
450
451/**
452 * gtk_search_bar_get_search_mode: (attributes org.gtk.Method.get_property=search-mode-enabled)
453 * @bar: a `GtkSearchBar`
454 *
455 * Returns whether the search mode is on or off.
456 *
457 * Returns: whether search mode is toggled on
458 */
459gboolean
460gtk_search_bar_get_search_mode (GtkSearchBar *bar)
461{
462 g_return_val_if_fail (GTK_IS_SEARCH_BAR (bar), FALSE);
463
464 return bar->reveal_child;
465}
466
467/**
468 * gtk_search_bar_set_search_mode: (attributes org.gtk.Method.set_property=search-mode-enabled)
469 * @bar: a `GtkSearchBar`
470 * @search_mode: the new state of the search mode
471 *
472 * Switches the search mode on or off.
473 */
474void
475gtk_search_bar_set_search_mode (GtkSearchBar *bar,
476 gboolean search_mode)
477{
478 g_return_if_fail (GTK_IS_SEARCH_BAR (bar));
479
480 gtk_revealer_set_reveal_child (GTK_REVEALER (bar->revealer), reveal_child: search_mode);
481}
482
483/**
484 * gtk_search_bar_get_show_close_button: (attributes org.gtk.Method.get_property=show-close-button)
485 * @bar: a `GtkSearchBar`
486 *
487 * Returns whether the close button is shown.
488 *
489 * Returns: whether the close button is shown
490 */
491gboolean
492gtk_search_bar_get_show_close_button (GtkSearchBar *bar)
493{
494 g_return_val_if_fail (GTK_IS_SEARCH_BAR (bar), FALSE);
495
496 return gtk_widget_get_visible (widget: bar->close_button);
497}
498
499/**
500 * gtk_search_bar_set_show_close_button: (attributes org.gtk.Method.set_property=show-close-button)
501 * @bar: a `GtkSearchBar`
502 * @visible: whether the close button will be shown or not
503 *
504 * Shows or hides the close button.
505 *
506 * Applications that already have a “search” toggle button should not
507 * show a close button in their search bar, as it duplicates the role
508 * of the toggle button.
509 */
510void
511gtk_search_bar_set_show_close_button (GtkSearchBar *bar,
512 gboolean visible)
513{
514 g_return_if_fail (GTK_IS_SEARCH_BAR (bar));
515
516 visible = visible != FALSE;
517
518 if (gtk_widget_get_visible (widget: bar->close_button) != visible)
519 {
520 gtk_widget_set_visible (widget: bar->close_button, visible);
521 g_object_notify_by_pspec (G_OBJECT (bar), pspec: widget_props[PROP_SHOW_CLOSE_BUTTON]);
522 }
523}
524
525static void
526changed_cb (gboolean *changed)
527{
528 *changed = TRUE;
529}
530
531static gboolean
532capture_widget_key_handled (GtkEventControllerKey *controller,
533 guint keyval,
534 guint keycode,
535 GdkModifierType state,
536 GtkSearchBar *bar)
537{
538 gboolean handled;
539
540 if (!gtk_widget_get_mapped (GTK_WIDGET (bar)))
541 return GDK_EVENT_PROPAGATE;
542
543 if (bar->reveal_child)
544 return GDK_EVENT_PROPAGATE;
545
546 if (bar->entry == NULL)
547 {
548 g_warning ("The search bar does not have an entry connected to it. Call gtk_search_bar_connect_entry() to connect one.");
549 return GDK_EVENT_PROPAGATE;
550 }
551
552 if (GTK_IS_SEARCH_ENTRY (bar->entry))
553 {
554 /* The search entry was told to listen to events from the search bar, so
555 * just forward the event to self, so the search entry has an opportunity
556 * to intercept those.
557 */
558 handled = gtk_event_controller_key_forward (controller, GTK_WIDGET (bar));
559 }
560 else
561 {
562 gboolean preedit_changed, buffer_changed;
563 guint preedit_change_id, buffer_change_id;
564 gboolean res;
565
566 if (gtk_search_entry_is_keynav (keyval, state) ||
567 keyval == GDK_KEY_space ||
568 keyval == GDK_KEY_Menu)
569 return GDK_EVENT_PROPAGATE;
570
571 if (keyval == GDK_KEY_Escape)
572 {
573 if (gtk_revealer_get_reveal_child (GTK_REVEALER (bar->revealer)))
574 {
575 stop_search_cb (entry: bar->entry, bar);
576 return GDK_EVENT_STOP;
577 }
578
579 return GDK_EVENT_PROPAGATE;
580 }
581
582 handled = GDK_EVENT_PROPAGATE;
583 preedit_changed = buffer_changed = FALSE;
584 preedit_change_id = g_signal_connect_swapped (bar->entry, "preedit-changed",
585 G_CALLBACK (changed_cb), &preedit_changed);
586 buffer_change_id = g_signal_connect_swapped (bar->entry, "changed",
587 G_CALLBACK (changed_cb), &buffer_changed);
588
589 res = gtk_event_controller_key_forward (controller, widget: bar->entry);
590
591 g_signal_handler_disconnect (instance: bar->entry, handler_id: preedit_change_id);
592 g_signal_handler_disconnect (instance: bar->entry, handler_id: buffer_change_id);
593
594 if ((res && buffer_changed) || preedit_changed)
595 handled = GDK_EVENT_STOP;
596 }
597
598 if (handled == GDK_EVENT_STOP)
599 gtk_revealer_set_reveal_child (GTK_REVEALER (bar->revealer), TRUE);
600
601 return handled;
602}
603
604/**
605 * gtk_search_bar_set_key_capture_widget: (attributes org.gtk.Method.set_property=key-capture-widget)
606 * @bar: a `GtkSearchBar`
607 * @widget: (nullable) (transfer none): a `GtkWidget`
608 *
609 * Sets @widget as the widget that @bar will capture key events
610 * from.
611 *
612 * If key events are handled by the search bar, the bar will
613 * be shown, and the entry populated with the entered text.
614 *
615 * Note that despite the name of this function, the events
616 * are only 'captured' in the bubble phase, which means that
617 * editable child widgets of @widget will receive text input
618 * before it gets captured. If that is not desired, you can
619 * capture and forward the events yourself with
620 * [method@Gtk.EventControllerKey.forward].
621 */
622void
623gtk_search_bar_set_key_capture_widget (GtkSearchBar *bar,
624 GtkWidget *widget)
625{
626 g_return_if_fail (GTK_IS_SEARCH_BAR (bar));
627 g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
628
629 if (bar->capture_widget == widget)
630 return;
631
632 if (bar->capture_widget)
633 {
634 gtk_widget_remove_controller (widget: bar->capture_widget,
635 controller: bar->capture_widget_controller);
636 g_object_remove_weak_pointer (G_OBJECT (bar->capture_widget),
637 weak_pointer_location: (gpointer *) &bar->capture_widget);
638 }
639
640 bar->capture_widget = widget;
641
642 if (widget)
643 {
644 g_object_add_weak_pointer (G_OBJECT (bar->capture_widget),
645 weak_pointer_location: (gpointer *) &bar->capture_widget);
646
647 bar->capture_widget_controller = gtk_event_controller_key_new ();
648 gtk_event_controller_set_propagation_phase (controller: bar->capture_widget_controller,
649 phase: GTK_PHASE_BUBBLE);
650 g_signal_connect (bar->capture_widget_controller, "key-pressed",
651 G_CALLBACK (capture_widget_key_handled), bar);
652 g_signal_connect (bar->capture_widget_controller, "key-released",
653 G_CALLBACK (capture_widget_key_handled), bar);
654 gtk_widget_add_controller (widget, controller: bar->capture_widget_controller);
655 }
656
657 g_object_notify_by_pspec (G_OBJECT (bar), pspec: widget_props[PROP_KEY_CAPTURE_WIDGET]);
658}
659
660/**
661 * gtk_search_bar_get_key_capture_widget: (attributes org.gtk.Method.get_property=key-capture-widget)
662 * @bar: a `GtkSearchBar`
663 *
664 * Gets the widget that @bar is capturing key events from.
665 *
666 * Returns: (nullable) (transfer none): The key capture widget.
667 **/
668GtkWidget *
669gtk_search_bar_get_key_capture_widget (GtkSearchBar *bar)
670{
671 g_return_val_if_fail (GTK_IS_SEARCH_BAR (bar), NULL);
672
673 return bar->capture_widget;
674}
675
676/**
677 * gtk_search_bar_set_child: (attributes org.gtk.Method.set_property=child)
678 * @bar: a `GtkSearchBar`
679 * @child: (nullable): the child widget
680 *
681 * Sets the child widget of @bar.
682 */
683void
684gtk_search_bar_set_child (GtkSearchBar *bar,
685 GtkWidget *child)
686{
687 if (bar->child)
688 {
689 if (GTK_IS_EDITABLE (bar->child))
690 gtk_search_bar_connect_entry (bar, NULL);
691
692 gtk_center_box_set_center_widget (GTK_CENTER_BOX (bar->box_center), NULL);
693 }
694
695 bar->child = child;
696
697 if (bar->child)
698 {
699 gtk_center_box_set_center_widget (GTK_CENTER_BOX (bar->box_center), child);
700
701 if (GTK_IS_EDITABLE (child))
702 gtk_search_bar_connect_entry (bar, GTK_EDITABLE (child));
703 }
704
705 g_object_notify_by_pspec (G_OBJECT (bar), pspec: widget_props[PROP_CHILD]);
706}
707
708/**
709 * gtk_search_bar_get_child: (attributes org.gtk.Method.get_property=child)
710 * @bar: a `GtkSearchBar`
711 *
712 * Gets the child widget of @bar.
713 *
714 * Returns: (nullable) (transfer none): the child widget of @bar
715 */
716GtkWidget *
717gtk_search_bar_get_child (GtkSearchBar *bar)
718{
719 return bar->child;
720}
721

source code of gtk/gtk/gtksearchbar.c