mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-25 21:21:21 +00:00
f7ab6f665d
A dropdown without a model is useless, so accept a model and expression in the constructor. Allow them to be NULL, but consume them if given. This makes chained constructors convenient without breaking language bindings. Drop gtk_drop_down_set_from_strings() and instead add gtk_drop_down_new_from_strings(). Update all users.
570 lines
17 KiB
C
570 lines
17 KiB
C
/* simple.c
|
|
* Copyright (C) 2017 Red Hat, Inc
|
|
* Author: Benjamin Otte
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
#define STRING_TYPE_HOLDER (string_holder_get_type ())
|
|
G_DECLARE_FINAL_TYPE (StringHolder, string_holder, STRING, HOLDER, GObject)
|
|
|
|
struct _StringHolder {
|
|
GObject parent_instance;
|
|
char *title;
|
|
char *icon;
|
|
char *description;
|
|
};
|
|
|
|
G_DEFINE_TYPE (StringHolder, string_holder, G_TYPE_OBJECT);
|
|
|
|
static void
|
|
string_holder_init (StringHolder *holder)
|
|
{
|
|
}
|
|
|
|
static void
|
|
string_holder_finalize (GObject *object)
|
|
{
|
|
StringHolder *holder = STRING_HOLDER (object);
|
|
|
|
g_free (holder->title);
|
|
g_free (holder->icon);
|
|
g_free (holder->description);
|
|
|
|
G_OBJECT_CLASS (string_holder_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
string_holder_class_init (StringHolderClass *class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
object_class->finalize = string_holder_finalize;
|
|
}
|
|
|
|
static StringHolder *
|
|
string_holder_new (const char *title, const char *icon, const char *description)
|
|
{
|
|
StringHolder *holder = g_object_new (STRING_TYPE_HOLDER, NULL);
|
|
holder->title = g_strdup (title);
|
|
holder->icon = g_strdup (icon);
|
|
holder->description = g_strdup (description);
|
|
return holder;
|
|
}
|
|
|
|
static void
|
|
strings_setup_item_single_line (GtkSignalListItemFactory *factory,
|
|
GtkListItem *item)
|
|
{
|
|
GtkWidget *box, *image, *title;
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
|
|
|
|
image = gtk_image_new ();
|
|
title = gtk_label_new ("");
|
|
gtk_label_set_xalign (GTK_LABEL (title), 0.0);
|
|
|
|
gtk_box_append (GTK_BOX (box), image);
|
|
gtk_box_append (GTK_BOX (box), title);
|
|
|
|
g_object_set_data (G_OBJECT (item), "title", title);
|
|
g_object_set_data (G_OBJECT (item), "image", image);
|
|
|
|
gtk_list_item_set_child (item, box);
|
|
}
|
|
|
|
static void
|
|
strings_setup_item_full (GtkSignalListItemFactory *factory,
|
|
GtkListItem *item)
|
|
{
|
|
GtkWidget *box, *box2, *image, *title, *description;
|
|
|
|
image = gtk_image_new ();
|
|
title = gtk_label_new ("");
|
|
gtk_label_set_xalign (GTK_LABEL (title), 0.0);
|
|
description = gtk_label_new ("");
|
|
gtk_label_set_xalign (GTK_LABEL (description), 0.0);
|
|
gtk_widget_add_css_class (description, "dim-label");
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
|
|
box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
|
|
|
|
gtk_box_append (GTK_BOX (box), image);
|
|
gtk_box_append (GTK_BOX (box), box2);
|
|
gtk_box_append (GTK_BOX (box2), title);
|
|
gtk_box_append (GTK_BOX (box2), description);
|
|
|
|
g_object_set_data (G_OBJECT (item), "title", title);
|
|
g_object_set_data (G_OBJECT (item), "image", image);
|
|
g_object_set_data (G_OBJECT (item), "description", description);
|
|
|
|
gtk_list_item_set_child (item, box);
|
|
}
|
|
|
|
static void
|
|
strings_bind_item (GtkSignalListItemFactory *factory,
|
|
GtkListItem *item)
|
|
{
|
|
GtkWidget *image, *title, *description;
|
|
StringHolder *holder;
|
|
|
|
holder = gtk_list_item_get_item (item);
|
|
|
|
title = g_object_get_data (G_OBJECT (item), "title");
|
|
image = g_object_get_data (G_OBJECT (item), "image");
|
|
description = g_object_get_data (G_OBJECT (item), "description");
|
|
|
|
gtk_label_set_label (GTK_LABEL (title), holder->title);
|
|
if (image)
|
|
{
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (image), holder->icon);
|
|
gtk_widget_set_visible (image, holder->icon != NULL);
|
|
}
|
|
if (description)
|
|
{
|
|
gtk_label_set_label (GTK_LABEL (description), holder->description);
|
|
gtk_widget_set_visible (description , holder->description != NULL);
|
|
}
|
|
}
|
|
|
|
static GtkListItemFactory *
|
|
strings_factory_new (gboolean full)
|
|
{
|
|
GtkListItemFactory *factory;
|
|
|
|
factory = gtk_signal_list_item_factory_new ();
|
|
if (full)
|
|
g_signal_connect (factory, "setup", G_CALLBACK (strings_setup_item_full), NULL);
|
|
else
|
|
g_signal_connect (factory, "setup", G_CALLBACK (strings_setup_item_single_line), NULL);
|
|
g_signal_connect (factory, "bind", G_CALLBACK (strings_bind_item), NULL);
|
|
|
|
return factory;
|
|
}
|
|
|
|
static GListModel *
|
|
strings_model_new (const char *const *titles,
|
|
const char *const *icons,
|
|
const char *const *descriptions)
|
|
{
|
|
GListStore *store;
|
|
int i;
|
|
|
|
store = g_list_store_new (STRING_TYPE_HOLDER);
|
|
for (i = 0; titles[i]; i++)
|
|
{
|
|
StringHolder *holder = string_holder_new (titles[i],
|
|
icons ? icons[i] : NULL,
|
|
descriptions ? descriptions[i] : NULL);
|
|
g_list_store_append (store, holder);
|
|
g_object_unref (holder);
|
|
}
|
|
|
|
return G_LIST_MODEL (store);
|
|
}
|
|
|
|
static GtkWidget *
|
|
drop_down_new_from_strings (const char *const *titles,
|
|
const char *const *icons,
|
|
const char *const *descriptions)
|
|
{
|
|
GtkWidget *widget;
|
|
GListModel *model;
|
|
GtkListItemFactory *factory;
|
|
GtkListItemFactory *list_factory;
|
|
|
|
g_return_val_if_fail (titles != NULL, NULL);
|
|
g_return_val_if_fail (icons == NULL || g_strv_length ((char **)icons) == g_strv_length ((char **)titles), NULL);
|
|
g_return_val_if_fail (descriptions == NULL || g_strv_length ((char **)icons) == g_strv_length ((char **)descriptions), NULL);
|
|
|
|
model = strings_model_new (titles, icons, descriptions);
|
|
factory = strings_factory_new (FALSE);
|
|
if (icons != NULL || descriptions != NULL)
|
|
list_factory = strings_factory_new (TRUE);
|
|
else
|
|
list_factory = NULL;
|
|
|
|
widget = g_object_new (GTK_TYPE_DROP_DOWN,
|
|
"model", model,
|
|
"factory", factory,
|
|
"list-factory", list_factory,
|
|
NULL);
|
|
|
|
g_object_unref (model);
|
|
g_object_unref (factory);
|
|
if (list_factory)
|
|
g_object_unref (list_factory);
|
|
|
|
return widget;
|
|
}
|
|
|
|
static char *
|
|
get_family_name (gpointer item)
|
|
{
|
|
return g_strdup (pango_font_family_get_name (PANGO_FONT_FAMILY (item)));
|
|
}
|
|
|
|
static char *
|
|
get_title (gpointer item)
|
|
{
|
|
return g_strdup (STRING_HOLDER (item)->title);
|
|
}
|
|
|
|
static gboolean
|
|
quit_cb (GtkWindow *window,
|
|
gpointer data)
|
|
{
|
|
*((gboolean*)data) = TRUE;
|
|
|
|
g_main_context_wakeup (NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define GTK_TYPE_STRING_PAIR (gtk_string_pair_get_type ())
|
|
G_DECLARE_FINAL_TYPE (GtkStringPair, gtk_string_pair, GTK, STRING_PAIR, GObject)
|
|
|
|
struct _GtkStringPair {
|
|
GObject parent_instance;
|
|
char *id;
|
|
char *string;
|
|
};
|
|
|
|
enum {
|
|
PROP_ID = 1,
|
|
PROP_STRING,
|
|
PROP_NUM_PROPERTIES
|
|
};
|
|
|
|
G_DEFINE_TYPE (GtkStringPair, gtk_string_pair, G_TYPE_OBJECT);
|
|
|
|
static void
|
|
gtk_string_pair_init (GtkStringPair *pair)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gtk_string_pair_finalize (GObject *object)
|
|
{
|
|
GtkStringPair *pair = GTK_STRING_PAIR (object);
|
|
|
|
g_free (pair->id);
|
|
g_free (pair->string);
|
|
|
|
G_OBJECT_CLASS (gtk_string_pair_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gtk_string_pair_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkStringPair *pair = GTK_STRING_PAIR (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_STRING:
|
|
g_free (pair->string);
|
|
pair->string = g_value_dup_string (value);
|
|
break;
|
|
|
|
case PROP_ID:
|
|
g_free (pair->id);
|
|
pair->id = g_value_dup_string (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_string_pair_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkStringPair *pair = GTK_STRING_PAIR (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_STRING:
|
|
g_value_set_string (value, pair->string);
|
|
break;
|
|
|
|
case PROP_ID:
|
|
g_value_set_string (value, pair->id);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_string_pair_class_init (GtkStringPairClass *class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
GParamSpec *pspec;
|
|
|
|
object_class->finalize = gtk_string_pair_finalize;
|
|
object_class->set_property = gtk_string_pair_set_property;
|
|
object_class->get_property = gtk_string_pair_get_property;
|
|
|
|
pspec = g_param_spec_string ("string", "String", "String",
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_property (object_class, PROP_STRING, pspec);
|
|
|
|
pspec = g_param_spec_string ("id", "ID", "ID",
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_property (object_class, PROP_ID, pspec);
|
|
}
|
|
|
|
static GtkStringPair *
|
|
gtk_string_pair_new (const char *id,
|
|
const char *string)
|
|
{
|
|
return g_object_new (GTK_TYPE_STRING_PAIR,
|
|
"id", id,
|
|
"string", string,
|
|
NULL);
|
|
}
|
|
|
|
static const char *
|
|
gtk_string_pair_get_string (GtkStringPair *pair)
|
|
{
|
|
return pair->string;
|
|
}
|
|
|
|
static const char *
|
|
gtk_string_pair_get_id (GtkStringPair *pair)
|
|
{
|
|
return pair->id;
|
|
}
|
|
|
|
static void
|
|
setup_no_item (GtkSignalListItemFactory *factory,
|
|
GtkListItem *item)
|
|
{
|
|
}
|
|
|
|
static void
|
|
setup_list_item (GtkSignalListItemFactory *factory,
|
|
GtkListItem *item)
|
|
{
|
|
GtkWidget *label;
|
|
|
|
label = gtk_label_new ("");
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
gtk_list_item_set_child (item, label);
|
|
}
|
|
|
|
static void
|
|
bind_list_item (GtkSignalListItemFactory *factory,
|
|
GtkListItem *item)
|
|
{
|
|
GtkStringPair *pair;
|
|
GtkWidget *label;
|
|
|
|
pair = gtk_list_item_get_item (item);
|
|
label = gtk_list_item_get_child (item);
|
|
|
|
gtk_label_set_text (GTK_LABEL (label), gtk_string_pair_get_string (pair));
|
|
}
|
|
|
|
static void
|
|
selected_changed (GtkDropDown *dropdown,
|
|
GParamSpec *pspec,
|
|
gpointer data)
|
|
{
|
|
GListModel *model;
|
|
guint selected;
|
|
GtkStringPair *pair;
|
|
|
|
model = gtk_drop_down_get_model (dropdown);
|
|
selected = gtk_drop_down_get_selected (dropdown);
|
|
|
|
pair = g_list_model_get_item (model, selected);
|
|
|
|
g_print ("selected %s\n", gtk_string_pair_get_id (pair));
|
|
|
|
g_object_unref (pair);
|
|
}
|
|
|
|
static void
|
|
selected_changed2 (GtkDropDown *dropdown,
|
|
GParamSpec *pspec,
|
|
gpointer data)
|
|
{
|
|
GListModel *model;
|
|
guint selected;
|
|
GtkStringPair *pair;
|
|
GtkWidget *entry = data;
|
|
|
|
model = gtk_drop_down_get_model (dropdown);
|
|
selected = gtk_drop_down_get_selected (dropdown);
|
|
|
|
pair = g_list_model_get_item (model, selected);
|
|
|
|
gtk_editable_set_text (GTK_EDITABLE (entry), gtk_string_pair_get_string (pair));
|
|
|
|
g_object_unref (pair);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *window, *button, *box, *spin, *check;
|
|
GListModel *model;
|
|
GtkExpression *expression;
|
|
const char * const times[] = { "1 minute", "2 minutes", "5 minutes", "20 minutes", NULL };
|
|
const char * const many_times[] = {
|
|
"1 minute", "2 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes",
|
|
"25 minutes", "30 minutes", "35 minutes", "40 minutes", "45 minutes", "50 minutes",
|
|
"55 minutes", "1 hour", "2 hours", "3 hours", "5 hours", "6 hours", "7 hours",
|
|
"8 hours", "9 hours", "10 hours", "11 hours", "12 hours", NULL
|
|
};
|
|
const char * const device_titles[] = { "Digital Output", "Headphones", "Digital Output", "Analog Output", NULL };
|
|
const char * const device_icons[] = { "audio-card-symbolic", "audio-headphones-symbolic", "audio-card-symbolic", "audio-card-symbolic", NULL };
|
|
const char * const device_descriptions[] = {
|
|
"Built-in Audio", "Built-in audio", "Thinkpad Tunderbolt 3 Dock USB Audio", "Thinkpad Tunderbolt 3 Dock USB Audio", NULL
|
|
};
|
|
gboolean quit = FALSE;
|
|
GListStore *store;
|
|
GtkListItemFactory *factory;
|
|
GtkWidget *entry;
|
|
GtkWidget *hbox;
|
|
|
|
gtk_init ();
|
|
|
|
window = gtk_window_new ();
|
|
gtk_window_set_title (GTK_WINDOW (window), "hello world");
|
|
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
|
|
g_signal_connect (window, "close-request", G_CALLBACK (quit_cb), &quit);
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
|
|
gtk_widget_set_margin_start (box, 10);
|
|
gtk_widget_set_margin_end (box, 10);
|
|
gtk_widget_set_margin_top (box, 10);
|
|
gtk_widget_set_margin_bottom (box, 10);
|
|
gtk_window_set_child (GTK_WINDOW (window), box);
|
|
|
|
button = gtk_drop_down_new (NULL, NULL);
|
|
|
|
model = G_LIST_MODEL (pango_cairo_font_map_get_default ());
|
|
gtk_drop_down_set_model (GTK_DROP_DOWN (button), model);
|
|
gtk_drop_down_set_selected (GTK_DROP_DOWN (button), 0);
|
|
|
|
expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
|
|
0, NULL,
|
|
(GCallback)get_family_name,
|
|
NULL, NULL);
|
|
gtk_drop_down_set_expression (GTK_DROP_DOWN (button), expression);
|
|
gtk_expression_unref (expression);
|
|
gtk_box_append (GTK_BOX (box), button);
|
|
|
|
spin = gtk_spin_button_new_with_range (-1, g_list_model_get_n_items (G_LIST_MODEL (model)), 1);
|
|
gtk_widget_set_halign (spin, GTK_ALIGN_START);
|
|
g_object_bind_property (button, "selected", spin, "value", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
|
|
gtk_box_append (GTK_BOX (box), spin);
|
|
|
|
check = gtk_check_button_new_with_label ("Enable search");
|
|
g_object_bind_property (button, "enable-search", check, "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
|
|
gtk_box_append (GTK_BOX (box), check);
|
|
|
|
g_object_unref (model);
|
|
|
|
button = drop_down_new_from_strings (times, NULL, NULL);
|
|
gtk_box_append (GTK_BOX (box), button);
|
|
|
|
button = drop_down_new_from_strings (many_times, NULL, NULL);
|
|
gtk_box_append (GTK_BOX (box), button);
|
|
|
|
button = drop_down_new_from_strings (many_times, NULL, NULL);
|
|
gtk_drop_down_set_enable_search (GTK_DROP_DOWN (button), TRUE);
|
|
expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
|
|
0, NULL,
|
|
(GCallback)get_title,
|
|
NULL, NULL);
|
|
gtk_drop_down_set_expression (GTK_DROP_DOWN (button), expression);
|
|
gtk_expression_unref (expression);
|
|
gtk_box_append (GTK_BOX (box), button);
|
|
|
|
button = drop_down_new_from_strings (device_titles, device_icons, device_descriptions);
|
|
gtk_box_append (GTK_BOX (box), button);
|
|
|
|
button = gtk_drop_down_new (NULL, NULL);
|
|
|
|
store = g_list_store_new (GTK_TYPE_STRING_PAIR);
|
|
g_list_store_append (store, gtk_string_pair_new ("1", "One"));
|
|
g_list_store_append (store, gtk_string_pair_new ("2", "Two"));
|
|
g_list_store_append (store, gtk_string_pair_new ("2.5", "Two ½"));
|
|
g_list_store_append (store, gtk_string_pair_new ("3", "Three"));
|
|
gtk_drop_down_set_model (GTK_DROP_DOWN (button), G_LIST_MODEL (store));
|
|
|
|
factory = gtk_signal_list_item_factory_new ();
|
|
g_signal_connect (factory, "setup", G_CALLBACK (setup_list_item), NULL);
|
|
g_signal_connect (factory, "bind", G_CALLBACK (bind_list_item), NULL);
|
|
gtk_drop_down_set_factory (GTK_DROP_DOWN (button), factory);
|
|
g_object_unref (factory);
|
|
|
|
g_signal_connect (button, "notify::selected", G_CALLBACK (selected_changed), NULL);
|
|
|
|
gtk_box_append (GTK_BOX (box), button);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
|
gtk_widget_add_css_class (hbox, "linked");
|
|
|
|
entry = gtk_entry_new ();
|
|
button = gtk_drop_down_new (NULL, NULL);
|
|
|
|
gtk_drop_down_set_model (GTK_DROP_DOWN (button), G_LIST_MODEL (store));
|
|
|
|
factory = gtk_signal_list_item_factory_new ();
|
|
g_signal_connect (factory, "setup", G_CALLBACK (setup_no_item), NULL);
|
|
gtk_drop_down_set_factory (GTK_DROP_DOWN (button), factory);
|
|
g_object_unref (factory);
|
|
|
|
factory = gtk_signal_list_item_factory_new ();
|
|
g_signal_connect (factory, "setup", G_CALLBACK (setup_list_item), NULL);
|
|
g_signal_connect (factory, "bind", G_CALLBACK (bind_list_item), NULL);
|
|
gtk_drop_down_set_list_factory (GTK_DROP_DOWN (button), factory);
|
|
g_object_unref (factory);
|
|
|
|
g_signal_connect (button, "notify::selected", G_CALLBACK (selected_changed2), entry);
|
|
|
|
gtk_box_append (GTK_BOX (hbox), entry);
|
|
gtk_box_append (GTK_BOX (hbox), button);
|
|
|
|
gtk_box_append (GTK_BOX (box), hbox);
|
|
|
|
g_object_unref (store);
|
|
|
|
gtk_window_present (GTK_WINDOW (window));
|
|
|
|
while (!quit)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
|
|
return 0;
|
|
}
|