suggestionentry: Add an optional button

This is useful for comboboxtext-like use cases.
To simulate comboboxtext behavior, set
use-filter = FALSE
show-button = TRUE
insert-selection = TRUE
This commit is contained in:
Matthias Clasen
2020-06-26 17:41:33 -04:00
parent 641c068744
commit f9e1e7bd1a
3 changed files with 144 additions and 18 deletions

View File

@@ -7641,4 +7641,6 @@ gtk_suggestion_entry_set_insert_selection
gtk_suggestion_entry_get_insert_selection
gtk_suggestion_entry_set_insert_prefix
gtk_suggestion_entry_get_insert_prefix
gtk_suggestion_entry_set_show_button
gtk_suggestion_entry_get_show_button
</SECTION>

View File

@@ -44,6 +44,9 @@
#include "gtkscrolledwindow.h"
#include "gtkeventcontrollerkey.h"
#include "gtkeventcontrollerfocus.h"
#include "gtkbox.h"
#include "gtkgizmoprivate.h"
#include "gtkactionable.h"
/**
@@ -75,14 +78,19 @@
* # CSS Nodes
*
* |[<!-- language="plain" -->
* entry.suggestion
* ── text
* ╰── popover
* widget
* ── box
* ├── entry.suggestion
* │ ├── text
* │ ╰── popover
* ╰── [button]
* ]|
*
* GtkSuggestionEntry has a single CSS node with name entry that carries
* a .sugggestion style class, and the text and popover nodes are children
* of that.
* of that. The parent of the entry node is a box node, which also contains
* the CSS node for the button (which may be hidden). The parent of the box
* node is a widget node.
*/
struct _GtkSuggestionEntry
@@ -96,6 +104,9 @@ struct _GtkSuggestionEntry
GtkFilterListModel *filter_model;
GtkSingleSelection *selection;
GtkWidget *box;
GtkWidget *gizmo;
GtkWidget *button;
GtkWidget *entry;
GtkWidget *popup;
GtkWidget *list;
@@ -107,6 +118,7 @@ struct _GtkSuggestionEntry
guint use_filter : 1;
guint insert_selection : 1;
guint insert_prefix : 1;
guint show_button : 1;
};
typedef struct _GtkSuggestionEntryClass GtkSuggestionEntryClass;
@@ -127,6 +139,7 @@ enum
PROP_USE_FILTER,
PROP_INSERT_PREFIX,
PROP_INSERT_SELECTION,
PROP_SHOW_BUTTON,
N_PROPERTIES,
};
@@ -162,8 +175,7 @@ gtk_suggestion_entry_dispose (GObject *object)
g_signal_handler_disconnect (self->entry, self->changed_id);
self->changed_id = 0;
}
g_clear_pointer (&self->popup, gtk_widget_unparent);
g_clear_pointer (&self->entry, gtk_widget_unparent);
g_clear_pointer (&self->box, gtk_widget_unparent);
g_clear_pointer (&self->expression, gtk_expression_unref);
g_clear_object (&self->factory);
@@ -207,7 +219,7 @@ gtk_suggestion_entry_get_property (GObject *object,
break;
case PROP_POPUP_VISIBLE:
g_value_set_boolean (value, gtk_widget_get_visible (self->popup));
g_value_set_boolean (value, self->popup && gtk_widget_get_visible (self->popup));
break;
case PROP_USE_FILTER:
@@ -222,6 +234,10 @@ gtk_suggestion_entry_get_property (GObject *object,
g_value_set_boolean (value, gtk_suggestion_entry_get_insert_prefix (self));
break;
case PROP_SHOW_BUTTON:
g_value_set_boolean (value, gtk_suggestion_entry_get_show_button (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -273,6 +289,10 @@ gtk_suggestion_entry_set_property (GObject *object,
gtk_suggestion_entry_set_insert_prefix (self, g_value_get_boolean (value));
break;
case PROP_SHOW_BUTTON:
gtk_suggestion_entry_set_show_button (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -290,7 +310,7 @@ gtk_suggestion_entry_measure (GtkWidget *widget,
{
GtkSuggestionEntry *self = GTK_SUGGESTION_ENTRY (widget);
gtk_widget_measure (self->entry,
gtk_widget_measure (self->box,
orientation,
size,
minimum, natural,
@@ -305,11 +325,8 @@ gtk_suggestion_entry_size_allocate (GtkWidget *widget,
{
GtkSuggestionEntry *self = GTK_SUGGESTION_ENTRY (widget);
gtk_widget_size_allocate (self->entry, &(GtkAllocation) { 0, 0, width, height }, baseline);
gtk_widget_size_allocate (self->box, &(GtkAllocation) { 0, 0, width, height }, baseline);
gtk_widget_set_size_request (self->popup, gtk_widget_get_allocated_width (widget), -1);
gtk_native_check_resize (GTK_NATIVE (self->popup));
}
static gboolean
@@ -431,11 +448,16 @@ gtk_suggestion_entry_class_init (GtkSuggestionEntryClass *klass)
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_SHOW_BUTTON] =
g_param_spec_boolean ("show-button",
P_("Show button"),
P_("Whether to show a button for presenting the popup"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPERTIES, properties);
gtk_editable_install_properties (object_class, N_PROPERTIES);
gtk_widget_class_set_css_name (widget_class, I_("entry"));
/**
* GtkSuggestionEntry|popup.show:
*
@@ -775,6 +797,46 @@ set_default_factory (GtkSuggestionEntry *self)
g_object_unref (factory);
}
static void
measure_entry (GtkGizmo *gizmo,
GtkOrientation orientation,
gint size,
gint *minimum,
gint *natural,
gint *minimum_baseline,
gint *natural_baseline)
{
gtk_widget_measure (gtk_widget_get_first_child (GTK_WIDGET (gizmo)),
orientation, size,
minimum, natural,
minimum_baseline, natural_baseline);
}
static void
allocate_entry (GtkGizmo *gizmo,
int width,
int height,
int baseline)
{
GtkSuggestionEntry *self;
self = GTK_SUGGESTION_ENTRY (gtk_widget_get_parent (gtk_widget_get_parent (GTK_WIDGET (gizmo))));
gtk_widget_size_allocate (gtk_widget_get_first_child (GTK_WIDGET (gizmo)),
&(GtkAllocation){ 0, 0, width, height },
baseline);
gtk_widget_set_size_request (self->popup, gtk_widget_get_allocated_width (GTK_WIDGET (gizmo)), -1);
gtk_native_check_resize (GTK_NATIVE (self->popup));
}
static gboolean
grab_focus_entry (GtkGizmo *gizmo)
{
return gtk_widget_grab_focus (gtk_widget_get_first_child (GTK_WIDGET (gizmo)));
}
static void
gtk_suggestion_entry_init (GtkSuggestionEntry *self)
{
@@ -783,19 +845,38 @@ gtk_suggestion_entry_init (GtkSuggestionEntry *self)
self->use_filter = TRUE;
self->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_add_css_class (self->box, "linked");
gtk_widget_set_hexpand (self->box, TRUE);
gtk_widget_set_parent (self->box, GTK_WIDGET (self));
self->gizmo = gtk_gizmo_new ("entry", measure_entry, allocate_entry, NULL, NULL,
(GtkGizmoFocusFunc)gtk_widget_focus_child,
grab_focus_entry);
gtk_widget_add_css_class (self->gizmo, "suggestion");
gtk_widget_set_hexpand (self->gizmo, TRUE);
gtk_box_append (GTK_BOX (self->box), self->gizmo);
self->entry = gtk_text_new ();
gtk_widget_set_parent (self->entry, GTK_WIDGET (self));
gtk_widget_set_parent (self->entry, self->gizmo);
gtk_widget_set_hexpand (self->entry, TRUE);
gtk_editable_init_delegate (GTK_EDITABLE (self));
self->changed_id = g_signal_connect (self->entry, "notify::text", G_CALLBACK (text_changed), self);
self->button = gtk_toggle_button_new ();
gtk_button_set_icon_name (GTK_BUTTON (self->button), "pan-down-symbolic");
gtk_widget_set_focus_on_click (self->button, FALSE);
gtk_actionable_set_action_name (GTK_ACTIONABLE (self->button), "popup.show");
gtk_box_append (GTK_BOX (self->box), self->button);
gtk_widget_hide (self->button);
self->popup = gtk_popover_new ();
gtk_popover_set_position (GTK_POPOVER (self->popup), GTK_POS_BOTTOM);
gtk_popover_set_autohide (GTK_POPOVER (self->popup), FALSE);
gtk_popover_set_has_arrow (GTK_POPOVER (self->popup), FALSE);
gtk_widget_set_halign (self->popup, GTK_ALIGN_START);
gtk_widget_add_css_class (self->popup, "menu");
gtk_widget_set_parent (self->popup, GTK_WIDGET (self));
gtk_widget_set_parent (self->popup, self->gizmo);
sw = gtk_scrolled_window_new ();
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_NEVER,
@@ -812,8 +893,6 @@ gtk_suggestion_entry_init (GtkSuggestionEntry *self)
set_default_factory (self);
gtk_widget_add_css_class (GTK_WIDGET (self), I_("suggestion"));
controller = gtk_event_controller_key_new ();
gtk_event_controller_set_name (controller, "gtk-suggestion-entry");
g_signal_connect (controller, "key-pressed",
@@ -1383,3 +1462,42 @@ gtk_suggestion_entry_get_insert_prefix (GtkSuggestionEntry *self)
return self->insert_prefix;
}
/**
* gtk_suggestion_entry_set_show_button:
* @self: a #GtkSuggestionEntry
* @show_button: %TRUE to show a button
*
* Sets whether the GtkSuggestionEntry should show a button
* for opening the popup with suggestions.
*/
void
gtk_suggestion_entry_set_show_button (GtkSuggestionEntry *self,
gboolean show_button)
{
g_return_if_fail (GTK_IS_SUGGESTION_ENTRY (self));
if (self->show_button == show_button)
return;
if (self->button)
gtk_widget_set_visible (self->button, show_button);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_BUTTON]);
}
/**
* gtk_suggestion_entry_get_show_button:
* @self: a #GtkSuggestionEntry
*
* Gets the value set by gtk_suggestion_entry_set_show_button().
*
* Returns: %TRUE if @self is showing a button for suggestions
*/
gboolean
gtk_suggestion_entry_get_show_button (GtkSuggestionEntry *self)
{
g_return_val_if_fail (GTK_IS_SUGGESTION_ENTRY (self), FALSE);
return self->show_button;
}

View File

@@ -84,6 +84,12 @@ void gtk_suggestion_entry_set_insert_prefix (GtkSuggestionEntry *
GDK_AVAILABLE_IN_ALL
gboolean gtk_suggestion_entry_get_insert_prefix (GtkSuggestionEntry *self);
GDK_AVAILABLE_IN_ALL
void gtk_suggestion_entry_set_show_button (GtkSuggestionEntry *self,
gboolean show_button);
GDK_AVAILABLE_IN_ALL
gboolean gtk_suggestion_entry_get_show_button (GtkSuggestionEntry *self);
G_END_DECLS
#endif /* __GTK_SUGGESTION_ENTRY_H__ */