1 | /* Entry/Search Entry |
2 | * |
3 | * GtkEntry allows to display icons and progress information. |
4 | * This demo shows how to use these features in a search entry. |
5 | */ |
6 | |
7 | #include <glib/gi18n.h> |
8 | #include <gtk/gtk.h> |
9 | |
10 | static GtkWidget *window = NULL; |
11 | static GtkWidget *notebook = NULL; |
12 | static GSimpleActionGroup *actions = NULL; |
13 | |
14 | static guint search_progress_id = 0; |
15 | static guint finish_search_id = 0; |
16 | |
17 | static void |
18 | show_find_button (void) |
19 | { |
20 | gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), page_num: 0); |
21 | } |
22 | |
23 | static void |
24 | show_cancel_button (void) |
25 | { |
26 | gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), page_num: 1); |
27 | } |
28 | |
29 | static gboolean |
30 | search_progress (gpointer data) |
31 | { |
32 | gtk_entry_progress_pulse (GTK_ENTRY (data)); |
33 | return G_SOURCE_CONTINUE; |
34 | } |
35 | |
36 | static void |
37 | search_progress_done (GtkEntry *entry) |
38 | { |
39 | gtk_entry_set_progress_fraction (entry, fraction: 0.0); |
40 | g_object_unref (object: entry); |
41 | } |
42 | |
43 | static gboolean |
44 | finish_search (GtkButton *button) |
45 | { |
46 | show_find_button (); |
47 | if (search_progress_id) |
48 | { |
49 | g_source_remove (tag: search_progress_id); |
50 | search_progress_id = 0; |
51 | } |
52 | return G_SOURCE_REMOVE; |
53 | } |
54 | |
55 | static gboolean |
56 | start_search_feedback (gpointer data) |
57 | { |
58 | gtk_entry_set_progress_fraction (GTK_ENTRY (data), fraction: 0.1); |
59 | search_progress_id = g_timeout_add_full (G_PRIORITY_DEFAULT, interval: 100, |
60 | function: (GSourceFunc)search_progress, g_object_ref (data), |
61 | notify: (GDestroyNotify)search_progress_done); |
62 | |
63 | return G_SOURCE_REMOVE; |
64 | } |
65 | |
66 | static void |
67 | start_search (GtkButton *button, |
68 | GtkEntry *entry) |
69 | { |
70 | show_cancel_button (); |
71 | search_progress_id = g_timeout_add_seconds (interval: 1, function: (GSourceFunc)start_search_feedback, data: entry); |
72 | finish_search_id = g_timeout_add_seconds (interval: 15, function: (GSourceFunc)finish_search, data: button); |
73 | } |
74 | |
75 | |
76 | static void |
77 | stop_search (GtkButton *button, |
78 | gpointer data) |
79 | { |
80 | if (finish_search_id) |
81 | { |
82 | g_source_remove (tag: finish_search_id); |
83 | finish_search_id = 0; |
84 | } |
85 | finish_search (button); |
86 | } |
87 | |
88 | static void |
89 | clear_entry (GSimpleAction *action, |
90 | GVariant *parameter, |
91 | gpointer user_data) |
92 | { |
93 | GtkEditable *editable = user_data; |
94 | |
95 | gtk_editable_set_text (editable, text: "" ); |
96 | } |
97 | |
98 | static void |
99 | set_search_by (GSimpleAction *action, |
100 | GVariant *value, |
101 | gpointer user_data) |
102 | { |
103 | GtkEntry *entry = user_data; |
104 | const char *term; |
105 | |
106 | term = g_variant_get_string (value, NULL); |
107 | |
108 | g_simple_action_set_state (simple: action, value); |
109 | |
110 | if (g_str_equal (v1: term, v2: "name" )) |
111 | { |
112 | gtk_entry_set_icon_tooltip_text (entry, icon_pos: GTK_ENTRY_ICON_PRIMARY, tooltip: "Search by name" ); |
113 | gtk_entry_set_placeholder_text (entry, text: "Name…" ); |
114 | } |
115 | else if (g_str_equal (v1: term, v2: "description" )) |
116 | { |
117 | gtk_entry_set_icon_tooltip_text (entry, icon_pos: GTK_ENTRY_ICON_PRIMARY, tooltip: "Search by description" ); |
118 | gtk_entry_set_placeholder_text (entry, text: "Description…" ); |
119 | } |
120 | else if (g_str_equal (v1: term, v2: "filename" )) |
121 | { |
122 | gtk_entry_set_icon_tooltip_text (entry, icon_pos: GTK_ENTRY_ICON_PRIMARY, tooltip: "Search by file name" ); |
123 | gtk_entry_set_placeholder_text (entry, text: "File name…" ); |
124 | } |
125 | } |
126 | |
127 | static void |
128 | icon_press_cb (GtkEntry *entry, |
129 | int position, |
130 | gpointer data) |
131 | { |
132 | if (position == GTK_ENTRY_ICON_PRIMARY) |
133 | { |
134 | GAction *action; |
135 | GVariant *state; |
136 | GVariant *new_state; |
137 | const char *s; |
138 | |
139 | action = g_action_map_lookup_action (G_ACTION_MAP (actions), action_name: "search-by" ); |
140 | state = g_action_get_state (action); |
141 | s = g_variant_get_string (value: state, NULL); |
142 | |
143 | if (g_str_equal (v1: s, v2: "name" )) |
144 | new_state = g_variant_new_string (string: "description" ); |
145 | else if (g_str_equal (v1: s, v2: "description" )) |
146 | new_state = g_variant_new_string (string: "filename" ); |
147 | else if (g_str_equal (v1: s, v2: "filename" )) |
148 | new_state = g_variant_new_string (string: "name" ); |
149 | else |
150 | g_assert_not_reached (); |
151 | |
152 | g_action_change_state (action, value: new_state); |
153 | g_variant_unref (value: state); |
154 | } |
155 | } |
156 | |
157 | static void |
158 | activate_cb (GtkEntry *entry, |
159 | GtkButton *button) |
160 | { |
161 | if (search_progress_id != 0) |
162 | return; |
163 | |
164 | start_search (button, entry); |
165 | } |
166 | |
167 | static void |
168 | search_entry_destroyed (gpointer data, |
169 | GObject *widget) |
170 | { |
171 | if (finish_search_id != 0) |
172 | { |
173 | g_source_remove (tag: finish_search_id); |
174 | finish_search_id = 0; |
175 | } |
176 | |
177 | if (search_progress_id != 0) |
178 | { |
179 | g_source_remove (tag: search_progress_id); |
180 | search_progress_id = 0; |
181 | } |
182 | |
183 | window = NULL; |
184 | } |
185 | |
186 | static void |
187 | text_changed (GObject *object, |
188 | GParamSpec *pspec, |
189 | gpointer data) |
190 | { |
191 | GtkEntry *entry = GTK_ENTRY (object); |
192 | GActionMap *action_map = data; |
193 | GAction *action; |
194 | gboolean has_text; |
195 | |
196 | has_text = gtk_entry_get_text_length (entry) > 0; |
197 | |
198 | action = g_action_map_lookup_action (action_map, action_name: "clear" ); |
199 | g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled: has_text); |
200 | } |
201 | |
202 | static GMenuModel * |
203 | (void) |
204 | { |
205 | GMenu * = g_menu_new (); |
206 | g_menu_append (menu, _("Name" ), detailed_action: "search.search-by::name" ); |
207 | g_menu_append (menu, _("Description" ), detailed_action: "search.search-by::description" ); |
208 | g_menu_append (menu, _("File Name" ), detailed_action: "search.search-by::filename" ); |
209 | |
210 | return G_MENU_MODEL (menu); |
211 | } |
212 | |
213 | static void |
214 | (GtkEntry *entry) |
215 | { |
216 | GMenu *; |
217 | GActionEntry entries[] = { |
218 | { "clear" , clear_entry, NULL, NULL, NULL }, |
219 | { "search-by" , NULL, "s" , "'name'" , set_search_by } |
220 | }; |
221 | GMenuModel *; |
222 | GMenuItem *item; |
223 | GAction *action; |
224 | GVariant *value; |
225 | |
226 | actions = g_simple_action_group_new (); |
227 | g_action_map_add_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS(entries), user_data: entry); |
228 | gtk_widget_insert_action_group (GTK_WIDGET (entry), name: "search" , G_ACTION_GROUP (actions)); |
229 | |
230 | action = g_action_map_lookup_action (G_ACTION_MAP (actions), action_name: "search-by" ); |
231 | value = g_variant_ref_sink (value: g_variant_new_string (string: "name" )); |
232 | set_search_by (G_SIMPLE_ACTION (action), value, user_data: entry); |
233 | g_variant_unref (value); |
234 | |
235 | menu = g_menu_new (); |
236 | item = g_menu_item_new (_("C_lear" ), detailed_action: "search.clear" ); |
237 | g_menu_item_set_attribute (menu_item: item, attribute: "touch-icon" , format_string: "s" , "edit-clear-symbolic" ); |
238 | g_menu_append_item (G_MENU (menu), item); |
239 | g_object_unref (object: item); |
240 | |
241 | submenu = create_search_menu_model (); |
242 | g_menu_append_submenu (menu, _("Search By" ), submenu); |
243 | g_object_unref (object: submenu); |
244 | |
245 | gtk_entry_set_extra_menu (entry, G_MENU_MODEL (menu)); |
246 | |
247 | g_object_unref (object: menu); |
248 | |
249 | g_signal_connect (entry, "notify::text" , G_CALLBACK (text_changed), actions); |
250 | } |
251 | |
252 | GtkWidget * |
253 | do_search_entry (GtkWidget *do_widget) |
254 | { |
255 | GtkWidget *vbox; |
256 | GtkWidget *hbox; |
257 | GtkWidget *entry; |
258 | GtkWidget *find_button; |
259 | GtkWidget *cancel_button; |
260 | |
261 | if (!window) |
262 | { |
263 | window = gtk_window_new (); |
264 | gtk_window_set_display (GTK_WINDOW (window), display: gtk_widget_get_display (widget: do_widget)); |
265 | gtk_window_set_title (GTK_WINDOW (window), title: "Search Entry" ); |
266 | gtk_window_set_resizable (GTK_WINDOW (window), FALSE); |
267 | |
268 | g_object_weak_ref (G_OBJECT (window), notify: search_entry_destroyed, data: &window); |
269 | |
270 | vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 12); |
271 | gtk_widget_set_margin_start (widget: vbox, margin: 18); |
272 | gtk_widget_set_margin_end (widget: vbox, margin: 18); |
273 | gtk_widget_set_margin_top (widget: vbox, margin: 18); |
274 | gtk_widget_set_margin_bottom (widget: vbox, margin: 18); |
275 | gtk_window_set_child (GTK_WINDOW (window), child: vbox); |
276 | |
277 | hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 12); |
278 | gtk_box_append (GTK_BOX (vbox), child: hbox); |
279 | |
280 | /* Create our entry */ |
281 | entry = gtk_entry_new (); |
282 | gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry), |
283 | icon_pos: GTK_ENTRY_ICON_PRIMARY, |
284 | icon_name: "edit-find-symbolic" ); |
285 | gtk_box_append (GTK_BOX (hbox), child: entry); |
286 | |
287 | /* Create the find and cancel buttons */ |
288 | notebook = gtk_notebook_new (); |
289 | gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE); |
290 | gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE); |
291 | gtk_box_append (GTK_BOX (hbox), child: notebook); |
292 | |
293 | find_button = gtk_button_new_with_label (label: "Find" ); |
294 | g_signal_connect (find_button, "clicked" , |
295 | G_CALLBACK (start_search), entry); |
296 | gtk_notebook_append_page (GTK_NOTEBOOK (notebook), child: find_button, NULL); |
297 | gtk_widget_show (widget: find_button); |
298 | |
299 | cancel_button = gtk_button_new_with_label (label: "Cancel" ); |
300 | g_signal_connect (cancel_button, "clicked" , |
301 | G_CALLBACK (stop_search), NULL); |
302 | gtk_notebook_append_page (GTK_NOTEBOOK (notebook), child: cancel_button, NULL); |
303 | gtk_widget_show (widget: cancel_button); |
304 | |
305 | /* Set up the search icon */ |
306 | gtk_entry_set_icon_activatable (GTK_ENTRY (entry), icon_pos: GTK_ENTRY_ICON_PRIMARY, TRUE); |
307 | gtk_entry_set_icon_sensitive (GTK_ENTRY (entry), icon_pos: GTK_ENTRY_ICON_PRIMARY, TRUE); |
308 | |
309 | g_signal_connect (entry, "icon-press" , G_CALLBACK (icon_press_cb), NULL); |
310 | g_signal_connect (entry, "activate" , G_CALLBACK (activate_cb), NULL); |
311 | |
312 | /* add accessible alternatives for icon functionality */ |
313 | entry_add_to_context_menu (GTK_ENTRY (entry)); |
314 | } |
315 | |
316 | if (!gtk_widget_get_visible (widget: window)) |
317 | gtk_widget_show (widget: window); |
318 | else |
319 | { |
320 | g_clear_object (&actions); |
321 | gtk_window_destroy (GTK_WINDOW (window)); |
322 | } |
323 | |
324 | return window; |
325 | } |
326 | |