mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2025-01-04 09:40:19 +00:00
Merge branch 'listview-for-merge' into 'master'
Listview for merge Closes #2214 See merge request GNOME/gtk!1991
This commit is contained in:
commit
ce1e364ab8
@ -35,6 +35,10 @@
|
||||
*/
|
||||
#mesondefine HAVE_DECL_ISNAN
|
||||
|
||||
/* Define to 1 if you have the declaration of `isnanf', and to 0 if you don't.
|
||||
*/
|
||||
#mesondefine HAVE_DECL_ISNANF
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#mesondefine HAVE_DLFCN_H
|
||||
|
||||
@ -294,4 +298,4 @@
|
||||
#mesondefine ISO_CODES_PREFIX
|
||||
|
||||
/* Define if tracker3 is available */
|
||||
#mesondefine HAVE_TRACKER3
|
||||
#mesondefine HAVE_TRACKER3
|
||||
|
@ -169,6 +169,7 @@ constraint_view_init (ConstraintView *self)
|
||||
GListModel *guides;
|
||||
GListModel *children;
|
||||
GListModel *constraints;
|
||||
GtkFilter *filter;
|
||||
|
||||
manager = gtk_constraint_layout_new ();
|
||||
gtk_widget_set_layout_manager (GTK_WIDGET (self), manager);
|
||||
@ -176,8 +177,12 @@ constraint_view_init (ConstraintView *self)
|
||||
all_children = gtk_widget_observe_children (GTK_WIDGET (self));
|
||||
all_constraints = gtk_constraint_layout_observe_constraints (GTK_CONSTRAINT_LAYOUT (manager));
|
||||
guides = gtk_constraint_layout_observe_guides (GTK_CONSTRAINT_LAYOUT (manager));
|
||||
constraints = (GListModel *)gtk_filter_list_model_new (all_constraints, omit_internal, NULL, NULL);
|
||||
children = (GListModel *)gtk_filter_list_model_new (all_children, omit_internal, NULL, NULL);
|
||||
filter = gtk_custom_filter_new (omit_internal, NULL, NULL);
|
||||
constraints = (GListModel *)gtk_filter_list_model_new (all_constraints, filter);
|
||||
g_object_unref (filter);
|
||||
filter = gtk_custom_filter_new (omit_internal, NULL, NULL);
|
||||
children = (GListModel *)gtk_filter_list_model_new (all_children, filter);
|
||||
g_object_unref (filter);
|
||||
|
||||
list = g_list_store_new (G_TYPE_LIST_MODEL);
|
||||
g_list_store_append (list, children);
|
||||
|
@ -184,58 +184,6 @@ max_input (GtkSpinButton *spin_button,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
min_output (GtkSpinButton *spin_button)
|
||||
{
|
||||
GtkAdjustment *adjustment;
|
||||
double value;
|
||||
GtkWidget *box, *text;
|
||||
|
||||
adjustment = gtk_spin_button_get_adjustment (spin_button);
|
||||
value = gtk_adjustment_get_value (adjustment);
|
||||
|
||||
box = gtk_widget_get_first_child (GTK_WIDGET (spin_button));
|
||||
text = gtk_widget_get_first_child (box);
|
||||
|
||||
if (value == 0.0)
|
||||
{
|
||||
gtk_editable_set_text (GTK_EDITABLE (spin_button), "");
|
||||
gtk_text_set_placeholder_text (GTK_TEXT (text), "unset");
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_text_set_placeholder_text (GTK_TEXT (text), "");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
max_output (GtkSpinButton *spin_button)
|
||||
{
|
||||
GtkAdjustment *adjustment;
|
||||
double value;
|
||||
GtkWidget *box, *text;
|
||||
|
||||
adjustment = gtk_spin_button_get_adjustment (spin_button);
|
||||
value = gtk_adjustment_get_value (adjustment);
|
||||
|
||||
box = gtk_widget_get_first_child (GTK_WIDGET (spin_button));
|
||||
text = gtk_widget_get_first_child (box);
|
||||
|
||||
if (value == (double)G_MAXINT)
|
||||
{
|
||||
gtk_editable_set_text (GTK_EDITABLE (spin_button), "");
|
||||
gtk_text_set_placeholder_text (GTK_TEXT (text), "unset");
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_text_set_placeholder_text (GTK_TEXT (text), "");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
guide_editor_constructed (GObject *object)
|
||||
{
|
||||
@ -244,16 +192,12 @@ guide_editor_constructed (GObject *object)
|
||||
guide_strength_combo (editor->strength);
|
||||
|
||||
g_signal_connect (editor->min_width, "input", G_CALLBACK (min_input), NULL);
|
||||
g_signal_connect (editor->min_width, "output", G_CALLBACK (min_output), NULL);
|
||||
|
||||
g_signal_connect (editor->min_height, "input", G_CALLBACK (min_input), NULL);
|
||||
g_signal_connect (editor->min_height, "output", G_CALLBACK (min_output), NULL);
|
||||
|
||||
g_signal_connect (editor->max_width, "input", G_CALLBACK (max_input), NULL);
|
||||
g_signal_connect (editor->max_width, "output", G_CALLBACK (max_output), NULL);
|
||||
|
||||
g_signal_connect (editor->max_height, "input", G_CALLBACK (max_input), NULL);
|
||||
g_signal_connect (editor->max_height, "output", G_CALLBACK (max_output), NULL);
|
||||
|
||||
if (editor->guide)
|
||||
{
|
||||
|
247
demos/gtk-demo/award.c
Normal file
247
demos/gtk-demo/award.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "award.h"
|
||||
|
||||
struct _GtkAward
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
char *explanation;
|
||||
char *name;
|
||||
char *title;
|
||||
GDateTime *granted; /* or NULL if not granted */
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_EXPLANATION,
|
||||
PROP_NAME,
|
||||
PROP_TITLE,
|
||||
PROP_GRANTED,
|
||||
|
||||
N_PROPS,
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
G_DEFINE_TYPE (GtkAward, gtk_award, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gtk_award_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
|
||||
{
|
||||
GtkAward *self = GTK_AWARD (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_EXPLANATION:
|
||||
self->explanation = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
self->name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
self->title = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_award_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkAward *self = GTK_AWARD (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_EXPLANATION:
|
||||
g_value_set_string (value, self->explanation);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, self->name);
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
g_value_set_string (value, self->title);
|
||||
break;
|
||||
|
||||
case PROP_GRANTED:
|
||||
g_value_set_boxed (value, self->granted);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_award_dispose (GObject *object)
|
||||
{
|
||||
GtkAward *self = GTK_AWARD (object);
|
||||
|
||||
g_clear_pointer (&self->name, g_free);
|
||||
g_clear_pointer (&self->title, g_free);
|
||||
g_clear_pointer (&self->granted, g_date_time_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_award_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_award_class_init (GtkAwardClass *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
gobject_class->set_property = gtk_award_set_property;
|
||||
gobject_class->get_property = gtk_award_get_property;
|
||||
gobject_class->dispose = gtk_award_dispose;
|
||||
|
||||
properties[PROP_EXPLANATION] =
|
||||
g_param_spec_string ("explanation",
|
||||
"Explanation",
|
||||
"How to get the title",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_NAME] =
|
||||
g_param_spec_string ("name",
|
||||
"Name",
|
||||
"internal name of the award",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_TITLE] =
|
||||
g_param_spec_string ("title",
|
||||
"Title",
|
||||
"user-visible title",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_GRANTED] =
|
||||
g_param_spec_boxed ("granted",
|
||||
"Granted",
|
||||
"Timestamp the award was granted or NULL if not granted yet",
|
||||
G_TYPE_DATE_TIME,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_award_init (GtkAward *self)
|
||||
{
|
||||
}
|
||||
|
||||
GListModel *
|
||||
gtk_award_get_list (void)
|
||||
{
|
||||
static GListModel *list = NULL;
|
||||
|
||||
if (list == NULL)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
|
||||
g_type_ensure (GTK_TYPE_AWARD);
|
||||
builder = gtk_builder_new_from_resource ("/awards.ui");
|
||||
list = G_LIST_MODEL (gtk_builder_get_object (builder, "list"));
|
||||
g_object_ref (list);
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
return g_object_ref (list);
|
||||
}
|
||||
|
||||
const char *
|
||||
gtk_award_get_name (GtkAward *award)
|
||||
{
|
||||
return award->name;
|
||||
}
|
||||
|
||||
const char *
|
||||
gtk_award_get_title (GtkAward *award)
|
||||
{
|
||||
return award->title;
|
||||
}
|
||||
|
||||
GDateTime *
|
||||
gtk_award_get_granted (GtkAward *award)
|
||||
{
|
||||
return award->granted;
|
||||
}
|
||||
|
||||
GtkAward *
|
||||
award_find (const char *name)
|
||||
{
|
||||
GListModel *list;
|
||||
GtkAward *self;
|
||||
guint i;
|
||||
|
||||
list = gtk_award_get_list ();
|
||||
g_object_unref (list);
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (list); i++)
|
||||
{
|
||||
self = g_list_model_get_item (list, i);
|
||||
g_object_unref (self);
|
||||
|
||||
if (g_ascii_strcasecmp (name, self->name) == 0)
|
||||
return self;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
award (const char *name)
|
||||
{
|
||||
GtkAward *self;
|
||||
GNotification *notification;
|
||||
|
||||
self = award_find (name);
|
||||
if (self == NULL)
|
||||
{
|
||||
g_warning ("Did not find award \"%s\"", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->granted)
|
||||
return;
|
||||
|
||||
self->granted = g_date_time_new_now_utc ();
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_GRANTED]);
|
||||
|
||||
notification = g_notification_new ("You won an award!");
|
||||
g_notification_set_body (notification, self->title);
|
||||
g_application_send_notification (g_application_get_default (), NULL, notification);
|
||||
g_object_unref (notification);
|
||||
}
|
||||
|
18
demos/gtk-demo/award.h
Normal file
18
demos/gtk-demo/award.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef __AWARD_H__
|
||||
#define __AWARD_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GTK_TYPE_AWARD (gtk_award_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GtkAward, gtk_award, GTK, AWARD, GObject)
|
||||
|
||||
GListModel * gtk_award_get_list (void);
|
||||
|
||||
const char * gtk_award_get_name (GtkAward *award);
|
||||
const char * gtk_award_get_title (GtkAward *award);
|
||||
GDateTime * gtk_award_get_granted (GtkAward *award);
|
||||
|
||||
void award (const char *name);
|
||||
|
||||
#endif /* __AWARD_H__ */
|
11
demos/gtk-demo/awardlistitem.ui
Normal file
11
demos/gtk-demo/awardlistitem.ui
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface domain="gtk40">
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" bind-source="GtkListItem" bind-property="position"></property>
|
||||
<property name="margin">6</property>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
89
demos/gtk-demo/awards.ui
Normal file
89
demos/gtk-demo/awards.ui
Normal file
@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GListStore" id="list">
|
||||
<property name="item-type">GtkAward</property>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">demo-inspector</property>
|
||||
<!-- Transformers -->
|
||||
<property name="title" translatable="yes">You got a high-rise double-pump carburetor.</property>
|
||||
<property name="explanation" translatable="yes">Launch the inspector</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">demo-start</property>
|
||||
<!-- The Matrix -->
|
||||
<property name="title" translatable="yes">After this, there is no turning back.</property>
|
||||
<property name="explanation" translatable="yes">Start gtk-demo</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">listbox-reshare</property>
|
||||
<!-- Mean Girls -->
|
||||
<property name="title" translatable="yes">Trying to make fetch happen</property>
|
||||
<property name="explanation" translatable="yes">Reshare a tweet</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">listbox-100th-row</property>
|
||||
<!-- Aladdin -->
|
||||
<property name="title" translatable="yes">The ever impressive, long contained, often imitated, but never duplicated Genie of the lamp.</property>
|
||||
<property name="explanation" translatable="yes">Select a 100th row in a list</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">password-best</property>
|
||||
<!-- Spaceballs -->
|
||||
<property name="title" translatable="yes">I've got the same combination on my luggage!</property>
|
||||
<property name="explanation" translatable="yes">Use "12345" as the password</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">password-correct</property>
|
||||
<!-- Stanley Parable -->
|
||||
<property name="title" translatable="yes">Night Shark 1-1-5</property>
|
||||
<property name="explanation" translatable="yes">Correctly enter a password</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">puzzle-give-up</property>
|
||||
<!-- Pretty Woman -->
|
||||
<property name="title" translatable="yes">Big Mistake. Big. Huge!</property>
|
||||
<property name="explanation" translatable="yes">Close the puzzle without finishing it</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">puzzle-solve</property>
|
||||
<!-- The Incredibles -->
|
||||
<property name="title" translatable="yes">That was totally wicked!</property>
|
||||
<property name="explanation" translatable="yes">Solve a puzzle</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">puzzle-solve-animated</property>
|
||||
<!-- The Phantom Menace -->
|
||||
<property name="title" translatable="yes">A surprise to be sure but a welcome one.</property>
|
||||
<property name="explanation" translatable="yes">Solve an animated puzzle</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">puzzle-solve-large</property>
|
||||
<!-- Portal -->
|
||||
<property name="title" translatable="yes">Science isn't about WHY. It's about WHY NOT?!</property>
|
||||
<property name="explanation" translatable="yes">Solve a puzzle with at least 20 pieces</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
48
demos/gtk-demo/awardview.c
Normal file
48
demos/gtk-demo/awardview.c
Normal file
@ -0,0 +1,48 @@
|
||||
/* Awards
|
||||
*
|
||||
* This demo demonstrates how to use lists to show the awards you have collected
|
||||
* while exploring this demo.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* Include the header for accessing the awards */
|
||||
#include "award.h"
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_awardview (GtkWidget *do_widget)
|
||||
{
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *sw, *listview;
|
||||
GListModel *list;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Awards");
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_window_set_child (GTK_WINDOW (window), sw);
|
||||
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_builder_list_item_factory_new_from_resource (NULL, "/awardview/awardlistitem.ui"));
|
||||
list = gtk_award_get_list ();
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), list);
|
||||
g_object_unref (list);
|
||||
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), listview);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
@ -1,13 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/">
|
||||
<file>awards.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/ui">
|
||||
<file preprocess="xml-stripblanks">main.ui</file>
|
||||
<file preprocess="xml-stripblanks">main-listitem.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/application_demo">
|
||||
<file>application.c</file>
|
||||
<file>application.ui</file>
|
||||
<file>menus.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/awardview">
|
||||
<file>awardlistitem.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/builder">
|
||||
<file>demo.ui</file>
|
||||
</gresource>
|
||||
@ -118,6 +125,19 @@
|
||||
<file>gnome-fs-directory.png</file>
|
||||
<file>gnome-fs-regular.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview_filebrowser">
|
||||
<file>listview_filebrowser.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview_minesweeper">
|
||||
<file>listview_minesweeper.ui</file>
|
||||
<file>listview_minesweeper_cell.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview_settings">
|
||||
<file>listview_settings.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview_weather">
|
||||
<file compressed="true">listview_weather.txt</file>
|
||||
</gresource>
|
||||
<gresource prefix="/shortcuts">
|
||||
<file>shortcuts.ui</file>
|
||||
<file>shortcuts-builder.ui</file>
|
||||
@ -154,6 +174,7 @@
|
||||
</gresource>
|
||||
<gresource prefix="/sources">
|
||||
<file>application_demo.c</file>
|
||||
<file>awardview.c</file>
|
||||
<file>assistant.c</file>
|
||||
<file>builder.c</file>
|
||||
<file>clipboard.c</file>
|
||||
@ -194,6 +215,12 @@
|
||||
<file>infobar.c</file>
|
||||
<file>links.c</file>
|
||||
<file>listbox.c</file>
|
||||
<file>listview_applauncher.c</file>
|
||||
<file>listview_clocks.c</file>
|
||||
<file>listview_filebrowser.c</file>
|
||||
<file>listview_minesweeper.c</file>
|
||||
<file>listview_settings.c</file>
|
||||
<file>listview_weather.c</file>
|
||||
<file>list_store.c</file>
|
||||
<file>markup.c</file>
|
||||
<file>modelbutton.c</file>
|
||||
|
@ -249,6 +249,14 @@ fishbowl_changes_toggled_cb (GtkToggleButton *button,
|
||||
gtk_button_set_icon_name (GTK_BUTTON (button), "changes-allow");
|
||||
}
|
||||
|
||||
char *
|
||||
format_header_cb (GObject *object,
|
||||
guint count,
|
||||
double fps)
|
||||
{
|
||||
return g_strdup_printf ("%u Icons, %.2f fps", count, fps);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_fishbowl (GtkWidget *do_widget)
|
||||
{
|
||||
|
@ -28,22 +28,12 @@
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label">fps</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" bind-source="bowl" bind-property="framerate-string"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label">Icons, </property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" bind-source="bowl" bind-property="count"/>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="format_header_cb">
|
||||
<lookup name="count">bowl</lookup>
|
||||
<lookup name="framerate">bowl</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
|
@ -4,6 +4,8 @@
|
||||
* as needed and support sorting and filtering.
|
||||
*
|
||||
* The children of a GtkFlowBox are regular widgets
|
||||
*
|
||||
* The dataset used here has 665 colors.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
@ -13,17 +13,16 @@ in_files = sys.argv[2:]
|
||||
file_output = """
|
||||
typedef GtkWidget *(*GDoDemoFunc) (GtkWidget *do_widget);
|
||||
|
||||
typedef struct _Demo Demo;
|
||||
typedef struct _DemoData DemoData;
|
||||
|
||||
struct _Demo
|
||||
struct _DemoData
|
||||
{
|
||||
const char *name;
|
||||
const char *title;
|
||||
const char *filename;
|
||||
GDoDemoFunc func;
|
||||
Demo *children;
|
||||
DemoData *children;
|
||||
};
|
||||
|
||||
"""
|
||||
|
||||
# Demo = namedtuple('Demo', ['name', 'title', 'file', 'func'])
|
||||
@ -67,7 +66,7 @@ for demo in demos:
|
||||
i = 0
|
||||
for parent in parents:
|
||||
id = parent_ids[i]
|
||||
file_output += "\nDemo child" + str(id) + "[] = {\n"
|
||||
file_output += "\nDemoData child" + str(id) + "[] = {\n"
|
||||
# iterate over all demos and check if the name starts with the given parent name
|
||||
for child in demos:
|
||||
if child[1].startswith(parent + "/"):
|
||||
@ -82,7 +81,7 @@ for parent in parents:
|
||||
# Sort demos by title
|
||||
demos = sorted(demos, key=lambda x: x[1])
|
||||
|
||||
file_output += "\nDemo gtk_demos[] = {\n"
|
||||
file_output += "\nDemoData gtk_demos[] = {\n"
|
||||
for demo in demos:
|
||||
# Do not generate one of these for demos with a parent demo
|
||||
if "/" not in demo[1]:
|
||||
|
@ -53,7 +53,6 @@ enum {
|
||||
PROP_BENCHMARK,
|
||||
PROP_COUNT,
|
||||
PROP_FRAMERATE,
|
||||
PROP_FRAMERATE_STRING,
|
||||
PROP_UPDATE_DELAY,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
@ -289,14 +288,6 @@ gtk_fishbowl_get_property (GObject *object,
|
||||
g_value_set_double (value, gtk_fishbowl_get_framerate (fishbowl));
|
||||
break;
|
||||
|
||||
case PROP_FRAMERATE_STRING:
|
||||
{
|
||||
char *s = g_strdup_printf ("%.2f", gtk_fishbowl_get_framerate (fishbowl));
|
||||
g_value_set_string (value, s);
|
||||
g_free (s);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_UPDATE_DELAY:
|
||||
g_value_set_int64 (value, gtk_fishbowl_get_update_delay (fishbowl));
|
||||
break;
|
||||
@ -350,13 +341,6 @@ gtk_fishbowl_class_init (GtkFishbowlClass *klass)
|
||||
0,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
props[PROP_FRAMERATE_STRING] =
|
||||
g_param_spec_string ("framerate-string",
|
||||
"Framerate as string",
|
||||
"Framerate as string, with 2 decimals",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
props[PROP_UPDATE_DELAY] =
|
||||
g_param_spec_int64 ("update-delay",
|
||||
"Update delay",
|
||||
@ -508,7 +492,6 @@ gtk_fishbowl_do_update (GtkFishbowl *fishbowl)
|
||||
priv->framerate = ((int)(priv->framerate * 100))/100.0;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_FRAMERATE]);
|
||||
g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_FRAMERATE_STRING]);
|
||||
|
||||
if (!priv->benchmark)
|
||||
return;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "award.h"
|
||||
|
||||
static GdkPixbuf *avatar_pixbuf_other;
|
||||
static GtkWidget *window = NULL;
|
||||
@ -234,9 +235,9 @@ reshare_clicked (GtkMessageRow *row,
|
||||
{
|
||||
GtkMessageRowPrivate *priv = row->priv;
|
||||
|
||||
award ("listbox-reshare");
|
||||
priv->message->n_reshares++;
|
||||
gtk_message_row_update (row);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
@ -255,11 +256,14 @@ gtk_message_row_state_flags_changed (GtkWidget *widget,
|
||||
{
|
||||
GtkMessageRowPrivate *priv = GTK_MESSAGE_ROW (widget)->priv;
|
||||
GtkStateFlags flags;
|
||||
gboolean visible;
|
||||
|
||||
flags = gtk_widget_get_state_flags (widget);
|
||||
|
||||
gtk_widget_set_visible (priv->extra_buttons_box,
|
||||
flags & (GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED));
|
||||
visible = flags & (GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED) ? TRUE : FALSE;
|
||||
gtk_widget_set_visible (priv->extra_buttons_box, visible);
|
||||
if (visible && gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (widget)) % 100 == 99)
|
||||
award ("listbox-100th-row");
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_message_row_parent_class)->state_flags_changed (widget, previous_state_flags);
|
||||
}
|
||||
|
199
demos/gtk-demo/listview_applauncher.c
Normal file
199
demos/gtk-demo/listview_applauncher.c
Normal file
@ -0,0 +1,199 @@
|
||||
/* Lists/Application launcher
|
||||
*
|
||||
* This demo uses the GtkCoverFlow widget as a fancy application launcher.
|
||||
*
|
||||
* It is also a very small introduction to listviews.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* This is the function that creates the #GListModel that we need.
|
||||
* GTK list widgets need a #GListModel to display, as models support change
|
||||
* notifications.
|
||||
* Unfortunately various older APIs do not provide list models, so we create
|
||||
* our own.
|
||||
*/
|
||||
static GListModel *
|
||||
create_application_list (void)
|
||||
{
|
||||
GListStore *store;
|
||||
GList *apps, *l;
|
||||
|
||||
/* We use a #GListStore here, which is a simple array-like list implementation
|
||||
* for manual management.
|
||||
* List models need to know what type of data they provide, so we need to
|
||||
* provide the type here. As we want to do a list of applications, #GAppInfo
|
||||
* is the object we provide.
|
||||
*/
|
||||
store = g_list_store_new (G_TYPE_APP_INFO);
|
||||
|
||||
apps = g_app_info_get_all ();
|
||||
|
||||
for (l = apps; l; l = l->next)
|
||||
{
|
||||
g_list_store_append (store, l->data);
|
||||
}
|
||||
|
||||
g_list_free_full (apps, g_object_unref);
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
/* This is the function we use for setting up new listitems to display.
|
||||
* We add just a #GtkImage here to display the application's icon as this is just
|
||||
* a simple demo.
|
||||
*/
|
||||
static void
|
||||
setup_listitem_cb (GtkListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *image;
|
||||
|
||||
image = gtk_image_new ();
|
||||
gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
|
||||
|
||||
gtk_list_item_set_child (list_item, image);
|
||||
}
|
||||
|
||||
/* Here we need to prepare the listitem for displaying its item. We get the
|
||||
* listitem already set up from the previous function, so we can reuse the
|
||||
* #GtkImage widget we set up above.
|
||||
* We get the item - which we know is a #GAppInfo because it comes out of
|
||||
* the model we set up above, grab its icon and display it.
|
||||
*/
|
||||
static void
|
||||
bind_listitem_cb (GtkListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *image;
|
||||
GAppInfo *app_info;
|
||||
|
||||
image = gtk_list_item_get_child (list_item);
|
||||
app_info = gtk_list_item_get_item (list_item);
|
||||
|
||||
gtk_image_set_from_gicon (GTK_IMAGE (image), g_app_info_get_icon (app_info));
|
||||
}
|
||||
|
||||
/* In more complex code, we would also need functions to unbind and teardown
|
||||
* the listitem, but this is simple code, so the default implementations are
|
||||
* enough. If we had connected signals, this step would have been necessary.
|
||||
*
|
||||
* The #GtkSignalListItemFactory documentation contains more information about
|
||||
* this step.
|
||||
*/
|
||||
|
||||
/* This function is called whenever an item in the list is activated. This is
|
||||
* the simple way to allow reacting to the Enter key or double-clicking on a
|
||||
* listitem.
|
||||
* Of course, it is possible to use far more complex interactions by turning
|
||||
* off activation and adding buttons or other widgets in the setup function
|
||||
* above, but this is a simple demo, so we'll use the simple way.
|
||||
*/
|
||||
static void
|
||||
activate_cb (GtkCoverFlow *coverflow,
|
||||
guint position,
|
||||
gpointer unused)
|
||||
{
|
||||
GAppInfo *app_info;
|
||||
GdkAppLaunchContext *context;
|
||||
GError *error = NULL;
|
||||
|
||||
app_info = g_list_model_get_item (gtk_cover_flow_get_model (coverflow), position);
|
||||
|
||||
/* Prepare the context for launching the application and launch it. This
|
||||
* code is explained in detail in the documentation for #GdkAppLaunchContext
|
||||
* and #GAppInfo.
|
||||
*/
|
||||
context = gdk_display_get_app_launch_context (gtk_widget_get_display (GTK_WIDGET (coverflow)));
|
||||
if (!g_app_info_launch (app_info,
|
||||
NULL,
|
||||
G_APP_LAUNCH_CONTEXT (context),
|
||||
&error))
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
/* And because error handling is important, even a simple demo has it:
|
||||
* We display an error dialog that something went wrong.
|
||||
*/
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (coverflow))),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"Could not launch %s", g_app_info_get_display_name (app_info));
|
||||
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
|
||||
g_clear_error (&error);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
g_object_unref (context);
|
||||
g_object_unref (app_info);
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_applauncher (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkWidget *coverflow, *sw;;
|
||||
GListModel *model;
|
||||
GtkListItemFactory *factory;
|
||||
|
||||
/* Create a window and set a few defaults */
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 640, 320);
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Application Launcher");
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
|
||||
|
||||
/* The #GtkListitemFactory is what is used to create #GtkListItems
|
||||
* to display the data from the model. So it is absolutely necessary
|
||||
* to create one.
|
||||
* We will use a #GtkSignalListItemFactory because it is the simplest
|
||||
* one to use. Different ones are available for different use cases.
|
||||
* The most powerful one is #GtkBuilderListItemFactory which uses
|
||||
* #GtkBuilder .ui files, so it requires little code.
|
||||
*/
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
|
||||
g_signal_connect (factory, "bind", G_CALLBACK (bind_listitem_cb), NULL);
|
||||
|
||||
/* Create the list widget here: We use a coverflow widget because it's
|
||||
* the coolest one. We could just as well use other list widgets such
|
||||
* as a #GtkListView or a #GtkGridView and the code would look very
|
||||
* similar.
|
||||
*/
|
||||
coverflow = gtk_cover_flow_new_with_factory (factory);
|
||||
/* We connect the activate signal here. It's the function we defined
|
||||
* above for launching the selected application.
|
||||
*/
|
||||
g_signal_connect (coverflow, "activate", G_CALLBACK (activate_cb), NULL);
|
||||
|
||||
/* And of course we need to set the data model. Here we call the function
|
||||
* we wrote above that gives us the list of applications. Then we set
|
||||
* it on the coverflow list widget.
|
||||
* The coverflow will now take items from the model and use the factory
|
||||
* to create as many listitems as it needs to show itself to the user.
|
||||
*/
|
||||
model = create_application_list ();
|
||||
gtk_cover_flow_set_model (GTK_COVER_FLOW (coverflow), model);
|
||||
g_object_unref (model);
|
||||
|
||||
/* List widgets should always be contained in a #GtkScrolledWindow,
|
||||
* because otherwise they might get too large or they might not
|
||||
* be scrollable.
|
||||
*/
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_window_set_child (GTK_WINDOW (window), sw);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), coverflow);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
489
demos/gtk-demo/listview_clocks.c
Normal file
489
demos/gtk-demo/listview_clocks.c
Normal file
@ -0,0 +1,489 @@
|
||||
/* Lists/Clocks
|
||||
*
|
||||
* This demo displays the time in different timezones.
|
||||
*
|
||||
* The goal is to show how to set up expressions that track changes
|
||||
* in objects and make them update widgets.
|
||||
*
|
||||
* For that, we create a GtkClock object that updates its time every
|
||||
* second and then use various ways to display that time.
|
||||
*
|
||||
* Typically, this will be done using GtkBuilder .ui files with the
|
||||
* help of the <binding> tag, but this demo shows the code that runs
|
||||
* behind that.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GTK_TYPE_CLOCK (gtk_clock_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkClock, gtk_clock, GTK, CLOCK, GObject)
|
||||
|
||||
/* This is our object. It's just a timezone */
|
||||
typedef struct _GtkClock GtkClock;
|
||||
struct _GtkClock
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
/* We allow this to be NULL for the local timezone */
|
||||
GTimeZone *timezone;
|
||||
/* Name of the location we're displaying time for */
|
||||
char *location;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_LOCATION,
|
||||
PROP_TIME,
|
||||
PROP_TIMEZONE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
/* This function returns the current time in the clock's timezone.
|
||||
* Note that this returns a new object every time, so we need to
|
||||
* remember to unref it after use. */
|
||||
static GDateTime *
|
||||
gtk_clock_get_time (GtkClock *clock)
|
||||
{
|
||||
if (clock->timezone)
|
||||
return g_date_time_new_now (clock->timezone);
|
||||
else
|
||||
return g_date_time_new_now_local ();
|
||||
}
|
||||
|
||||
/* Here, we implement the functionality required by the GdkPaintable interface.
|
||||
* This way we have a trivial way to display an analog clock.
|
||||
* It also allows demonstrating how to directly use objects in the listview
|
||||
* later by making this object do something interesting. */
|
||||
static void
|
||||
gtk_clock_snapshot (GdkPaintable *paintable,
|
||||
GdkSnapshot *snapshot,
|
||||
double width,
|
||||
double height)
|
||||
{
|
||||
GtkClock *self = GTK_CLOCK (paintable);
|
||||
GDateTime *time;
|
||||
GskRoundedRect outline;
|
||||
|
||||
#define BLACK ((GdkRGBA) { 0, 0, 0, 1 })
|
||||
|
||||
/* save/restore() is necessary so we can undo the transforms we start
|
||||
* out with. */
|
||||
gtk_snapshot_save (snapshot);
|
||||
|
||||
/* First, we move the (0, 0) point to the center of the area so
|
||||
* we can draw everything relative to it. */
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (width / 2, height / 2));
|
||||
/* Next we scale it, so that we can pretend that the clock is
|
||||
* 100px in size. That way, we don't need to do any complicated
|
||||
* math later.
|
||||
* We use MIN() here so that we use the smaller dimension for sizing.
|
||||
* That way we don't overdraw but keep the aspect ratio. */
|
||||
gtk_snapshot_scale (snapshot, MIN (width, height) / 100.0, MIN (width, height) / 100.0);
|
||||
/* Now we have a circle with diameter 100px (and radius 50px) that
|
||||
* has its (0, 0) point at the center.
|
||||
* Let's draw a simple clock into it. */
|
||||
|
||||
time = gtk_clock_get_time (self);
|
||||
|
||||
/* First, draw a circle. This is a neat little trick to draw a circle
|
||||
* without requiring Cairo. */
|
||||
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-50, -50, 100, 100), 50);
|
||||
gtk_snapshot_append_border (snapshot,
|
||||
&outline,
|
||||
(float[4]) { 4, 4, 4, 4 },
|
||||
(GdkRGBA [4]) { BLACK, BLACK, BLACK, BLACK });
|
||||
|
||||
/* Next, draw the hour hand.
|
||||
* We do this using tranforms again: Instead of computing where the angle points
|
||||
* to, we just rotate everything and then draw the hand as if if was :00.
|
||||
* We don't even need to care about am/pm here because rotations just work. */
|
||||
gtk_snapshot_save (snapshot);
|
||||
gtk_snapshot_rotate (snapshot, 30 * g_date_time_get_hour (time) + 0.5 * g_date_time_get_minute (time));
|
||||
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-2, -23, 4, 25), 2);
|
||||
gtk_snapshot_push_rounded_clip (snapshot, &outline);
|
||||
gtk_snapshot_append_color (snapshot, &BLACK, &outline.bounds);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
gtk_snapshot_restore (snapshot);
|
||||
|
||||
/* And the same as above for the minute hand. Just make this one longer
|
||||
* so people can tell the hands apart. */
|
||||
gtk_snapshot_save (snapshot);
|
||||
gtk_snapshot_rotate (snapshot, 6 * g_date_time_get_minute (time));
|
||||
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-2, -43, 4, 45), 2);
|
||||
gtk_snapshot_push_rounded_clip (snapshot, &outline);
|
||||
gtk_snapshot_append_color (snapshot, &BLACK, &outline.bounds);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
gtk_snapshot_restore (snapshot);
|
||||
|
||||
/* and finally, the second indicator. */
|
||||
gtk_snapshot_save (snapshot);
|
||||
gtk_snapshot_rotate (snapshot, 6 * g_date_time_get_second (time));
|
||||
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-2, -43, 4, 10), 2);
|
||||
gtk_snapshot_push_rounded_clip (snapshot, &outline);
|
||||
gtk_snapshot_append_color (snapshot, &BLACK, &outline.bounds);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
gtk_snapshot_restore (snapshot);
|
||||
|
||||
/* And finally, don't forget to restore the initial save() that we did for
|
||||
* the initial transformations. */
|
||||
gtk_snapshot_restore (snapshot);
|
||||
|
||||
g_date_time_unref (time);
|
||||
}
|
||||
|
||||
/* Our desired size is 100px. That sounds okay for an analog clock */
|
||||
static int
|
||||
gtk_clock_get_intrinsic_width (GdkPaintable *paintable)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_clock_get_intrinsic_height (GdkPaintable *paintable)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
/* Initialize the paintable interface. This way we turn our clock objects
|
||||
* into objects that can be drawn.
|
||||
* There are more functions to this interface to define desired size,
|
||||
* but this is enough.
|
||||
*/
|
||||
static void
|
||||
gtk_clock_paintable_init (GdkPaintableInterface *iface)
|
||||
{
|
||||
iface->snapshot = gtk_clock_snapshot;
|
||||
iface->get_intrinsic_width = gtk_clock_get_intrinsic_width;
|
||||
iface->get_intrinsic_height = gtk_clock_get_intrinsic_height;
|
||||
}
|
||||
|
||||
/* Finally, we define the type. The important part is adding the paintable
|
||||
* interface, so GTK knows that this object can indeed be drawm.
|
||||
*/
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkClock, gtk_clock, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||
gtk_clock_paintable_init))
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_clock_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkClock *self = GTK_CLOCK (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_LOCATION:
|
||||
g_value_set_string (value, self->location);
|
||||
break;
|
||||
|
||||
case PROP_TIME:
|
||||
g_value_take_boxed (value, gtk_clock_get_time (self));
|
||||
break;
|
||||
|
||||
case PROP_TIMEZONE:
|
||||
g_value_set_boxed (value, self->timezone);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkClock *self = GTK_CLOCK (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_LOCATION:
|
||||
self->location = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_TIMEZONE:
|
||||
self->timezone = g_value_dup_boxed (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is the list of all the ticking clocks */
|
||||
static GSList *ticking_clocks = NULL;
|
||||
/* This is the id of the timeout source that is updating all ticking clocks */
|
||||
static guint ticking_clock_id = 0;
|
||||
|
||||
/* Every second, this function is called to tell everybody that the
|
||||
* clocks are ticking.
|
||||
*/
|
||||
static gboolean
|
||||
gtk_clock_tick (gpointer unused)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
for (l = ticking_clocks; l; l = l->next)
|
||||
{
|
||||
GtkClock *clock = l->data;
|
||||
|
||||
/* We will now return a different value for the time porperty,
|
||||
* so notify about that.
|
||||
*/
|
||||
g_object_notify_by_pspec (G_OBJECT (clock), properties[PROP_TIME]);
|
||||
/* We will also draw the hands of the clock differently.
|
||||
* So notify about that, too.
|
||||
*/
|
||||
gdk_paintable_invalidate_contents (GDK_PAINTABLE (clock));
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_stop_ticking (GtkClock *self)
|
||||
{
|
||||
ticking_clocks = g_slist_remove (ticking_clocks, self);
|
||||
|
||||
/* If no clock is remaining, stop running the tick updates */
|
||||
if (ticking_clocks == NULL && ticking_clock_id != 0)
|
||||
g_clear_handle_id (&ticking_clock_id, g_source_remove);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_start_ticking (GtkClock *self)
|
||||
{
|
||||
/* if no clock is ticking yet, start */
|
||||
if (ticking_clock_id == 0)
|
||||
ticking_clock_id = g_timeout_add_seconds (1, gtk_clock_tick, NULL);
|
||||
|
||||
ticking_clocks = g_slist_prepend (ticking_clocks, self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_finalize (GObject *object)
|
||||
{
|
||||
GtkClock *self = GTK_CLOCK (object);
|
||||
|
||||
gtk_clock_stop_ticking (self);
|
||||
|
||||
g_free (self->location);
|
||||
g_clear_pointer (&self->timezone, g_time_zone_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_clock_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_class_init (GtkClockClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gtk_clock_get_property;
|
||||
gobject_class->set_property = gtk_clock_set_property;
|
||||
gobject_class->finalize = gtk_clock_finalize;
|
||||
|
||||
properties[PROP_LOCATION] =
|
||||
g_param_spec_string ("location", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
properties[PROP_TIME] =
|
||||
g_param_spec_boxed ("time", NULL, NULL, G_TYPE_DATE_TIME, G_PARAM_READABLE);
|
||||
properties[PROP_TIMEZONE] =
|
||||
g_param_spec_boxed ("timezone", NULL, NULL, G_TYPE_TIME_ZONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_init (GtkClock *self)
|
||||
{
|
||||
gtk_clock_start_ticking (self);
|
||||
}
|
||||
|
||||
static GtkClock *
|
||||
gtk_clock_new (const char *location,
|
||||
GTimeZone *timezone)
|
||||
{
|
||||
GtkClock *result;
|
||||
|
||||
result = g_object_new (GTK_TYPE_CLOCK,
|
||||
"location", location,
|
||||
"timezone", timezone,
|
||||
NULL);
|
||||
|
||||
g_clear_pointer (&timezone, g_time_zone_unref);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_clocks_model (void)
|
||||
{
|
||||
GListStore *result;
|
||||
GtkClock *clock;
|
||||
|
||||
result = g_list_store_new (GTK_TYPE_CLOCK);
|
||||
|
||||
/* local time */
|
||||
clock = gtk_clock_new ("local", NULL);
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
/* UTC time */
|
||||
clock = gtk_clock_new ("UTC", g_time_zone_new_utc ());
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
/* A bunch of timezones with GTK hackers */
|
||||
clock = gtk_clock_new ("San Francisco", g_time_zone_new ("America/Los_Angeles"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("Boston", g_time_zone_new ("America/New_York"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("London", g_time_zone_new ("Europe/London"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("Berlin", g_time_zone_new ("Europe/Berlin"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("Moscow", g_time_zone_new ("Europe/Moscow"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("New Delhi", g_time_zone_new ("Asia/Kolkata"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("Shanghai", g_time_zone_new ("Asia/Shanghai"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
|
||||
return G_LIST_MODEL (result);
|
||||
}
|
||||
|
||||
static char *
|
||||
convert_time_to_string (GObject *image,
|
||||
GDateTime *time,
|
||||
gpointer unused)
|
||||
{
|
||||
return g_date_time_format (time, "%x\n%X");
|
||||
}
|
||||
|
||||
/* And this function is the crux for this whole demo.
|
||||
* It shows how to use expressions to set up bindings.
|
||||
*/
|
||||
static void
|
||||
setup_listitem_cb (GtkListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *box, *picture, *location_label, *time_label;
|
||||
GtkExpression *clock_expression, *expression;
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_list_item_set_child (list_item, box);
|
||||
|
||||
/* First, we create an expression that gets us the clock from the listitem:
|
||||
* 1. Create an expression that gets the list item.
|
||||
* 2. Use that expression's "item" property to get the clock
|
||||
*/
|
||||
expression = gtk_constant_expression_new (GTK_TYPE_LIST_ITEM, list_item);
|
||||
clock_expression = gtk_property_expression_new (GTK_TYPE_LIST_ITEM, expression, "item");
|
||||
|
||||
/* Bind the clock's location to a label.
|
||||
* This is easy: We just get the "location" property of the clock.
|
||||
*/
|
||||
expression = gtk_property_expression_new (GTK_TYPE_CLOCK,
|
||||
gtk_expression_ref (clock_expression),
|
||||
"location");
|
||||
/* Now create the label and bind the expression to it. */
|
||||
location_label = gtk_label_new (NULL);
|
||||
gtk_expression_bind (expression, location_label, "label", location_label);
|
||||
gtk_box_append (GTK_BOX (box), location_label);
|
||||
|
||||
|
||||
/* Here we bind the item itself to a GdkPicture.
|
||||
* This is simply done by using the clock expression itself.
|
||||
*/
|
||||
expression = gtk_expression_ref (clock_expression);
|
||||
/* Now create the widget and bind the expression to it. */
|
||||
picture = gtk_picture_new ();
|
||||
gtk_expression_bind (expression, picture, "paintable", picture);
|
||||
gtk_box_append (GTK_BOX (box), picture);
|
||||
|
||||
|
||||
/* And finally, everything comes together.
|
||||
* We create a label for displaying the time as text.
|
||||
* For that, we need to transform the "GDateTime" of the
|
||||
* time property into a string so that the label can display it.
|
||||
*/
|
||||
expression = gtk_property_expression_new (GTK_TYPE_CLOCK,
|
||||
gtk_expression_ref (clock_expression),
|
||||
"time");
|
||||
expression = gtk_cclosure_expression_new (G_TYPE_STRING,
|
||||
NULL,
|
||||
1, (GtkExpression *[1]) { expression },
|
||||
G_CALLBACK (convert_time_to_string),
|
||||
NULL, NULL);
|
||||
/* Now create the label and bind the expression to it. */
|
||||
time_label = gtk_label_new (NULL);
|
||||
gtk_expression_bind (expression, time_label, "label", time_label);
|
||||
gtk_box_append (GTK_BOX (box), time_label);
|
||||
|
||||
gtk_expression_unref (clock_expression);
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_clocks (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkWidget *gridview, *sw;
|
||||
GtkListItemFactory *factory;
|
||||
GListModel *model;
|
||||
GtkNoSelection *selection;
|
||||
|
||||
/* This is the normal window setup code every demo does */
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Clocks");
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
|
||||
|
||||
/* List widgets go into a scrolled window. Always. */
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_window_set_child (GTK_WINDOW (window), sw);
|
||||
|
||||
/* Create the factory that creates the listitems. Because we
|
||||
* used bindings above during setup, we only need to connect
|
||||
* to the setup signal.
|
||||
* The bindings take care of the bind step.
|
||||
*/
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
|
||||
|
||||
gridview = gtk_grid_view_new_with_factory (factory);
|
||||
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
|
||||
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
|
||||
|
||||
model = create_clocks_model ();
|
||||
selection = gtk_no_selection_new (model);
|
||||
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), G_LIST_MODEL (selection));
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), gridview);
|
||||
g_object_unref (selection);
|
||||
g_object_unref (model);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
255
demos/gtk-demo/listview_filebrowser.c
Normal file
255
demos/gtk-demo/listview_filebrowser.c
Normal file
@ -0,0 +1,255 @@
|
||||
/* Lists/File browser
|
||||
*
|
||||
* This demo shows off the different layouts that are quickly achievable
|
||||
* with GtkListview and GtkGridView by implementing a file browser with
|
||||
* different views.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
/* Create a simple object that holds the data for the different views */
|
||||
typedef struct _FileBrowserView FileBrowserView;
|
||||
struct _FileBrowserView
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkListItemFactory *factory;
|
||||
char *icon_name;
|
||||
GtkOrientation orientation;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_FACTORY,
|
||||
PROP_ICON_NAME,
|
||||
PROP_ORIENTATION,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
#define FILE_BROWSER_TYPE_VIEW (file_browser_view_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (FileBrowserView, file_browser_view, FILE_BROWSER, VIEW, GObject);
|
||||
|
||||
G_DEFINE_TYPE (FileBrowserView, file_browser_view, G_TYPE_OBJECT);
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
file_browser_view_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FileBrowserView *self = FILE_BROWSER_VIEW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, self->factory);
|
||||
break;
|
||||
|
||||
case PROP_ICON_NAME:
|
||||
g_value_set_string (value, self->icon_name);
|
||||
break;
|
||||
|
||||
case PROP_ORIENTATION:
|
||||
g_value_set_enum (value, self->orientation);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
file_browser_view_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FileBrowserView *self = FILE_BROWSER_VIEW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_set_object (&self->factory, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_ICON_NAME:
|
||||
g_free (self->icon_name);
|
||||
self->icon_name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_ORIENTATION:
|
||||
self->orientation = g_value_get_enum (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
file_browser_view_finalize (GObject *object)
|
||||
{
|
||||
FileBrowserView *self = FILE_BROWSER_VIEW (object);
|
||||
|
||||
g_object_unref (self->factory);
|
||||
g_free (self->icon_name);
|
||||
|
||||
G_OBJECT_CLASS (file_browser_view_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
file_browser_view_class_init (FileBrowserViewClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = file_browser_view_get_property;
|
||||
gobject_class->set_property = file_browser_view_set_property;
|
||||
gobject_class->finalize = file_browser_view_finalize;
|
||||
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
"factory",
|
||||
"factory to use in the main view",
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE);
|
||||
properties[PROP_ICON_NAME] =
|
||||
g_param_spec_string ("icon-name",
|
||||
"icon name",
|
||||
"icon to display for selecting this view",
|
||||
NULL,
|
||||
G_PARAM_READWRITE);
|
||||
properties[PROP_ORIENTATION] =
|
||||
g_param_spec_enum ("orientation",
|
||||
"orientation",
|
||||
"orientation of the view",
|
||||
GTK_TYPE_ORIENTATION,
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void file_browser_view_init (FileBrowserView *self)
|
||||
{
|
||||
}
|
||||
|
||||
char *
|
||||
filebrowser_get_display_name (GObject *object,
|
||||
GFileInfo *info)
|
||||
{
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
return g_strdup (g_file_info_get_attribute_string (info, "standard::display-name"));
|
||||
}
|
||||
|
||||
char *
|
||||
filebrowser_get_content_type (GObject *object,
|
||||
GFileInfo *info)
|
||||
{
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
return g_strdup (g_file_info_get_attribute_string (info, "standard::content-type"));
|
||||
}
|
||||
|
||||
char *
|
||||
filebrowser_get_size (GObject *object,
|
||||
GFileInfo *info)
|
||||
{
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
return g_format_size (g_file_info_get_attribute_uint64 (info, "standard::size"));
|
||||
}
|
||||
|
||||
GIcon *
|
||||
filebrowser_get_icon (GObject *object,
|
||||
GFileInfo *info)
|
||||
{
|
||||
GIcon *icon;
|
||||
|
||||
if (info)
|
||||
icon = G_ICON (g_file_info_get_attribute_object (info, "standard::icon"));
|
||||
else
|
||||
icon = NULL;
|
||||
|
||||
if (icon)
|
||||
g_object_ref (icon);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
void
|
||||
filebrowser_up_clicked_cb (GtkButton *button,
|
||||
GtkDirectoryList *list)
|
||||
{
|
||||
GFile *file;
|
||||
|
||||
file = g_file_get_parent (gtk_directory_list_get_file (list));
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
gtk_directory_list_set_file (list, file);
|
||||
}
|
||||
|
||||
void
|
||||
filebrowser_view_activated_cb (GtkGridView *view,
|
||||
guint pos,
|
||||
GtkDirectoryList *list)
|
||||
{
|
||||
GFileInfo *info;
|
||||
|
||||
info = g_list_model_get_item (gtk_grid_view_get_model (view), pos);
|
||||
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
|
||||
gtk_directory_list_set_file (list, G_FILE (g_file_info_get_attribute_object (info, "standard::file")));
|
||||
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_listview_filebrowser (GtkWidget *do_widget)
|
||||
{
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *view;
|
||||
GtkBuilder *builder;
|
||||
GtkDirectoryList *dirlist;
|
||||
GFile *file;
|
||||
char *cwd;
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/listview_filebrowser/listview_filebrowser.ui");
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
|
||||
|
||||
/* Create the model and fill it with the contents of the current directory */
|
||||
cwd = g_get_current_dir ();
|
||||
file = g_file_new_for_path (cwd);
|
||||
g_free (cwd);
|
||||
dirlist = GTK_DIRECTORY_LIST (gtk_builder_get_object (builder, "dirlist"));
|
||||
gtk_directory_list_set_file (dirlist, file);
|
||||
g_object_unref (file);
|
||||
|
||||
/* grab focus in the view */
|
||||
view = GTK_WIDGET (gtk_builder_get_object (builder, "view"));
|
||||
gtk_widget_grab_focus (view);
|
||||
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
240
demos/gtk-demo/listview_filebrowser.ui
Normal file
240
demos/gtk-demo/listview_filebrowser.ui
Normal file
@ -0,0 +1,240 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GListStore" id="viewlist">
|
||||
<property name="item-type">FileBrowserView</property>
|
||||
<child>
|
||||
<object class="FileBrowserView">
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="filebrowser_get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="filebrowser_get_display_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="icon-name">view-list-symbolic</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="FileBrowserView">
|
||||
<property name="icon-name">view-grid-symbolic</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-size">large</property>
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="filebrowser_get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="wrap">1</property>
|
||||
<property name="wrap-mode">word-char</property>
|
||||
<property name="lines">2</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="width-chars">10</property>
|
||||
<property name="max-width-chars">30</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="filebrowser_get_display_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="orientation">vertical</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="FileBrowserView">
|
||||
<property name="icon-name">view-paged-symbolic</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-size">large</property>
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="filebrowser_get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="filebrowser_get_display_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="filebrowser_get_size">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="filebrowser_get_content_type">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkDirectoryList" id="dirlist">
|
||||
<property name="attributes">standard::name,standard::display-name,standard::icon,standard::size,standard::content-type</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="title" translatable="yes">File browser</property>
|
||||
<property name="default-width">600</property>
|
||||
<property name="default-height">400</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="">
|
||||
<property name="show-title-buttons">1</property>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="icon-name">go-up</property>
|
||||
<signal name="clicked" handler="filebrowser_up_clicked_cb" object="dirlist" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkListView">
|
||||
<property name="valign">center</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
<style>
|
||||
<class name="linked"/>
|
||||
</style>
|
||||
<property name="model">
|
||||
<object class="GtkSingleSelection" id="selected-view">
|
||||
<property name="model">viewlist</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkImage">
|
||||
<binding name="icon-name">
|
||||
<lookup type="FileBrowserView" name="icon-name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="can-focus">1</property>
|
||||
<child>
|
||||
<object class="GtkGridView" id="view">
|
||||
<property name="model">dirlist</property>
|
||||
<property name="max-columns">15</property>
|
||||
<binding name="factory">
|
||||
<lookup name="factory" type="FileBrowserView">
|
||||
<lookup name="selected-item">selected-view</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<binding name="orientation">
|
||||
<lookup name="orientation" type="FileBrowserView">
|
||||
<lookup name="selected-item">selected-view</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<signal name="activate" handler="filebrowser_view_activated_cb" object="dirlist" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
472
demos/gtk-demo/listview_minesweeper.c
Normal file
472
demos/gtk-demo/listview_minesweeper.c
Normal file
@ -0,0 +1,472 @@
|
||||
/* Lists/Minesweeper
|
||||
*
|
||||
* This demo shows how to develop a user interface for small game using a
|
||||
* gridview.
|
||||
*
|
||||
* It demonstrates how to use the activate signal and single-press behavior
|
||||
* to implement rather different interaction behavior to a typical list.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/*** The cell object ***/
|
||||
|
||||
/* Create an object that holds the data for a cell in the game */
|
||||
typedef struct _SweeperCell SweeperCell;
|
||||
struct _SweeperCell
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
gboolean is_mine;
|
||||
gboolean is_visible;
|
||||
guint neighbor_mines;
|
||||
};
|
||||
|
||||
enum {
|
||||
CELL_PROP_0,
|
||||
CELL_PROP_LABEL,
|
||||
|
||||
N_CELL_PROPS
|
||||
};
|
||||
|
||||
#define SWEEPER_TYPE_CELL (sweeper_cell_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (SweeperCell, sweeper_cell, SWEEPER, CELL, GObject);
|
||||
|
||||
G_DEFINE_TYPE (SweeperCell, sweeper_cell, G_TYPE_OBJECT);
|
||||
static GParamSpec *cell_properties[N_CELL_PROPS] = { NULL, };
|
||||
|
||||
static const char *
|
||||
sweeper_cell_get_label (SweeperCell *self)
|
||||
{
|
||||
static const char *minecount_labels[10] = { "", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
|
||||
|
||||
if (!self->is_visible)
|
||||
return "?";
|
||||
|
||||
if (self->is_mine)
|
||||
return "💣";
|
||||
|
||||
return minecount_labels[self->neighbor_mines];
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SweeperCell *self = SWEEPER_CELL (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case CELL_PROP_LABEL:
|
||||
g_value_set_string (value, sweeper_cell_get_label (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_class_init (SweeperCellClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = sweeper_cell_get_property;
|
||||
|
||||
cell_properties[CELL_PROP_LABEL] =
|
||||
g_param_spec_string ("label",
|
||||
"label",
|
||||
"label to display for this row",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_CELL_PROPS, cell_properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_init (SweeperCell *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_reveal (SweeperCell *self)
|
||||
{
|
||||
if (self->is_visible)
|
||||
return;
|
||||
|
||||
self->is_visible = TRUE;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), cell_properties[CELL_PROP_LABEL]);
|
||||
}
|
||||
|
||||
static SweeperCell *
|
||||
sweeper_cell_new (void)
|
||||
{
|
||||
return g_object_new (SWEEPER_TYPE_CELL, NULL);
|
||||
}
|
||||
|
||||
/*** The board object ***/
|
||||
|
||||
/* Create an object that holds the data for the game */
|
||||
typedef struct _SweeperGame SweeperGame;
|
||||
struct _SweeperGame
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GPtrArray *cells;
|
||||
guint width;
|
||||
guint height;
|
||||
gboolean playing;
|
||||
gboolean win;
|
||||
};
|
||||
|
||||
enum {
|
||||
GAME_PROP_0,
|
||||
GAME_PROP_HEIGHT,
|
||||
GAME_PROP_PLAYING,
|
||||
GAME_PROP_WIDTH,
|
||||
GAME_PROP_WIN,
|
||||
|
||||
N_GAME_PROPS
|
||||
};
|
||||
|
||||
#define SWEEPER_TYPE_GAME (sweeper_game_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (SweeperGame, sweeper_game, SWEEPER, GAME, GObject);
|
||||
|
||||
static GType
|
||||
sweeper_game_list_model_get_item_type (GListModel *model)
|
||||
{
|
||||
return SWEEPER_TYPE_GAME;
|
||||
}
|
||||
|
||||
static guint
|
||||
sweeper_game_list_model_get_n_items (GListModel *model)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (model);
|
||||
|
||||
return self->width * self->height;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
sweeper_game_list_model_get_item (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (model);
|
||||
|
||||
return g_object_ref (g_ptr_array_index (self->cells, position));
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_list_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = sweeper_game_list_model_get_item_type;
|
||||
iface->get_n_items = sweeper_game_list_model_get_n_items;
|
||||
iface->get_item = sweeper_game_list_model_get_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (SweeperGame, sweeper_game, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, sweeper_game_list_model_init))
|
||||
|
||||
static GParamSpec *game_properties[N_GAME_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
sweeper_game_dispose (GObject *object)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (object);
|
||||
|
||||
g_clear_pointer (&self->cells, g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (sweeper_game_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case GAME_PROP_HEIGHT:
|
||||
g_value_set_uint (value, self->height);
|
||||
break;
|
||||
|
||||
case GAME_PROP_PLAYING:
|
||||
g_value_set_boolean (value, self->playing);
|
||||
break;
|
||||
|
||||
case GAME_PROP_WIDTH:
|
||||
g_value_set_uint (value, self->width);
|
||||
break;
|
||||
|
||||
case GAME_PROP_WIN:
|
||||
g_value_set_boolean (value, self->win);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_class_init (SweeperGameClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = sweeper_game_dispose;
|
||||
gobject_class->get_property = sweeper_game_get_property;
|
||||
|
||||
game_properties[GAME_PROP_HEIGHT] =
|
||||
g_param_spec_uint ("height",
|
||||
"height",
|
||||
"height of the game grid",
|
||||
1, G_MAXUINT, 8,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
game_properties[GAME_PROP_PLAYING] =
|
||||
g_param_spec_boolean ("playing",
|
||||
"playing",
|
||||
"if the game is still going on",
|
||||
FALSE,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
game_properties[GAME_PROP_WIDTH] =
|
||||
g_param_spec_uint ("width",
|
||||
"width",
|
||||
"width of the game grid",
|
||||
1, G_MAXUINT, 8,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
game_properties[GAME_PROP_WIN] =
|
||||
g_param_spec_boolean ("win",
|
||||
"win",
|
||||
"if the game was won",
|
||||
FALSE,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_GAME_PROPS, game_properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_reset_board (SweeperGame *self,
|
||||
guint width,
|
||||
guint height)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_ptr_array_set_size (self->cells, 0);
|
||||
|
||||
for (i = 0; i < width * height; i++)
|
||||
{
|
||||
g_ptr_array_add (self->cells, sweeper_cell_new ());
|
||||
}
|
||||
|
||||
if (self->width != width)
|
||||
{
|
||||
self->width = width;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIDTH]);
|
||||
}
|
||||
if (self->height != height)
|
||||
{
|
||||
self->height = height;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_HEIGHT]);
|
||||
}
|
||||
if (!self->playing)
|
||||
{
|
||||
self->playing = TRUE;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_PLAYING]);
|
||||
}
|
||||
if (self->win)
|
||||
{
|
||||
self->win = FALSE;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIN]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_place_mines (SweeperGame *self,
|
||||
guint n_mines)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < n_mines; i++)
|
||||
{
|
||||
SweeperCell *cell;
|
||||
|
||||
do {
|
||||
cell = g_ptr_array_index (self->cells, g_random_int_range (0, self->cells->len));
|
||||
} while (cell->is_mine);
|
||||
|
||||
cell->is_mine = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static SweeperCell *
|
||||
get_cell (SweeperGame *self,
|
||||
guint x,
|
||||
guint y)
|
||||
{
|
||||
return g_ptr_array_index (self->cells, y * self->width + x);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_count_neighbor_mines (SweeperGame *self,
|
||||
guint width,
|
||||
guint height)
|
||||
{
|
||||
guint x, y, x2, y2;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
SweeperCell *cell = get_cell (self, x, y);
|
||||
|
||||
for (y2 = MAX (1, y) - 1; y2 < MIN (height, y + 2); y2++)
|
||||
{
|
||||
for (x2 = MAX (1, x) - 1; x2 < MIN (width, x + 2); x2++)
|
||||
{
|
||||
SweeperCell *other = get_cell (self, x2, y2);
|
||||
|
||||
if (other->is_mine)
|
||||
cell->neighbor_mines++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_new_game (SweeperGame *self,
|
||||
guint width,
|
||||
guint height,
|
||||
guint n_mines)
|
||||
{
|
||||
guint n_items_before;
|
||||
|
||||
g_return_if_fail (n_mines <= width * height);
|
||||
|
||||
n_items_before = self->width * self->height;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
sweeper_game_reset_board (self, width, height);
|
||||
sweeper_game_place_mines (self, n_mines);
|
||||
sweeper_game_count_neighbor_mines (self, width, height);
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items_before, width * height);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_init (SweeperGame *self)
|
||||
{
|
||||
self->cells = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
sweeper_game_new_game (self, 8, 8, 10);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_end (SweeperGame *self,
|
||||
gboolean win)
|
||||
{
|
||||
if (self->playing)
|
||||
{
|
||||
self->playing = FALSE;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_PLAYING]);
|
||||
}
|
||||
if (self->win != win)
|
||||
{
|
||||
self->win = win;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIN]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_check_finished (SweeperGame *self)
|
||||
{
|
||||
guint i;
|
||||
|
||||
if (!self->playing)
|
||||
return;
|
||||
|
||||
for (i = 0; i < self->cells->len; i++)
|
||||
{
|
||||
SweeperCell *cell = g_ptr_array_index (self->cells, i);
|
||||
|
||||
/* There's still a non-revealed cell that isn't a mine */
|
||||
if (!cell->is_visible && !cell->is_mine)
|
||||
return;
|
||||
}
|
||||
|
||||
sweeper_game_end (self, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_reveal_cell (SweeperGame *self,
|
||||
guint position)
|
||||
{
|
||||
SweeperCell *cell;
|
||||
|
||||
if (!self->playing)
|
||||
return;
|
||||
|
||||
cell = g_ptr_array_index (self->cells, position);
|
||||
sweeper_cell_reveal (cell);
|
||||
|
||||
if (cell->is_mine)
|
||||
sweeper_game_end (self, FALSE);
|
||||
|
||||
sweeper_game_check_finished (self);
|
||||
}
|
||||
|
||||
void
|
||||
minesweeper_cell_clicked_cb (GtkGridView *gridview,
|
||||
guint pos,
|
||||
SweeperGame *game)
|
||||
{
|
||||
sweeper_game_reveal_cell (game, pos);
|
||||
}
|
||||
|
||||
void
|
||||
minesweeper_new_game_cb (GtkButton *button,
|
||||
SweeperGame *game)
|
||||
{
|
||||
sweeper_game_new_game (game, 8, 8, 10);
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_minesweeper (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
|
||||
g_type_ensure (SWEEPER_TYPE_GAME);
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/listview_minesweeper/listview_minesweeper.ui");
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
|
||||
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
49
demos/gtk-demo/listview_minesweeper.ui
Normal file
49
demos/gtk-demo/listview_minesweeper.ui
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="SweeperGame" id="game">
|
||||
</object>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="title" translatable="yes">Minesweeper</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="">
|
||||
<property name="show-title-buttons">1</property>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="label">New Game</property>
|
||||
<signal name="clicked" handler="minesweeper_new_game_cb" object="game" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="title">
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">trophy-gold</property>
|
||||
<binding name="visible">
|
||||
<lookup name="win">game</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGridView" id="view">
|
||||
<property name="model">
|
||||
<object class="GtkNoSelection">
|
||||
<property name="model">game</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="single-click-activate">1</property>
|
||||
<binding name="max-columns">
|
||||
<lookup name="width">game</lookup>
|
||||
</binding>
|
||||
<binding name="min-columns">
|
||||
<lookup name="width">game</lookup>
|
||||
</binding>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="resource">/listview_minesweeper/listview_minesweeper_cell.ui</property>
|
||||
</object>
|
||||
</property>
|
||||
<signal name="activate" handler="minesweeper_cell_clicked_cb" object="game" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
16
demos/gtk-demo/listview_minesweeper_cell.ui
Normal file
16
demos/gtk-demo/listview_minesweeper_cell.ui
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<binding name="label">
|
||||
<lookup name="label" type="SweeperCell">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
333
demos/gtk-demo/listview_settings.c
Normal file
333
demos/gtk-demo/listview_settings.c
Normal file
@ -0,0 +1,333 @@
|
||||
/* Lists/Settings
|
||||
*
|
||||
* This demo shows a settings viewer for GSettings.
|
||||
*
|
||||
* It demonstrates how to implement support for trees with GtkListView.
|
||||
* It also shows how to set up sorting and filtering for columns in a
|
||||
* GtkColumnView.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* Create an object that wraps GSettingsSchemaKey because that's a boxed type */
|
||||
typedef struct _SettingsKey SettingsKey;
|
||||
struct _SettingsKey
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GSettings *settings;
|
||||
GSettingsSchemaKey *key;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_NAME,
|
||||
PROP_SUMMARY,
|
||||
PROP_DESCRIPTION,
|
||||
PROP_VALUE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
#define SETTINGS_TYPE_KEY (settings_key_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (SettingsKey, settings_key, SETTINGS, KEY, GObject);
|
||||
|
||||
G_DEFINE_TYPE (SettingsKey, settings_key, G_TYPE_OBJECT);
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
settings_key_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SettingsKey *self = SETTINGS_KEY (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_DESCRIPTION:
|
||||
g_value_set_string (value, g_settings_schema_key_get_description (self->key));
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, g_settings_schema_key_get_name (self->key));
|
||||
break;
|
||||
|
||||
case PROP_SUMMARY:
|
||||
g_value_set_string (value, g_settings_schema_key_get_summary (self->key));
|
||||
break;
|
||||
|
||||
case PROP_VALUE:
|
||||
{
|
||||
GVariant *variant = g_settings_get_value (self->settings, g_settings_schema_key_get_name (self->key));
|
||||
g_value_take_string (value, g_variant_print (variant, FALSE));
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
settings_key_finalize (GObject *object)
|
||||
{
|
||||
SettingsKey *self = SETTINGS_KEY (object);
|
||||
|
||||
g_object_unref (self->settings);
|
||||
g_settings_schema_key_unref (self->key);
|
||||
|
||||
G_OBJECT_CLASS (settings_key_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
settings_key_class_init (SettingsKeyClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = settings_key_finalize;
|
||||
gobject_class->get_property = settings_key_get_property;
|
||||
|
||||
properties[PROP_DESCRIPTION] =
|
||||
g_param_spec_string ("description", NULL, NULL, NULL, G_PARAM_READABLE);
|
||||
properties[PROP_NAME] =
|
||||
g_param_spec_string ("name", NULL, NULL, NULL, G_PARAM_READABLE);
|
||||
properties[PROP_SUMMARY] =
|
||||
g_param_spec_string ("summary", NULL, NULL, NULL, G_PARAM_READABLE);
|
||||
properties[PROP_VALUE] =
|
||||
g_param_spec_string ("value", NULL, NULL, NULL, G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
settings_key_init (SettingsKey *self)
|
||||
{
|
||||
}
|
||||
|
||||
static SettingsKey *
|
||||
settings_key_new (GSettings *settings,
|
||||
GSettingsSchemaKey *key)
|
||||
{
|
||||
SettingsKey *result = g_object_new (SETTINGS_TYPE_KEY, NULL);
|
||||
|
||||
result->settings = g_object_ref (settings);
|
||||
result->key = g_settings_schema_key_ref (key);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
strvcmp (gconstpointer p1,
|
||||
gconstpointer p2)
|
||||
{
|
||||
const char * const *s1 = p1;
|
||||
const char * const *s2 = p2;
|
||||
|
||||
return strcmp (*s1, *s2);
|
||||
}
|
||||
|
||||
static GtkFilter *current_filter;
|
||||
|
||||
static gboolean
|
||||
transform_settings_to_keys (GBinding *binding,
|
||||
const GValue *from_value,
|
||||
GValue *to_value,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTreeListRow *treelistrow;
|
||||
GSettings *settings;
|
||||
GSettingsSchema *schema;
|
||||
GListStore *store;
|
||||
GtkSortListModel *sort_model;
|
||||
GtkFilterListModel *filter_model;
|
||||
GtkFilter *filter;
|
||||
GtkExpression *expression;
|
||||
char **keys;
|
||||
guint i;
|
||||
|
||||
treelistrow = g_value_get_object (from_value);
|
||||
if (treelistrow == NULL)
|
||||
return TRUE;
|
||||
settings = gtk_tree_list_row_get_item (treelistrow);
|
||||
g_object_get (settings, "settings-schema", &schema, NULL);
|
||||
|
||||
store = g_list_store_new (SETTINGS_TYPE_KEY);
|
||||
|
||||
keys = g_settings_schema_list_keys (schema);
|
||||
|
||||
for (i = 0; keys[i] != NULL; i++)
|
||||
{
|
||||
GSettingsSchemaKey *almost_there = g_settings_schema_get_key (schema, keys[i]);
|
||||
SettingsKey *finally = settings_key_new (settings, almost_there);
|
||||
g_list_store_append (store, finally);
|
||||
g_object_unref (finally);
|
||||
g_settings_schema_key_unref (almost_there);
|
||||
}
|
||||
|
||||
g_strfreev (keys);
|
||||
g_settings_schema_unref (schema);
|
||||
g_object_unref (settings);
|
||||
|
||||
sort_model = gtk_sort_list_model_new (G_LIST_MODEL (store),
|
||||
gtk_column_view_get_sorter (GTK_COLUMN_VIEW (data)));
|
||||
g_object_unref (store);
|
||||
|
||||
expression = gtk_property_expression_new (SETTINGS_TYPE_KEY, NULL, "name");
|
||||
filter = gtk_string_filter_new ();
|
||||
gtk_string_filter_set_expression (GTK_STRING_FILTER (filter), expression);
|
||||
filter_model = gtk_filter_list_model_new (G_LIST_MODEL (sort_model), filter);
|
||||
gtk_expression_unref (expression);
|
||||
g_object_unref (sort_model);
|
||||
|
||||
g_set_object (¤t_filter, filter);
|
||||
|
||||
g_object_unref (filter);
|
||||
|
||||
g_value_take_object (to_value, filter_model);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_settings_model (gpointer item,
|
||||
gpointer unused)
|
||||
{
|
||||
GSettings *settings = item;
|
||||
char **schemas;
|
||||
GListStore *result;
|
||||
guint i;
|
||||
|
||||
if (settings == NULL)
|
||||
{
|
||||
g_settings_schema_source_list_schemas (g_settings_schema_source_get_default (),
|
||||
TRUE,
|
||||
&schemas,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
schemas = g_settings_list_children (settings);
|
||||
}
|
||||
|
||||
if (schemas == NULL || schemas[0] == NULL)
|
||||
{
|
||||
g_free (schemas);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qsort (schemas, g_strv_length (schemas), sizeof (char *), strvcmp);
|
||||
|
||||
result = g_list_store_new (G_TYPE_SETTINGS);
|
||||
for (i = 0; schemas[i] != NULL; i++)
|
||||
{
|
||||
GSettings *child;
|
||||
|
||||
if (settings == NULL)
|
||||
child = g_settings_new (schemas[i]);
|
||||
else
|
||||
child = g_settings_get_child (settings, schemas[i]);
|
||||
|
||||
g_list_store_append (result, child);
|
||||
g_object_unref (child);
|
||||
}
|
||||
|
||||
g_strfreev (schemas);
|
||||
|
||||
return G_LIST_MODEL (result);
|
||||
}
|
||||
|
||||
static void
|
||||
search_enabled (GtkSearchEntry *entry)
|
||||
{
|
||||
gtk_editable_set_text (GTK_EDITABLE (entry), "");
|
||||
}
|
||||
|
||||
static void
|
||||
search_changed (GtkSearchEntry *entry,
|
||||
gpointer data)
|
||||
{
|
||||
const char *text = gtk_editable_get_text (GTK_EDITABLE (entry));
|
||||
|
||||
if (current_filter)
|
||||
gtk_string_filter_set_search (GTK_STRING_FILTER (current_filter), text);
|
||||
}
|
||||
|
||||
static void
|
||||
stop_search (GtkSearchEntry *entry,
|
||||
gpointer data)
|
||||
{
|
||||
gtk_editable_set_text (GTK_EDITABLE (entry), "");
|
||||
|
||||
if (current_filter)
|
||||
gtk_string_filter_set_search (GTK_STRING_FILTER (current_filter), "");
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_settings (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkWidget *listview, *columnview;
|
||||
GListModel *model;
|
||||
GtkTreeListModel *treemodel;
|
||||
GtkSingleSelection *selection;
|
||||
GtkBuilderScope *scope;
|
||||
GtkBuilder *builder;
|
||||
GtkColumnViewColumn *name_column;
|
||||
GtkSorter *sorter;
|
||||
|
||||
g_type_ensure (SETTINGS_TYPE_KEY);
|
||||
|
||||
scope = gtk_builder_cscope_new ();
|
||||
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "search_enabled", (GCallback)search_enabled);
|
||||
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "search_changed", (GCallback)search_changed);
|
||||
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "stop_search", (GCallback)stop_search);
|
||||
|
||||
builder = gtk_builder_new ();
|
||||
gtk_builder_set_scope (builder, scope);
|
||||
gtk_builder_add_from_resource (builder, "/listview_settings/listview_settings.ui", NULL);
|
||||
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
|
||||
|
||||
listview = GTK_WIDGET (gtk_builder_get_object (builder, "listview"));
|
||||
columnview = GTK_WIDGET (gtk_builder_get_object (builder, "columnview"));
|
||||
model = create_settings_model (NULL, NULL);
|
||||
treemodel = gtk_tree_list_model_new (FALSE,
|
||||
model,
|
||||
TRUE,
|
||||
create_settings_model,
|
||||
NULL,
|
||||
NULL);
|
||||
selection = gtk_single_selection_new (G_LIST_MODEL (treemodel));
|
||||
g_object_bind_property_full (selection, "selected-item",
|
||||
columnview, "model",
|
||||
G_BINDING_SYNC_CREATE,
|
||||
transform_settings_to_keys,
|
||||
NULL,
|
||||
columnview, NULL);
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (selection));
|
||||
g_object_unref (selection);
|
||||
g_object_unref (treemodel);
|
||||
g_object_unref (model);
|
||||
|
||||
name_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "name_column"));
|
||||
sorter = gtk_string_sorter_new (gtk_property_expression_new (SETTINGS_TYPE_KEY, NULL, "name"));
|
||||
gtk_column_view_column_set_sorter (name_column, sorter);
|
||||
g_object_unref (sorter);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
138
demos/gtk-demo/listview_settings.ui
Normal file
138
demos/gtk-demo/listview_settings.ui
Normal file
@ -0,0 +1,138 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="title" translatable="yes">Settings</property>
|
||||
<property name="default-width">640</property>
|
||||
<property name="default-height">480</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar">
|
||||
<property name="show-title-buttons">1</property>
|
||||
<child type="end">
|
||||
<object class="GtkToggleButton" id="search_button">
|
||||
<property name="icon-name">system-search-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned">
|
||||
<property name="position">300</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<child>
|
||||
<object class="GtkListView" id="listview">
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkTreeExpander" id="expander">
|
||||
<binding name="list-row">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</binding>
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<lookup name="schema" type="GSettings">
|
||||
<lookup name="item">expander</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkSearchBar">
|
||||
<property name="search-mode-enabled" bind-source="search_button" bind-property="active" bind-flags="bidirectional"/>
|
||||
<signal name="notify::search-mode-enabled" handler="search_enabled" object="entry"/>
|
||||
<child>
|
||||
<object class="GtkSearchEntry" id="entry">
|
||||
<signal name="search-changed" handler="search_changed"/>
|
||||
<signal name="stop-search" handler="stop_search"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hexpand">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<child>
|
||||
<object class="GtkColumnView" id="columnview">
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="name_column">
|
||||
<property name="title">Name</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<lookup name="name" type="SettingsKey">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title">Value</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<lookup name="value" type="SettingsKey">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
318
demos/gtk-demo/listview_weather.c
Normal file
318
demos/gtk-demo/listview_weather.c
Normal file
@ -0,0 +1,318 @@
|
||||
/* Lists/Weather
|
||||
*
|
||||
* This demo shows a few of the rarer features of GtkListView and
|
||||
* how they can be used to display weather information.
|
||||
*
|
||||
* The hourly weather info uses a horizontal listview. This is easy
|
||||
* to achieve because GtkListView implements the GtkOrientable interface.
|
||||
* To make the items in the list stand out more, the listview uses
|
||||
* separators.
|
||||
*
|
||||
* A GtkNoSelectionModel is used to make sure no item in the list can be
|
||||
* selected. All other interactions with the items is still possible.
|
||||
*
|
||||
* The dataset used here has 70000 items.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GTK_TYPE_WEATHER_INFO (gtk_weather_info_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GtkWeatherInfo, gtk_weather_info, GTK, WEATHER_INFO, GObject)
|
||||
|
||||
typedef enum {
|
||||
GTK_WEATHER_CLEAR,
|
||||
GTK_WEATHER_FEW_CLOUDS,
|
||||
GTK_WEATHER_FOG,
|
||||
GTK_WEATHER_OVERCAST,
|
||||
GTK_WEATHER_SCATTERED_SHOWERS,
|
||||
GTK_WEATHER_SHOWERS,
|
||||
GTK_WEATHER_SNOW,
|
||||
GTK_WEATHER_STORM
|
||||
} GtkWeatherType;
|
||||
|
||||
struct _GtkWeatherInfo
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
gint64 timestamp;
|
||||
int temperature;
|
||||
GtkWeatherType weather_type;
|
||||
};
|
||||
|
||||
struct _GtkWeatherInfoClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkWeatherInfo, gtk_weather_info, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gtk_weather_info_class_init (GtkWeatherInfoClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_weather_info_init (GtkWeatherInfo *self)
|
||||
{
|
||||
}
|
||||
|
||||
static GtkWeatherInfo *
|
||||
gtk_weather_info_new (GDateTime *timestamp,
|
||||
GtkWeatherInfo *copy_from)
|
||||
{
|
||||
GtkWeatherInfo *result;
|
||||
|
||||
result = g_object_new (GTK_TYPE_WEATHER_INFO, NULL);
|
||||
|
||||
result->timestamp = g_date_time_to_unix (timestamp);
|
||||
if (copy_from)
|
||||
{
|
||||
result->temperature = copy_from->temperature;
|
||||
result->weather_type = copy_from->weather_type;
|
||||
g_object_unref (copy_from);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GDateTime *
|
||||
parse_timestamp (const char *string,
|
||||
GTimeZone *timezone)
|
||||
{
|
||||
char *with_seconds;
|
||||
GDateTime *result;
|
||||
|
||||
with_seconds = g_strconcat (string, ":00", NULL);
|
||||
result = g_date_time_new_from_iso8601 (with_seconds, timezone);
|
||||
g_free (with_seconds);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GtkWeatherType
|
||||
parse_weather_type (const char *clouds,
|
||||
const char *precip,
|
||||
GtkWeatherType fallback)
|
||||
{
|
||||
if (strstr (precip, "SN"))
|
||||
return GTK_WEATHER_SNOW;
|
||||
|
||||
if (strstr (precip, "TS"))
|
||||
return GTK_WEATHER_STORM;
|
||||
|
||||
if (strstr (precip, "DZ"))
|
||||
return GTK_WEATHER_SCATTERED_SHOWERS;
|
||||
|
||||
if (strstr (precip, "SH") || strstr (precip, "RA"))
|
||||
return GTK_WEATHER_SHOWERS;
|
||||
|
||||
if (strstr (precip, "FG"))
|
||||
return GTK_WEATHER_FOG;
|
||||
|
||||
if (g_str_equal (clouds, "M") ||
|
||||
g_str_equal (clouds, ""))
|
||||
return fallback;
|
||||
|
||||
if (strstr (clouds, "OVC") ||
|
||||
strstr (clouds, "BKN"))
|
||||
return GTK_WEATHER_OVERCAST;
|
||||
|
||||
if (strstr (clouds, "BKN") ||
|
||||
strstr (clouds, "SCT"))
|
||||
return GTK_WEATHER_FEW_CLOUDS;
|
||||
|
||||
if (strstr (clouds, "VV"))
|
||||
return GTK_WEATHER_FOG;
|
||||
|
||||
return GTK_WEATHER_CLEAR;
|
||||
}
|
||||
|
||||
static double
|
||||
parse_temperature (const char *s,
|
||||
double fallback)
|
||||
{
|
||||
char *endptr;
|
||||
double d;
|
||||
|
||||
d = g_ascii_strtod (s, &endptr);
|
||||
if (*endptr != '\0')
|
||||
return fallback;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_weather_model (void)
|
||||
{
|
||||
GListStore *store;
|
||||
GTimeZone *utc;
|
||||
GDateTime *timestamp;
|
||||
GtkWeatherInfo *info;
|
||||
GBytes *data;
|
||||
char **lines;
|
||||
guint i;
|
||||
|
||||
store = g_list_store_new (GTK_TYPE_WEATHER_INFO);
|
||||
data = g_resources_lookup_data ("/listview_weather/listview_weather.txt", 0, NULL);
|
||||
lines = g_strsplit (g_bytes_get_data (data, NULL), "\n", 0);
|
||||
|
||||
utc = g_time_zone_new_utc ();
|
||||
timestamp = g_date_time_new (utc, 2011, 1, 1, 0, 0, 0);
|
||||
info = gtk_weather_info_new (timestamp, NULL);
|
||||
g_list_store_append (store, info);
|
||||
|
||||
for (i = 0; lines[i] != NULL && *lines[i]; i++)
|
||||
{
|
||||
char **fields;
|
||||
GDateTime *date;
|
||||
|
||||
fields = g_strsplit (lines[i], ",", 0);
|
||||
date = parse_timestamp (fields[0], utc);
|
||||
while (g_date_time_difference (date, timestamp) > 30 * G_TIME_SPAN_MINUTE)
|
||||
{
|
||||
GDateTime *new_timestamp = g_date_time_add_hours (timestamp, 1);
|
||||
g_date_time_unref (timestamp);
|
||||
timestamp = new_timestamp;
|
||||
info = gtk_weather_info_new (timestamp, info);
|
||||
g_list_store_append (store, info);
|
||||
}
|
||||
|
||||
info->temperature = parse_temperature (fields[1], info->temperature);
|
||||
info->weather_type = parse_weather_type (fields[2], fields[3], info->weather_type);
|
||||
g_date_time_unref (date);
|
||||
g_strfreev (fields);
|
||||
}
|
||||
|
||||
g_strfreev (lines);
|
||||
g_bytes_unref (data);
|
||||
g_time_zone_unref (utc);
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_widget (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *box, *child;
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_list_item_set_child (list_item, box);
|
||||
|
||||
child = gtk_label_new (NULL);
|
||||
gtk_label_set_width_chars (GTK_LABEL (child), 5);
|
||||
gtk_box_append (GTK_BOX (box), child);
|
||||
|
||||
child = gtk_image_new ();
|
||||
gtk_image_set_icon_size (GTK_IMAGE (child), GTK_ICON_SIZE_LARGE);
|
||||
gtk_box_append (GTK_BOX (box), child);
|
||||
|
||||
child = gtk_label_new (NULL);
|
||||
gtk_widget_set_vexpand (child, TRUE);
|
||||
gtk_widget_set_valign (child, GTK_ALIGN_END);
|
||||
gtk_label_set_width_chars (GTK_LABEL (child), 4);
|
||||
gtk_box_append (GTK_BOX (box), child);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_widget (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *box, *child;
|
||||
GtkWeatherInfo *info;
|
||||
GDateTime *timestamp;
|
||||
char *s;
|
||||
|
||||
box = gtk_list_item_get_child (list_item);
|
||||
info = gtk_list_item_get_item (list_item);
|
||||
|
||||
child = gtk_widget_get_first_child (box);
|
||||
timestamp = g_date_time_new_from_unix_utc (info->timestamp);
|
||||
s = g_date_time_format (timestamp, "%R");
|
||||
gtk_label_set_text (GTK_LABEL (child), s);
|
||||
g_free (s);
|
||||
g_date_time_unref (timestamp);
|
||||
|
||||
child = gtk_widget_get_next_sibling (child);
|
||||
switch (info->weather_type)
|
||||
{
|
||||
case GTK_WEATHER_CLEAR:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-clear-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_FEW_CLOUDS:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-few-clouds-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_FOG:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-fog-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_OVERCAST:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-overcast-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_SCATTERED_SHOWERS:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-showers-scattered-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_SHOWERS:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-showers-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_SNOW:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-snow-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_STORM:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-storm-symbolic");
|
||||
break;
|
||||
default:
|
||||
gtk_image_clear (GTK_IMAGE (child));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
child = gtk_widget_get_next_sibling (child);
|
||||
s = g_strdup_printf ("%d°", info->temperature);
|
||||
gtk_label_set_text (GTK_LABEL (child), s);
|
||||
g_free (s);
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_weather (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkWidget *listview, *sw;;
|
||||
GListModel *model, *selection;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Weather");
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Weather");
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_window_set_child (GTK_WINDOW (window), sw);
|
||||
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_functions_list_item_factory_new (setup_widget,
|
||||
bind_widget,
|
||||
NULL, NULL));
|
||||
gtk_orientable_set_orientation (GTK_ORIENTABLE (listview), GTK_ORIENTATION_HORIZONTAL);
|
||||
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
|
||||
model = create_weather_model ();
|
||||
selection = G_LIST_MODEL (gtk_no_selection_new (model));
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), selection);
|
||||
g_object_unref (selection);
|
||||
g_object_unref (model);
|
||||
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), listview);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
70116
demos/gtk-demo/listview_weather.txt
Normal file
70116
demos/gtk-demo/listview_weather.txt
Normal file
File diff suppressed because it is too large
Load Diff
22
demos/gtk-demo/main-listitem.ui
Normal file
22
demos/gtk-demo/main-listitem.ui
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkTreeExpander" id="expander">
|
||||
<binding name="list-row">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</binding>
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">
|
||||
<lookup name="title" type="GtkDemo">
|
||||
<lookup name="item">expander</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
@ -8,6 +8,7 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "award.h"
|
||||
#include "demos.h"
|
||||
|
||||
static GtkWidget *info_view;
|
||||
@ -16,18 +17,96 @@ static GtkWidget *source_view;
|
||||
static gchar *current_file = NULL;
|
||||
|
||||
static GtkWidget *notebook;
|
||||
static GtkWidget *treeview;
|
||||
static GtkSingleSelection *selection;
|
||||
static GtkWidget *toplevel;
|
||||
|
||||
enum {
|
||||
NAME_COLUMN,
|
||||
TITLE_COLUMN,
|
||||
FILENAME_COLUMN,
|
||||
FUNC_COLUMN,
|
||||
STYLE_COLUMN,
|
||||
NUM_COLUMNS
|
||||
typedef struct _GtkDemo GtkDemo;
|
||||
struct _GtkDemo
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
const char *name;
|
||||
const char *title;
|
||||
const char *filename;
|
||||
GDoDemoFunc func;
|
||||
GListModel *children_model;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_FILENAME,
|
||||
PROP_NAME,
|
||||
PROP_TITLE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
# define GTK_TYPE_DEMO (gtk_demo_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkDemo, gtk_demo, GTK, DEMO, GObject);
|
||||
|
||||
G_DEFINE_TYPE (GtkDemo, gtk_demo, G_TYPE_OBJECT);
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_demo_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkDemo *self = GTK_DEMO (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FILENAME:
|
||||
g_value_set_string (value, self->filename);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, self->name);
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
g_value_set_string (value, self->title);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gtk_demo_class_init (GtkDemoClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gtk_demo_get_property;
|
||||
|
||||
properties[PROP_FILENAME] =
|
||||
g_param_spec_string ("filename",
|
||||
"filename",
|
||||
"filename",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
properties[PROP_NAME] =
|
||||
g_param_spec_string ("name",
|
||||
"name",
|
||||
"name",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
properties[PROP_TITLE] =
|
||||
g_param_spec_string ("title",
|
||||
"title",
|
||||
"title",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void gtk_demo_init (GtkDemo *self)
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _CallbackData CallbackData;
|
||||
struct _CallbackData
|
||||
{
|
||||
@ -35,6 +114,27 @@ struct _CallbackData
|
||||
GtkTreePath *path;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
gtk_demo_run (GtkDemo *self,
|
||||
GtkWidget *window)
|
||||
{
|
||||
GtkWidget *result;
|
||||
|
||||
if (!self->func)
|
||||
return FALSE;
|
||||
|
||||
result = self->func (window);
|
||||
if (result == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (GTK_IS_WINDOW (result))
|
||||
{
|
||||
gtk_window_set_transient_for (GTK_WINDOW (result), GTK_WINDOW (window));
|
||||
gtk_window_set_modal (GTK_WINDOW (result), TRUE);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
activate_about (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
@ -113,84 +213,18 @@ activate_inspector (GSimpleAction *action,
|
||||
gpointer user_data)
|
||||
{
|
||||
gtk_window_set_interactive_debugging (TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
window_closed_cb (GtkWidget *window, gpointer data)
|
||||
{
|
||||
CallbackData *cbdata = data;
|
||||
GtkTreeIter iter;
|
||||
PangoStyle style;
|
||||
|
||||
gtk_tree_model_get_iter (cbdata->model, &iter, cbdata->path);
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cbdata->model), &iter,
|
||||
STYLE_COLUMN, &style,
|
||||
-1);
|
||||
if (style == PANGO_STYLE_ITALIC)
|
||||
gtk_tree_store_set (GTK_TREE_STORE (cbdata->model), &iter,
|
||||
STYLE_COLUMN, PANGO_STYLE_NORMAL,
|
||||
-1);
|
||||
|
||||
gtk_tree_path_free (cbdata->path);
|
||||
g_free (cbdata);
|
||||
}
|
||||
|
||||
static void
|
||||
run_example_for_row (GtkWidget *window,
|
||||
GtkTreeModel *model,
|
||||
GtkTreeIter *iter)
|
||||
{
|
||||
PangoStyle style;
|
||||
GDoDemoFunc func;
|
||||
GtkWidget *demo;
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (model),
|
||||
iter,
|
||||
FUNC_COLUMN, &func,
|
||||
STYLE_COLUMN, &style,
|
||||
-1);
|
||||
|
||||
if (func)
|
||||
{
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model),
|
||||
iter,
|
||||
STYLE_COLUMN, (style == PANGO_STYLE_ITALIC ? PANGO_STYLE_NORMAL : PANGO_STYLE_ITALIC),
|
||||
-1);
|
||||
demo = (func) (window);
|
||||
|
||||
if (demo != NULL)
|
||||
{
|
||||
CallbackData *cbdata;
|
||||
|
||||
cbdata = g_new (CallbackData, 1);
|
||||
cbdata->model = model;
|
||||
cbdata->path = gtk_tree_model_get_path (model, iter);
|
||||
|
||||
if (GTK_IS_WINDOW (demo))
|
||||
{
|
||||
gtk_window_set_transient_for (GTK_WINDOW (demo), GTK_WINDOW (window));
|
||||
gtk_window_set_modal (GTK_WINDOW (demo), TRUE);
|
||||
}
|
||||
|
||||
g_signal_connect (demo, "destroy",
|
||||
G_CALLBACK (window_closed_cb), cbdata);
|
||||
}
|
||||
}
|
||||
award ("demo-inspector");
|
||||
}
|
||||
|
||||
static void
|
||||
activate_run (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data)
|
||||
gpointer window)
|
||||
{
|
||||
GtkWidget *window = user_data;
|
||||
GtkTreeSelection *selection;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeListRow *row = gtk_single_selection_get_selected_item (selection);
|
||||
GtkDemo *demo = gtk_tree_list_row_get_item (row);
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
|
||||
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
||||
run_example_for_row (window, model, &iter);
|
||||
gtk_demo_run (demo, window);
|
||||
}
|
||||
|
||||
/* Stupid syntax highlighting.
|
||||
@ -895,107 +929,93 @@ load_file (const gchar *demoname,
|
||||
}
|
||||
|
||||
static void
|
||||
selection_cb (GtkTreeSelection *selection,
|
||||
GtkTreeModel *model)
|
||||
activate_cb (GtkWidget *widget,
|
||||
guint position,
|
||||
gpointer window)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
char *name;
|
||||
char *filename;
|
||||
char *title;
|
||||
GtkTreeListRow *row = g_list_model_get_item (gtk_list_view_get_model (GTK_LIST_VIEW (widget)), position);
|
||||
GtkDemo *demo = gtk_tree_list_row_get_item (row);
|
||||
|
||||
if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
|
||||
return;
|
||||
gtk_demo_run (demo, window);
|
||||
|
||||
gtk_tree_model_get (model, &iter,
|
||||
NAME_COLUMN, &name,
|
||||
TITLE_COLUMN, &title,
|
||||
FILENAME_COLUMN, &filename,
|
||||
-1);
|
||||
|
||||
if (filename)
|
||||
load_file (name, filename);
|
||||
|
||||
gtk_window_set_title (GTK_WINDOW (toplevel), title);
|
||||
|
||||
g_free (name);
|
||||
g_free (title);
|
||||
g_free (filename);
|
||||
g_object_unref (row);
|
||||
}
|
||||
|
||||
static void
|
||||
populate_model (GtkTreeModel *model)
|
||||
selection_cb (GtkSingleSelection *sel,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
Demo *d = gtk_demos;
|
||||
GtkTreeListRow *row = gtk_single_selection_get_selected_item (sel);
|
||||
GtkDemo *demo = gtk_tree_list_row_get_item (row);
|
||||
|
||||
/* this code only supports 1 level of children. If we
|
||||
* want more we probably have to use a recursing function.
|
||||
*/
|
||||
while (d->title)
|
||||
if (demo->filename)
|
||||
load_file (demo->name, demo->filename);
|
||||
|
||||
gtk_window_set_title (GTK_WINDOW (toplevel), demo->title);
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_demo_model (void)
|
||||
{
|
||||
GListStore *store = g_list_store_new (GTK_TYPE_DEMO);
|
||||
DemoData *demo = gtk_demos;
|
||||
|
||||
while (demo->title)
|
||||
{
|
||||
Demo *children = d->children;
|
||||
GtkTreeIter iter;
|
||||
GtkDemo *d = GTK_DEMO (g_object_new (GTK_TYPE_DEMO, NULL));
|
||||
DemoData *children = demo->children;
|
||||
|
||||
gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
|
||||
d->name = demo->name;
|
||||
d->title = demo->title;
|
||||
d->filename = demo->filename;
|
||||
d->func = demo->func;
|
||||
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model),
|
||||
&iter,
|
||||
NAME_COLUMN, d->name,
|
||||
TITLE_COLUMN, d->title,
|
||||
FILENAME_COLUMN, d->filename,
|
||||
FUNC_COLUMN, d->func,
|
||||
STYLE_COLUMN, PANGO_STYLE_NORMAL,
|
||||
-1);
|
||||
g_list_store_append (store, d);
|
||||
|
||||
d++;
|
||||
|
||||
if (!children)
|
||||
continue;
|
||||
|
||||
while (children->title)
|
||||
if (children)
|
||||
{
|
||||
GtkTreeIter child_iter;
|
||||
d->children_model = G_LIST_MODEL (g_list_store_new (GTK_TYPE_DEMO));
|
||||
|
||||
gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &iter);
|
||||
while (children->title)
|
||||
{
|
||||
GtkDemo *child = GTK_DEMO (g_object_new (GTK_TYPE_DEMO, NULL));
|
||||
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model),
|
||||
&child_iter,
|
||||
NAME_COLUMN, children->name,
|
||||
TITLE_COLUMN, children->title,
|
||||
FILENAME_COLUMN, children->filename,
|
||||
FUNC_COLUMN, children->func,
|
||||
STYLE_COLUMN, PANGO_STYLE_NORMAL,
|
||||
-1);
|
||||
child->name = children->name;
|
||||
child->title = children->title;
|
||||
child->filename = children->filename;
|
||||
child->func = children->func;
|
||||
|
||||
children++;
|
||||
g_list_store_append (G_LIST_STORE (d->children_model), child);
|
||||
children++;
|
||||
}
|
||||
}
|
||||
|
||||
demo++;
|
||||
}
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
static void
|
||||
row_activated_cb (GtkWidget *tree_view,
|
||||
GtkTreePath *path,
|
||||
GtkTreeViewColumn *column)
|
||||
static GListModel *
|
||||
get_child_model (gpointer item,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkWidget *window;
|
||||
GtkTreeModel *model;
|
||||
GtkDemo *demo = item;
|
||||
|
||||
window = GTK_WIDGET (gtk_widget_get_root (tree_view));
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
|
||||
gtk_tree_model_get_iter (model, &iter, path);
|
||||
if (demo->children_model)
|
||||
return g_object_ref (G_LIST_MODEL (demo->children_model));
|
||||
|
||||
run_example_for_row (window, model, &iter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
activate (GApplication *app)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
GtkWindow *window;
|
||||
GtkWidget *widget;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
GListModel *listmodel;
|
||||
GtkTreeListModel *treemodel;
|
||||
GtkWidget *window, *listview;
|
||||
|
||||
static GActionEntry win_entries[] = {
|
||||
{ "run", activate_run, NULL, NULL, NULL }
|
||||
@ -1003,33 +1023,35 @@ activate (GApplication *app)
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/ui/main.ui");
|
||||
|
||||
window = (GtkWindow *)gtk_builder_get_object (builder, "window");
|
||||
gtk_application_add_window (GTK_APPLICATION (app), window);
|
||||
window = (GtkWidget *)gtk_builder_get_object (builder, "window");
|
||||
gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (window));
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (window),
|
||||
win_entries, G_N_ELEMENTS (win_entries),
|
||||
window);
|
||||
|
||||
notebook = (GtkWidget *)gtk_builder_get_object (builder, "notebook");
|
||||
notebook = GTK_WIDGET (gtk_builder_get_object (builder, "notebook"));
|
||||
|
||||
info_view = (GtkWidget *)gtk_builder_get_object (builder, "info-textview");
|
||||
source_view = (GtkWidget *)gtk_builder_get_object (builder, "source-textview");
|
||||
treeview = (GtkWidget *)gtk_builder_get_object (builder, "treeview");
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
|
||||
info_view = GTK_WIDGET (gtk_builder_get_object (builder, "info-textview"));
|
||||
source_view = GTK_WIDGET (gtk_builder_get_object (builder, "source-textview"));
|
||||
toplevel = GTK_WIDGET (window);
|
||||
listview = GTK_WIDGET (gtk_builder_get_object (builder, "listview"));
|
||||
g_signal_connect (listview, "activate", G_CALLBACK (activate_cb), window);
|
||||
|
||||
load_file (gtk_demos[0].name, gtk_demos[0].filename);
|
||||
|
||||
populate_model (model);
|
||||
listmodel = create_demo_model ();
|
||||
treemodel = gtk_tree_list_model_new (FALSE,
|
||||
G_LIST_MODEL (listmodel),
|
||||
FALSE,
|
||||
get_child_model,
|
||||
NULL,
|
||||
NULL);
|
||||
selection = gtk_single_selection_new (G_LIST_MODEL (treemodel));
|
||||
g_signal_connect (selection, "notify::selected-item", G_CALLBACK (selection_cb), NULL);
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview),
|
||||
G_LIST_MODEL (selection));
|
||||
|
||||
g_signal_connect (treeview, "row-activated", G_CALLBACK (row_activated_cb), model);
|
||||
|
||||
widget = (GtkWidget *)gtk_builder_get_object (builder, "treeview-selection");
|
||||
g_signal_connect (widget, "changed", G_CALLBACK (selection_cb), model);
|
||||
|
||||
gtk_tree_model_get_iter_first (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)), &iter);
|
||||
gtk_tree_selection_select_iter (GTK_TREE_SELECTION (widget), &iter);
|
||||
|
||||
gtk_tree_view_collapse_all (GTK_TREE_VIEW (treeview));
|
||||
award ("demo-start");
|
||||
|
||||
gtk_widget_show (GTK_WIDGET (window));
|
||||
|
||||
@ -1046,7 +1068,7 @@ auto_quit (gpointer data)
|
||||
static void
|
||||
list_demos (void)
|
||||
{
|
||||
Demo *d, *c;
|
||||
DemoData *d, *c;
|
||||
|
||||
d = gtk_demos;
|
||||
|
||||
@ -1073,7 +1095,7 @@ command_line (GApplication *app,
|
||||
const gchar *name = NULL;
|
||||
gboolean autoquit = FALSE;
|
||||
gboolean list = FALSE;
|
||||
Demo *d, *c;
|
||||
DemoData *d, *c;
|
||||
GDoDemoFunc func = 0;
|
||||
GtkWidget *window, *demo;
|
||||
|
||||
|
@ -65,30 +65,12 @@
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="min-content-width">150</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="treeview">
|
||||
<property name="model">treestore</property>
|
||||
<property name="headers-visible">0</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection">
|
||||
<property name="mode">browse</property>
|
||||
<object class="GtkListView" id="listview">
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="resource">/ui/main-listitem.ui</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="style">4</attribute>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererText">
|
||||
<property name="text"> </property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -3,6 +3,7 @@
|
||||
demos = files([
|
||||
'application_demo.c',
|
||||
'assistant.c',
|
||||
'awardview.c',
|
||||
'builder.c',
|
||||
'clipboard.c',
|
||||
'combobox.c',
|
||||
@ -41,6 +42,12 @@ demos = files([
|
||||
'listbox.c',
|
||||
'flowbox.c',
|
||||
'list_store.c',
|
||||
'listview_applauncher.c',
|
||||
'listview_clocks.c',
|
||||
'listview_filebrowser.c',
|
||||
'listview_minesweeper.c',
|
||||
'listview_settings.c',
|
||||
'listview_weather.c',
|
||||
'markup.c',
|
||||
'modelbutton.c',
|
||||
'overlay.c',
|
||||
@ -84,6 +91,7 @@ demos = files([
|
||||
gtkdemo_deps = [ libgtk_dep, ]
|
||||
|
||||
extra_demo_sources = files(['main.c',
|
||||
'award.c',
|
||||
'gtkfishbowl.c',
|
||||
'fontplane.c',
|
||||
'gtkgears.c',
|
||||
@ -95,7 +103,7 @@ extra_demo_sources = files(['main.c',
|
||||
if harfbuzz_dep.found() and pangoft_dep.found()
|
||||
demos += files('font_features.c')
|
||||
extra_demo_sources += files(['script-names.c', 'language-names.c'])
|
||||
gtkdemo_deps += [ harfbuzz_dep, ]
|
||||
gtkdemo_deps += [ harfbuzz_dep, epoxy_dep ]
|
||||
endif
|
||||
|
||||
if os_unix
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "award.h"
|
||||
|
||||
static GtkWidget *entry;
|
||||
static GtkWidget *entry2;
|
||||
static GtkWidget *button;
|
||||
@ -25,6 +27,18 @@ update_button (GObject *object,
|
||||
|
||||
gtk_widget_set_sensitive (button,
|
||||
text[0] != '\0' && g_str_equal (text, text2));
|
||||
|
||||
if (g_str_equal (text, text2) &&
|
||||
g_ascii_strcasecmp (text, "12345") == 0)
|
||||
award ("password-best");
|
||||
}
|
||||
|
||||
static void
|
||||
button_pressed (GtkButton *widget,
|
||||
GtkWidget *window)
|
||||
{
|
||||
award ("password-correct");
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
@ -74,7 +88,7 @@ do_password_entry (GtkWidget *do_widget)
|
||||
|
||||
button = gtk_button_new_with_mnemonic ("_Done");
|
||||
gtk_widget_add_css_class (button, "suggested-action");
|
||||
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_destroy), window);
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (button_pressed), window);
|
||||
gtk_widget_set_sensitive (button, FALSE);
|
||||
gtk_header_bar_pack_end (GTK_HEADER_BAR (header), button);
|
||||
|
||||
|
@ -11,6 +11,9 @@
|
||||
#include "puzzlepiece.h"
|
||||
#include "paintable.h"
|
||||
|
||||
/* Give out awards */
|
||||
#include "award.h"
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
static GtkWidget *frame = NULL;
|
||||
static GtkWidget *choices = NULL;
|
||||
@ -156,6 +159,14 @@ check_solved (GtkWidget *grid)
|
||||
picture = gtk_grid_get_child_at (GTK_GRID (grid), pos_x, pos_y);
|
||||
gtk_picture_set_paintable (GTK_PICTURE (picture), piece);
|
||||
|
||||
/* Hand out a bunch of awards
|
||||
*/
|
||||
award ("puzzle-solve");
|
||||
if ((gdk_paintable_get_flags (piece) & GDK_PAINTABLE_STATIC_CONTENTS) == 0)
|
||||
award ("puzzle-solve-animated");
|
||||
if (height * width > 20)
|
||||
award ("puzzle-solve-large");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -390,6 +401,18 @@ add_choice (GtkWidget *container,
|
||||
gtk_flow_box_insert (GTK_FLOW_BOX (container), icon, -1);
|
||||
}
|
||||
|
||||
static void
|
||||
widget_destroyed (gpointer data,
|
||||
GObject *widget)
|
||||
{
|
||||
if (data)
|
||||
*(gpointer *) data = NULL;
|
||||
|
||||
if (!solved)
|
||||
award ("puzzle-give-up");
|
||||
}
|
||||
|
||||
|
||||
GtkWidget *
|
||||
do_sliding_puzzle (GtkWidget *do_widget)
|
||||
{
|
||||
@ -460,7 +483,7 @@ do_sliding_puzzle (GtkWidget *do_widget)
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Sliding Puzzle");
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), header);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
g_object_weak_ref (G_OBJECT (window), widget_destroyed, &window);
|
||||
|
||||
frame = gtk_aspect_frame_new (0.5, 0.5, (float) gdk_paintable_get_intrinsic_aspect_ratio (puzzle), FALSE);
|
||||
gtk_window_set_child (GTK_WINDOW (window), frame);
|
||||
|
@ -48,19 +48,51 @@
|
||||
|
||||
<chapter id="Lists">
|
||||
<title>GListModel support</title>
|
||||
<xi:include href="xml/gtkexpression.xml" />
|
||||
<xi:include href="xml/gtkfilterlistmodel.xml" />
|
||||
<section>
|
||||
<xi:include href="xml/gtkfilter.xml" />
|
||||
<xi:include href="xml/gtkcustomfilter.xml" />
|
||||
<xi:include href="xml/gtkstringfilter.xml" />
|
||||
<xi:include href="xml/gtkmultifilter.xml" />
|
||||
</section>
|
||||
<xi:include href="xml/gtkflattenlistmodel.xml" />
|
||||
<xi:include href="xml/gtkmaplistmodel.xml" />
|
||||
<xi:include href="xml/gtkslicelistmodel.xml" />
|
||||
<xi:include href="xml/gtksortlistmodel.xml" />
|
||||
<section>
|
||||
<xi:include href="xml/gtksorter.xml" />
|
||||
<xi:include href="xml/gtkcustomsorter.xml" />
|
||||
<xi:include href="xml/gtkstringsorter.xml" />
|
||||
<xi:include href="xml/gtknumericsorter.xml" />
|
||||
<xi:include href="xml/gtkmultisorter.xml" />
|
||||
</section>
|
||||
<xi:include href="xml/gtkselectionmodel.xml" />
|
||||
<xi:include href="xml/gtknoselection.xml" />
|
||||
<xi:include href="xml/gtksingleselection.xml" />
|
||||
<xi:include href="xml/gtkdirectorylist.xml" />
|
||||
</chapter>
|
||||
|
||||
<chapter id="ListContainers">
|
||||
<title>List-based Widgets</title>
|
||||
<xi:include href="section-list-widget.xml"/>
|
||||
<xi:include href="xml/gtklistitem.xml" />
|
||||
<xi:include href="xml/gtklistitemfactory.xml" />
|
||||
<section>
|
||||
<xi:include href="xml/gtksignallistitemfactory.xml" />
|
||||
<xi:include href="xml/gtkbuilderlistitemfactory.xml" />
|
||||
</section>
|
||||
<xi:include href="xml/gtklistview.xml" />
|
||||
<xi:include href="xml/gtkgridview.xml" />
|
||||
<xi:include href="xml/gtkcolumnview.xml" />
|
||||
<xi:include href="xml/gtkcolumnviewcolumn.xml" />
|
||||
</chapter>
|
||||
|
||||
<chapter id="Trees">
|
||||
<xi:include href="xml/gtktreelistrow.xml" />
|
||||
<xi:include href="xml/gtktreelistmodel.xml" />
|
||||
<xi:include href="xml/gtktreelistrowsorter.xml" />
|
||||
<xi:include href="xml/gtktreeexpander.xml" />
|
||||
</chapter>
|
||||
|
||||
<chapter id="Application">
|
||||
|
@ -392,6 +392,179 @@ gtk_single_selection_set_can_unselect
|
||||
gtk_single_selection_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtklistitem</FILE>
|
||||
<TITLE>GtkListItem</TITLE>
|
||||
GtkListItem
|
||||
gtk_list_item_get_item
|
||||
gtk_list_item_get_position
|
||||
gtk_list_item_get_child
|
||||
gtk_list_item_set_child
|
||||
gtk_list_item_get_selected
|
||||
gtk_list_item_get_selectable
|
||||
gtk_list_item_set_selectable
|
||||
<SUBSECTION Standard>
|
||||
GTK_LIST_ITEM
|
||||
GTK_LIST_ITEM_CLASS
|
||||
GTK_LIST_ITEM_GET_CLASS
|
||||
GTK_IS_LIST_ITEM
|
||||
GTK_IS_LIST_ITEM_CLASS
|
||||
GTK_TYPE_LIST_ITEM
|
||||
<SUBSECTION Private>
|
||||
gtk_list_item_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtklistitemfactory</FILE>
|
||||
<TITLE>GtkListItemFactory</TITLE>
|
||||
GtkListItemFactory
|
||||
<SUBSECTION Standard>
|
||||
GTK_LIST_ITEM_FACTORY
|
||||
GTK_LIST_ITEM_FACTORY_CLASS
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS
|
||||
GTK_IS_LIST_ITEM_FACTORY
|
||||
GTK_IS_LIST_ITEM_FACTORY_CLASS
|
||||
GTK_TYPE_LIST_ITEM_FACTORY
|
||||
<SUBSECTION Private>
|
||||
gtk_list_item_factory_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkbuilderlistitemfactory</FILE>
|
||||
<TITLE>GtkBuilderListItemFactory</TITLE>
|
||||
GtkBuilderListItemFactory
|
||||
gtk_builder_list_item_factory_new_from_bytes
|
||||
gtk_builder_list_item_factory_new_from_resource
|
||||
gtk_builder_list_item_factory_get_bytes
|
||||
gtk_builder_list_item_factory_get_resource
|
||||
gtk_builder_list_item_factory_get_scope
|
||||
<SUBSECTION Standard>
|
||||
GTK_BUILDER_LIST_ITEM_FACTORY
|
||||
GTK_BUILDER_LIST_ITEM_FACTORY_CLASS
|
||||
GTK_BUILDER_LIST_ITEM_FACTORY_GET_CLASS
|
||||
GTK_IS_BUILDER_LIST_ITEM_FACTORY
|
||||
GTK_IS_BUILDER_LIST_ITEM_FACTORY_CLASS
|
||||
GTK_TYPE_BUILDER_LIST_ITEM_FACTORY
|
||||
<SUBSECTION Private>
|
||||
gtk_builder_list_item_factory_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtksignallistitemfactory</FILE>
|
||||
<TITLE>GtkSignalListItemFactory</TITLE>
|
||||
GtkSignalListItemFactory
|
||||
gtk_signal_list_item_factory_new
|
||||
<SUBSECTION Standard>
|
||||
GTK_SIGNAL_LIST_ITEM_FACTORY
|
||||
GTK_SIGNAL_LIST_ITEM_FACTORY_CLASS
|
||||
GTK_SIGNAL_LIST_ITEM_FACTORY_GET_CLASS
|
||||
GTK_IS_SIGNAL_LIST_ITEM_FACTORY
|
||||
GTK_IS_SIGNAL_LIST_ITEM_FACTORY_CLASS
|
||||
GTK_TYPE_SIGNAL_LIST_ITEM_FACTORY
|
||||
<SUBSECTION Private>
|
||||
gtk_signal_list_item_factory_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtklistview</FILE>
|
||||
<TITLE>GtkListView</TITLE>
|
||||
GtkListView
|
||||
gtk_list_view_new
|
||||
gtk_list_view_new_with_factory
|
||||
gtk_list_view_set_factory
|
||||
gtk_list_view_get_factory
|
||||
gtk_list_view_set_model
|
||||
gtk_list_view_get_model
|
||||
gtk_list_view_set_show_separators
|
||||
gtk_list_view_get_show_separators
|
||||
gtk_list_view_set_single_click_activate
|
||||
gtk_list_view_get_single_click_activate
|
||||
<SUBSECTION Standard>
|
||||
GTK_LIST_VIEW
|
||||
GTK_LIST_VIEW_CLASS
|
||||
GTK_LIST_VIEW_GET_CLASS
|
||||
GTK_IS_LIST_VIEW
|
||||
GTK_IS_LIST_VIEW_CLASS
|
||||
GTK_TYPE_LIST_VIEW
|
||||
<SUBSECTION Private>
|
||||
gtk_list_view_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkcolumnview</FILE>
|
||||
<TITLE>GtkColumnView</TITLE>
|
||||
GtkColumnView
|
||||
gtk_column_view_new
|
||||
gtk_column_view_append_column
|
||||
gtk_column_view_remove_column
|
||||
gtk_column_view_get_columns
|
||||
gtk_column_view_get_model
|
||||
gtk_column_view_set_model
|
||||
gtk_column_view_get_sorter
|
||||
gtk_column_view_get_show_separators
|
||||
gtk_column_view_set_show_separators
|
||||
gtk_column_view_sort_by_column
|
||||
gtk_column_view_set_single_click_activate
|
||||
gtk_column_view_get_single_click_activate
|
||||
<SUBSECTION Standard>
|
||||
GTK_COLUMN_VIEW
|
||||
GTK_COLUMN_VIEW_CLASS
|
||||
GTK_COLUMN_VIEW_GET_CLASS
|
||||
GTK_IS_COLUMN_VIEW
|
||||
GTK_IS_COLUMN_VIEW_CLASS
|
||||
GTK_TYPE_COLUMN_VIEW
|
||||
<SUBSECTION Private>
|
||||
gtk_column_view_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkcolumnviewcolumn</FILE>
|
||||
<TITLE>GtkColumnViewColumn</TITLE>
|
||||
GtkColumnViewColumn
|
||||
gtk_column_view_column_new
|
||||
gtk_column_view_column_new_with_factory
|
||||
gtk_column_view_column_get_column_view
|
||||
gtk_column_view_column_set_factory
|
||||
gtk_column_view_column_get_factory
|
||||
gtk_column_view_column_set_title
|
||||
gtk_column_view_column_get_title
|
||||
gtk_column_view_column_set_sorter
|
||||
gtk_column_view_column_get_sorter
|
||||
<SUBSECTION Standard>
|
||||
GTK_COLUMN_VIEW_COLUMN
|
||||
GTK_COLUMN_VIEW_COLUMN_CLASS
|
||||
GTK_COLUMN_VIEW_COLUMN_GET_CLASS
|
||||
GTK_IS_COLUMN_VIEW_COLUMN
|
||||
GTK_IS_COLUMN_VIEW_COLUMN_CLASS
|
||||
GTK_TYPE_COLUMN_VIEW_COLUMN
|
||||
<SUBSECTION Private>
|
||||
gtk_column_view_column_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkgridview</FILE>
|
||||
<TITLE>GtkGridView</TITLE>
|
||||
GtkGridView
|
||||
gtk_grid_view_new
|
||||
gtk_grid_view_set_model
|
||||
gtk_grid_view_get_model
|
||||
gtk_grid_view_set_max_columns
|
||||
gtk_grid_view_get_max_columns
|
||||
gtk_grid_view_set_min_columns
|
||||
gtk_grid_view_get_min_columns
|
||||
gtk_grid_view_set_single_click_activate
|
||||
gtk_grid_view_get_single_click_activate
|
||||
<SUBSECTION Standard>
|
||||
GTK_GRID_VIEW
|
||||
GTK_GRID_VIEW_CLASS
|
||||
GTK_GRID_VIEW_GET_CLASS
|
||||
GTK_IS_GRID_VIEW
|
||||
GTK_IS_GRID_VIEW_CLASS
|
||||
GTK_TYPE_GRID_VIEW
|
||||
<SUBSECTION Private>
|
||||
gtk_grid_view_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkbuildable</FILE>
|
||||
GtkBuildable
|
||||
@ -1160,6 +1333,92 @@ GTK_TYPE_FILE_FILTER
|
||||
gtk_file_filter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkdirectorylist</FILE>
|
||||
<TITLE>GtkDirectoryList</TITLE>
|
||||
GtkDirectoryList
|
||||
gtk_directory_list_new
|
||||
gtk_directory_list_get_attributes
|
||||
gtk_directory_list_set_attributes
|
||||
gtk_directory_list_get_file
|
||||
gtk_directory_list_set_file
|
||||
gtk_directory_list_get_io_priority
|
||||
gtk_directory_list_set_io_priority
|
||||
gtk_directory_list_is_loading
|
||||
gtk_directory_list_get_error
|
||||
<SUBSECTION Standard>
|
||||
GTK_DIRECTORY_LIST
|
||||
GTK_IS_DIRECTORY_LIST
|
||||
GTK_TYPE_DIRECTORY_LIST
|
||||
GTK_DIRECTORY_LIST_CLASS
|
||||
GTK_IS_DIRECTORY_LIST_CLASS
|
||||
GTK_DIRECTORY_LIST_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_directory_list_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkfilter</FILE>
|
||||
<TITLE>GtkFilter</TITLE>
|
||||
GtkFilter
|
||||
gtk_filter_match
|
||||
gtk_filter_get_strictness
|
||||
<SUBSECTION>
|
||||
GtkFilterChange
|
||||
gtk_filter_changed
|
||||
<SUBSECTION>
|
||||
gtk_custom_filter_new
|
||||
<SUBSECTION Standard>
|
||||
GTK_FILTER
|
||||
GTK_IS_FILTER
|
||||
GTK_TYPE_FILTER
|
||||
GTK_FILTER_CLASS
|
||||
GTK_IS_FILTER_CLASS
|
||||
GTK_FILTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_filter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkcustomfilter</FILE>
|
||||
<TITLE>GtkCustomFilter</TITLE>
|
||||
GtkCustomFilter
|
||||
GtkCustomFilterFunc
|
||||
gtk_custom_filter_new
|
||||
<SUBSECTION Standard>
|
||||
GTK_CUSTOM_FILTER
|
||||
GTK_IS_CUSTOM_FILTER
|
||||
GTK_TYPE_CUSTOM_FILTER
|
||||
GTK_CUSTOM_FILTER_CLASS
|
||||
GTK_IS_CUSTOM_FILTER_CLASS
|
||||
GTK_CUSTOM_FILTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_custom_filter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkmultifilter</FILE>
|
||||
<TITLE>GtkMultiFilter</TITLE>
|
||||
GtkMultiFilter
|
||||
gtk_multi_filter_append
|
||||
gtk_multi_filter_remove
|
||||
<SUBSECTION>
|
||||
GtkAnyFilter
|
||||
gtk_any_filter_new
|
||||
<SUBSECTION>
|
||||
GtkEveryFilter
|
||||
gtk_every_filter_new
|
||||
<SUBSECTION Standard>
|
||||
GTK_CUSTOM_FILTER
|
||||
GTK_IS_CUSTOM_FILTER
|
||||
GTK_TYPE_CUSTOM_FILTER
|
||||
GTK_CUSTOM_FILTER_CLASS
|
||||
GTK_IS_CUSTOM_FILTER_CLASS
|
||||
GTK_CUSTOM_FILTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_custom_filter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkfilterlistmodel</FILE>
|
||||
<TITLE>GtkFilterListModel</TITLE>
|
||||
@ -1169,9 +1428,8 @@ gtk_filter_list_model_new
|
||||
gtk_filter_list_model_new_for_type
|
||||
gtk_filter_list_model_set_model
|
||||
gtk_filter_list_model_get_model
|
||||
gtk_filter_list_model_set_filter_func
|
||||
gtk_filter_list_model_has_filter
|
||||
gtk_filter_list_model_refilter
|
||||
gtk_filter_list_model_set_filter
|
||||
gtk_filter_list_model_get_filter
|
||||
<SUBSECTION Standard>
|
||||
GTK_FILTER_LIST_MODEL
|
||||
GTK_IS_FILTER_LIST_MODEL
|
||||
@ -2336,17 +2594,123 @@ GTK_SLICE_LIST_MODEL_GET_CLASS
|
||||
gtk_slice_list_model_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtksorter</FILE>
|
||||
<TITLE>GtkSorter</TITLE>
|
||||
GtkSorter
|
||||
GtkSorterOrder
|
||||
GtkSorterChange
|
||||
gtk_sorter_compare
|
||||
gtk_sorter_get_order
|
||||
gtk_sorter_changed
|
||||
<SUBSECTION Standard>
|
||||
GTK_SORTER
|
||||
GTK_IS_SORTER
|
||||
GTK_TYPE_SORTER
|
||||
GTK_SORTER_CLASS
|
||||
GTK_IS_SORTER_CLASS
|
||||
GTK_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkstringsorter</FILE>
|
||||
<TITLE>GtkStringSorter</TITLE>
|
||||
GtkStringSorter
|
||||
gtk_string_sorter_new
|
||||
gtk_string_sorter_get_expression
|
||||
gtk_string_sorter_set_expression
|
||||
gtk_string_sorter_get_ignore_case
|
||||
gtk_string_sorter_set_ignore_case
|
||||
<SUBSECTION Standard>
|
||||
GTK_STRING_SORTER
|
||||
GTK_IS_STRING_SORTER
|
||||
GTK_TYPE_STRING_SORTER
|
||||
GTK_IS_STRING_SORTER_CLASS
|
||||
GTK_STRING_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_string_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtknumericsorter</FILE>
|
||||
<TITLE>GtkNumericSorter</TITLE>
|
||||
GtkNumericSorter
|
||||
gtk_numeric_sorter_new
|
||||
gtk_numeric_sorter_get_expression
|
||||
gtk_numeric_sorter_set_expression
|
||||
gtk_numeric_sorter_get_sort_order
|
||||
gtk_numeric_sorter_set_sort_order
|
||||
<SUBSECTION Standard>
|
||||
GTK_NUMERIC_SORTER
|
||||
GTK_IS_NUMERIC_SORTER
|
||||
GTK_TYPE_NUMERIC_SORTER
|
||||
GTK_IS_NUMERIC_SORTER_CLASS
|
||||
GTK_NUMERIC_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_numeric_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkcustomsorter</FILE>
|
||||
<TITLE>GtkCustomSorter</TITLE>
|
||||
GtkCustomSorter
|
||||
gtk_custom_sorter_new
|
||||
<SUBSECTION Standard>
|
||||
GTK_CUSTOM_SORTER
|
||||
GTK_IS_CUSTOM_SORTER
|
||||
GTK_TYPE_CUSTOM_SORTER
|
||||
GTK_IS_CUSTOM_SORTER_CLASS
|
||||
GTK_CUSTOM_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_custom_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkmultisorter</FILE>
|
||||
<TITLE>GtkMultiSorter</TITLE>
|
||||
GtkMultiSorter
|
||||
gtk_multi_sorter_new
|
||||
gtk_multi_sorter_append
|
||||
gtk_multi_sorter_remove
|
||||
<SUBSECTION Standard>
|
||||
GTK_MULTI_SORTER
|
||||
GTK_IS_MULTI_SORTER
|
||||
GTK_TYPE_MULTI_SORTER
|
||||
GTK_IS_MULTI_SORTER_CLASS
|
||||
GTK_MULTI_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_multi_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtktreelistrowsorter</FILE>
|
||||
<TITLE>GtkTreeListRowSorter</TITLE>
|
||||
GtkTreeListRowSorter
|
||||
gtk_tree_list_row_sorter_new
|
||||
gtk_tree_list_row_sorter_get_sorter
|
||||
gtk_tree_list_row_sorter_set_sorter
|
||||
<SUBSECTION Standard>
|
||||
GTK_TREE_LIST_ROW_SORTER
|
||||
GTK_IS_TREE_LIST_ROW_SORTER
|
||||
GTK_TYPE_TREE_LIST_ROW_SORTER
|
||||
GTK_IS_TREE_LIST_ROW_SORTER_CLASS
|
||||
GTK_TREE_LIST_ROW_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_tree_list_row_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtksortlistmodel</FILE>
|
||||
<TITLE>GtkSortListModel</TITLE>
|
||||
GtkSortListModel
|
||||
gtk_sort_list_model_new
|
||||
gtk_sort_list_model_new_for_type
|
||||
gtk_sort_list_model_set_sort_func
|
||||
gtk_sort_list_model_has_sort
|
||||
gtk_sort_list_model_set_sorter
|
||||
gtk_sort_list_model_get_sorter
|
||||
gtk_sort_list_model_set_model
|
||||
gtk_sort_list_model_get_model
|
||||
gtk_sort_list_model_resort
|
||||
<SUBSECTION Standard>
|
||||
GTK_SORT_LIST_MODEL
|
||||
GTK_IS_SORT_LIST_MODEL
|
||||
@ -2932,6 +3296,26 @@ GTK_TREE_LIST_MODEL_GET_CLASS
|
||||
gtk_tree_list_row_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtktreeexpander</FILE>
|
||||
<TITLE>GtkTreeExpander</TITLE>
|
||||
gtk_tree_expander_new
|
||||
gtk_tree_expander_get_child
|
||||
gtk_tree_expander_set_child
|
||||
gtk_tree_expander_get_item
|
||||
gtk_tree_expander_get_list_row
|
||||
gtk_tree_expander_set_list_row
|
||||
<SUBSECTION Standard>
|
||||
GTK_TREE_EXPANDER
|
||||
GTK_IS_TREE_EXPANDER
|
||||
GTK_TYPE_TREE_EXPANDER
|
||||
GTK_TREE_EXPANDER_CLASS
|
||||
GTK_IS_TREE_EXPANDER_CLASS
|
||||
GTK_TREE_EXPANDER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_tree_expander_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtktreemodel</FILE>
|
||||
<TITLE>GtkTreeModel</TITLE>
|
||||
@ -4553,6 +4937,8 @@ GtkDeleteType
|
||||
GtkDirectionType
|
||||
GtkJustification
|
||||
GtkMovementStep
|
||||
GtkOrdering
|
||||
gtk_ordering_from_cmpfunc
|
||||
GtkOrientation
|
||||
GtkPackType
|
||||
GtkPositionType
|
||||
@ -7004,6 +7390,56 @@ gtk_overlay_layout_child_get_clip_overlay
|
||||
GTK_TYPE_OVERLAY_LAYOUT
|
||||
gtk_overlay_layout_get_type
|
||||
GTK_TYPE_OVERLAY_LAYOUT_CHLD
|
||||
gtk_overlay_layout_child_
|
||||
get_type
|
||||
gtk_overlay_layout_child_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkexpression</FILE>
|
||||
GtkExpression
|
||||
GtkExpressionWatch
|
||||
GtkExpressionNotify
|
||||
gtk_expression_ref
|
||||
gtk_expression_unref
|
||||
gtk_expression_get_value_type
|
||||
gtk_expression_is_static
|
||||
gtk_expression_evaluate
|
||||
gtk_expression_watch
|
||||
gtk_expression_bind
|
||||
gtk_expression_watch_ref
|
||||
gtk_expression_watch_unref
|
||||
gtk_expression_watch_evaluate
|
||||
gtk_expression_watch_unwatch
|
||||
|
||||
<SUBSECTION>
|
||||
gtk_property_expression_new
|
||||
gtk_property_expression_new_for_pspec
|
||||
gtk_constant_expression_new
|
||||
gtk_constant_expression_new_for_value
|
||||
gtk_object_expression_new
|
||||
gtk_closure_expression_new
|
||||
gtk_cclosure_expression_new
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GTK_IS_EXPRESSION
|
||||
GTK_TYPE_EXPRESSION
|
||||
<SUBSECTION Private>
|
||||
gtk_expression_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkstringfilter</FILE>
|
||||
GtkStringFilter
|
||||
GtkStringFilterMatchMode
|
||||
gtk_string_filter_new
|
||||
gtk_string_filter_get_search
|
||||
gtk_string_filter_set_search
|
||||
gtk_string_filter_get_expression
|
||||
gtk_string_filter_set_expression
|
||||
gtk_string_filter_get_ignore_case
|
||||
gtk_string_filter_set_ignore_case
|
||||
gtk_string_filter_get_match_mode
|
||||
gtk_string_filter_set_match_mode
|
||||
|
||||
<SUBSECTION Private>
|
||||
gtk_string_filter_get_type
|
||||
</SECTION>
|
||||
|
@ -7,6 +7,7 @@ gtk_accessible_get_type
|
||||
gtk_actionable_get_type
|
||||
gtk_action_bar_get_type
|
||||
gtk_adjustment_get_type
|
||||
gtk_any_filter_get_type
|
||||
gtk_app_chooser_get_type
|
||||
gtk_app_chooser_button_get_type
|
||||
gtk_app_chooser_dialog_get_type
|
||||
@ -19,10 +20,11 @@ gtk_assistant_page_get_type
|
||||
gtk_bin_layout_get_type
|
||||
gtk_box_get_type
|
||||
gtk_box_layout_get_type
|
||||
gtk_buildable_get_type
|
||||
gtk_builder_cscope_get_type
|
||||
gtk_builder_get_type
|
||||
gtk_builder_list_item_factory_get_type
|
||||
gtk_builder_scope_get_type
|
||||
gtk_buildable_get_type
|
||||
gtk_button_get_type
|
||||
gtk_calendar_get_type
|
||||
gtk_cell_area_get_type
|
||||
@ -46,6 +48,8 @@ gtk_color_button_get_type
|
||||
gtk_color_chooser_get_type
|
||||
gtk_color_chooser_dialog_get_type
|
||||
gtk_color_chooser_widget_get_type
|
||||
gtk_column_view_get_type
|
||||
gtk_column_view_column_get_type
|
||||
gtk_combo_box_get_type
|
||||
gtk_combo_box_text_get_type
|
||||
gtk_constraint_get_type
|
||||
@ -53,7 +57,10 @@ gtk_constraint_guide_get_type
|
||||
gtk_constraint_layout_get_type
|
||||
gtk_constraint_target_get_type
|
||||
gtk_css_provider_get_type
|
||||
gtk_custom_filter_get_type
|
||||
gtk_custom_sorter_get_type
|
||||
gtk_dialog_get_type
|
||||
gtk_directory_list_get_type
|
||||
gtk_drag_icon_get_type
|
||||
gtk_drag_source_get_type
|
||||
gtk_drawing_area_get_type
|
||||
@ -70,12 +77,14 @@ gtk_event_controller_focus_get_type
|
||||
gtk_event_controller_legacy_get_type
|
||||
gtk_event_controller_motion_get_type
|
||||
gtk_event_controller_scroll_get_type
|
||||
gtk_every_filter_get_type
|
||||
gtk_expander_get_type
|
||||
gtk_file_chooser_button_get_type
|
||||
gtk_file_chooser_dialog_get_type
|
||||
gtk_file_chooser_get_type
|
||||
gtk_file_chooser_widget_get_type
|
||||
gtk_file_filter_get_type
|
||||
gtk_filter_get_type
|
||||
gtk_filter_list_model_get_type
|
||||
gtk_fixed_get_type
|
||||
gtk_fixed_layout_get_type
|
||||
@ -101,6 +110,7 @@ gtk_gl_area_get_type
|
||||
gtk_grid_get_type
|
||||
gtk_grid_layout_child_get_type
|
||||
gtk_grid_layout_get_type
|
||||
gtk_grid_view_get_type
|
||||
gtk_header_bar_get_type
|
||||
gtk_icon_theme_get_type
|
||||
gtk_icon_view_get_type
|
||||
@ -113,9 +123,12 @@ gtk_label_get_type
|
||||
gtk_layout_child_get_type
|
||||
gtk_layout_manager_get_type
|
||||
gtk_link_button_get_type
|
||||
gtk_list_item_get_type
|
||||
gtk_list_item_factory_get_type
|
||||
gtk_list_store_get_type
|
||||
gtk_list_box_get_type
|
||||
gtk_list_box_row_get_type
|
||||
gtk_list_view_get_type
|
||||
gtk_lock_button_get_type
|
||||
gtk_map_list_model_get_type
|
||||
gtk_media_controls_get_type
|
||||
@ -124,11 +137,14 @@ gtk_media_stream_get_type
|
||||
gtk_menu_button_get_type
|
||||
gtk_message_dialog_get_type
|
||||
gtk_mount_operation_get_type
|
||||
gtk_multi_filter_get_type
|
||||
gtk_multi_sorter_get_type
|
||||
gtk_native_get_type
|
||||
gtk_native_dialog_get_type
|
||||
gtk_no_selection_get_type
|
||||
gtk_notebook_get_type
|
||||
gtk_notebook_page_get_type
|
||||
gtk_numeric_sorter_get_type
|
||||
gtk_orientable_get_type
|
||||
gtk_overlay_get_type
|
||||
gtk_overlay_layout_get_type
|
||||
@ -174,11 +190,13 @@ gtk_shortcuts_window_get_type
|
||||
gtk_shortcuts_section_get_type
|
||||
gtk_shortcuts_group_get_type
|
||||
gtk_shortcuts_shortcut_get_type
|
||||
gtk_signal_list_item_factory_get_type
|
||||
gtk_single_selection_get_type
|
||||
gtk_size_group_get_type
|
||||
gtk_slice_list_model_get_type
|
||||
gtk_snapshot_get_type
|
||||
gtk_sort_list_model_get_type
|
||||
gtk_sorter_get_type
|
||||
gtk_spin_button_get_type
|
||||
gtk_spinner_get_type
|
||||
gtk_stack_get_type
|
||||
@ -186,6 +204,8 @@ gtk_stack_page_get_type
|
||||
gtk_stack_sidebar_get_type
|
||||
gtk_stack_switcher_get_type
|
||||
gtk_statusbar_get_type
|
||||
gtk_string_filter_get_type
|
||||
gtk_string_sorter_get_type
|
||||
gtk_switch_get_type
|
||||
gtk_level_bar_get_type
|
||||
gtk_style_context_get_type
|
||||
@ -203,6 +223,7 @@ gtk_tree_drag_dest_get_type
|
||||
gtk_tree_drag_source_get_type
|
||||
gtk_tree_list_model_get_type
|
||||
gtk_tree_list_row_get_type
|
||||
gtk_tree_list_row_sorter_get_type
|
||||
gtk_tree_model_filter_get_type
|
||||
gtk_tree_model_get_type
|
||||
gtk_tree_model_sort_get_type
|
||||
|
@ -87,12 +87,12 @@ Finally here's a quick list of equivalent functionality to look for when transit
|
||||
| #GtkListStore | #GListStore |
|
||||
| #GtkTreeStore | #GtkTreeListModel, #GtkTreeExpander |
|
||||
| #GtkTreeSelection | #GtkSelectionModel |
|
||||
| #GtkTreeViewColumn | FIXME: ColumnView |
|
||||
| #GtkTreeView | #GtkListView, FIXME: ColumnView |
|
||||
| #GtkCellView | ? |
|
||||
| #GtkComboBox | FIXME |
|
||||
| #GtkTreeViewColumn | #GtkColumnView |
|
||||
| #GtkTreeView | #GtkListView, #GtkColumnView |
|
||||
| #GtkCellView | |
|
||||
| #GtkComboBox | |
|
||||
| #GtkIconView | #GtkGridView |
|
||||
| #GtkTreeSortable | FIXME: ColumnView? |
|
||||
| #GtkTreeSortable | |
|
||||
| #GtkTreeModelSort | #GtkSortListModel |
|
||||
| #GtkTreeModelFilter | #GtkFilterListModel |
|
||||
| #GtkCellLayout | #GtkListItemFactory |
|
||||
|
@ -24,6 +24,13 @@ private_headers = [
|
||||
'gtkcolorplaneprivate.h',
|
||||
'gtkcolorscaleprivate.h',
|
||||
'gtkcolorswatchprivate.h',
|
||||
'gtkcolumnlistitemfactoryprivate.h',
|
||||
'gtkcolumnviewcellprivate.h',
|
||||
'gtkcolumnviewcolumnprivate.h',
|
||||
'gtkcolumnviewlayoutprivate.h',
|
||||
'gtkcolumnviewprivate.h',
|
||||
'gtkcolumnviewsorterprivate.h',
|
||||
'gtkcolumnviewtitleprivate.h',
|
||||
'gtkcomboboxprivate.h',
|
||||
'gtkconstraintexpressionprivate.h',
|
||||
'gtkconstraintguideprivate.h',
|
||||
@ -132,6 +139,11 @@ private_headers = [
|
||||
'gtkimmoduleprivate.h',
|
||||
'gtkkineticscrollingprivate.h',
|
||||
'gtklabelprivate.h',
|
||||
'gtklistbaseprivate.h',
|
||||
'gtklistitemprivate.h',
|
||||
'gtklistitemfactoryprivate.h',
|
||||
'gtklistitemmanagerprivate.h',
|
||||
'gtklistitemwidgetprivate.h',
|
||||
'gtklockbuttonprivate.h',
|
||||
'gtkmagnifierprivate.h',
|
||||
'gtkmediafileprivate.h',
|
||||
@ -374,6 +386,7 @@ expand_content_md_files = [
|
||||
'css-properties.md',
|
||||
'section-text-widget.md',
|
||||
'section-tree-widget.md',
|
||||
'section-list-widget.md',
|
||||
'question_index.md',
|
||||
]
|
||||
|
||||
|
104
docs/reference/gtk/section-list-widget.md
Normal file
104
docs/reference/gtk/section-list-widget.md
Normal file
@ -0,0 +1,104 @@
|
||||
# List Widget Overview {#ListWidget}
|
||||
|
||||
GTK provides powerful widgets to display and edit lists of data. This document gives an overview over the concepts and how they work together to allow developers to implement lists.
|
||||
|
||||
Lists are intended to be used whenever developers want to display lists of objects in roughly the same way.
|
||||
|
||||
Lists are perfectly fine to be used for very short list of only 2 or 3 elements, but generally scale fine to millions of items. Of course, the larger the list grows, the more care needs to be taken to choose the right data structures to keep things running well.
|
||||
|
||||
Lists are meant to be used with changing data, both with the items itself changing as well as the list adding and removing items. Of course, they work just as well with static data.
|
||||
|
||||
## Terminology
|
||||
|
||||
These terms are used throughout the documentation when talking about lists and you should be aware of what they refer to. These are often generic terms that have a specific meaning in this context.
|
||||
|
||||
**_Views_** or **_list widgets_** are the widgets that hold and manage the lists. Examples of thse widgets would be #GtkListView or #GtkGridView.
|
||||
|
||||
Views display data from a **_model_**. A model is a #GListModel and models can be provided in 3 ways or combinations thereof:
|
||||
|
||||
* Many list models implementations already exist. There are models that provide specific data, like #GtkDirectoryList. And there are models like #GListStore that allow building lists manually.
|
||||
|
||||
* Wrapping list models exists like #GtkFilterListModel or #GtkSortListModel that modify or adapt or combine other models.
|
||||
|
||||
* Last but not least, developers are encouraged to create their own #GListModel implementations. The interface is kept deliberately small to make this easy.
|
||||
|
||||
The same model can be used in multiple different views and wrapped with multiple different models at once.
|
||||
|
||||
The elements in a model are called **_items_**. All items are #GObjects.
|
||||
|
||||
Every item in a model has a **_position_** which is the unsigned integer that describes where in the model the item is located. This position can of course change as items are added or removed from the model.
|
||||
|
||||
It is important to be aware of the difference between items and positions because the mapping from position to item is not permanent, so developers should think about whether they want to track items or positions when working with models. Oftentimes some things are really hard to do one way but very easy the other way.
|
||||
|
||||
The other important part of a view is a **_factory_**. Each factory is a #GtkListItemFactory implementation that takes care of mapping the items of the model to widgets that can be shown in the view.
|
||||
|
||||
The way factories do this is by creating a **_listitem_** for each item that is currently in use. Listitems are always #GtkListItem objects. They are only ever created by GTK and provide information about what item they are meant to display.
|
||||
|
||||
Different factory implementations use various different methods to allow developers to add the right widgets to listitems and to link those widgets with the item managed by the listitem. Finding a suitable factory implementation for the data displayed, the programming language and development environment is an important task that can simplify setting up the view tremendously.
|
||||
|
||||
Views support selections via a **_selection model_**. A selection model is an implementation of the #GtkSelectionModel interface on top of the #GListModel interface that allows marking each item in a model as either selected or not selected. Just like regular models, this can be implemented either by implementing #GtkSelectionModel directly or by wrapping a model with one of the GTK models provided for this purposes, such as #GtkNoSelection or #GtkSingleSelection.
|
||||
The behavior of selection models - ie which items they allow selecting and what effect this has on other items - is completely up to the selection model. As such, single-selections, multi-selections or sharing selection state between different selection models and/or views is possible.
|
||||
The selection state of an item is exposed in the listitem via the GtkListItem:selected property.
|
||||
|
||||
Views and listitems also support activation. Activation means that double clicking or pressing enter while inside a focused row will cause the view to emit and activation signal such as GtkListView::activate. This provides an easy way to set up lists, but can also be turned off on listitems if undesired.
|
||||
|
||||
Both selections and activation are supported among other things via widget actions (FIXME: Link docs). This allows developers to add widgets to their lists that cause selections to change or to trigger activation via the #GtkActionable interface. For a list of all supported actions see the relevant documentation. (FIXME: where do we document actions and how to I link that?)
|
||||
|
||||
## Behind the scenes
|
||||
|
||||
While for short lists it is not a problem to instantiate widgets for every item in the model, once lists grow to thousands or millions of elements, this gets less feasible. Because of this, the views only create a limited amount of listitems and recycle them by binding them to new items. In general, views try to keep listitems available only for the items that can actually be seen on screen.
|
||||
|
||||
While this behavior allows views to scale effortlessly to huge lists, it has a few implication on what can be done with views. For example, it is not possible to query a view for a listitem used for a certain position - there might not be one and even if there is, that listitem might soon be recycled for a new position.
|
||||
|
||||
It is also important that developers save state they care about in the item and do not rely on the widgets they created as those widgets can be recycled for a new position at any time causing any state to be lost.
|
||||
|
||||
Another important requirement for views is that they need to know which items are not visible so they can be recycled. Views achieve that by implementing the #GtkScrollable interface and expecting to be placed directly into a #GtkScrolledWindow.
|
||||
|
||||
Of course, if you are only using models with few items, this is not important and you can treat views like any other widget. But if you use large lists and your performance suffers, you should be aware of this. Views also allow tuning the number of listitems they create such as with gtk_grid_view_set_max_columns(), and developers running into performance problems should definitely study the tradeoffs of those and experiment with them.
|
||||
|
||||
## Displaying trees
|
||||
|
||||
While #GtkTreeView provided builtin support for trees, the list widgets, and in particular #GListModel do not. This was a design choice because the common use case is displaying lists and not trees and it greatly simplifies the API interface provided.
|
||||
|
||||
However, GTK provides functionality to make trees look and behave like lists for the people who still want to display lists. This is achieved by using the #GtkTreeListModel model to flatten a tree into a list. The #GtkTreeExpander widget can then be used inside a listitem to allow users to expand and collapse rows and provide a similar experience to #GtkTreeView.
|
||||
|
||||
Developers should refer to those objects' API reference for more discussion on the topic.
|
||||
|
||||
## comparison to GtkTreeView
|
||||
|
||||
Developers familiar with #GtkTreeView may wonder how this way of doing lists compares to the way they know. This section will try to outline the similarities and differences between the two.
|
||||
|
||||
This new approach tries to provide roughly the same functionality as the old approach but often uses a very different approach to achieve these goals.
|
||||
|
||||
The main difference and one of the primary reasons for this new development is that items can be displayed using regular widgets and #GtkCellRenderer is no longer necessary. This allows all benefits that widgets provide, such as complex layout and animating widgets and not only makes cell renderers obsolete, but also #GtkCellArea.
|
||||
|
||||
The other big difference is the massive change to the data model. #GtkTreeModel was a rather complex interface for a tree data structure and #GListModel was deliberately designed to be a simple data structure for lists only. (See above (FIXME: link) for how to still do trees with this new model.) Another big change is that the new model allows for bulk changes via the #GListModel:items-changed signal while #GtkTreeModel only allows a single item to change at once.
|
||||
The goal here is of course to encourage implementation of custom list models.
|
||||
|
||||
Another consequence of the new model is that it is now easily possible to refer to the contents of a row in the model directly by keeping the item, while #GtkTreeRowReference was a very slow mechanism to achieve the same. And because the items are real objects, developers can make them emit change signals causing listitems and their children to update, which wasn't possible with #GtkTreeModel.
|
||||
|
||||
The selection handling is also different. While selections used to be managed via custom code in each widget, selection state is now meant to be managed by the selection models. In particular this allows for complex use cases with specialized requirements (FIXME: Can I add a shoutout to @mitch here because I vividly remember a huge discussion about GtkTreeView's selection behavior and the Gimp).
|
||||
|
||||
Finally here's a quick list of equivalent functionality to look for when transitioning code for easy lookup:
|
||||
|
||||
| old | new |
|
||||
| ------------------- | ----------------------------------- |
|
||||
| #GtkTreeModel | #GListModel |
|
||||
| #GtkTreePath | #guint position, #GtkTreeListRow |
|
||||
| #GtkTreeIter | #guint position |
|
||||
| GtkTreeRowReference | #GObject item |
|
||||
| #GtkListStore | #GListStore |
|
||||
| #GtkTreeStore | #GtkTreeListModel, #GtkTreeExpander |
|
||||
| #GtkTreeSelection | #GtkSelectionModel |
|
||||
| #GtkTreeViewColumn | #GtkColumnView |
|
||||
| #GtkTreeView | #GtkListView, #GtkColumnView |
|
||||
| #GtkCellView | |
|
||||
| #GtkComboBox | |
|
||||
| #GtkIconView | #GtkGridView |
|
||||
| #GtkTreeSortable | #GtkColumnView |
|
||||
| #GtkTreeModelSort | #GtkSortListModel |
|
||||
| #GtkTreeModelFilter | #GtkFilterListModel |
|
||||
| #GtkCellLayout | #GtkListItemFactory |
|
||||
| #GtkCellArea | #GtkWidget |
|
||||
| #GtkCellRenderer | #GtkWidget |
|
||||
|
@ -136,6 +136,16 @@ static double gdk_paintable_default_get_intrinsic_aspect_ratio (GdkPaintable *pa
|
||||
return (double) width / height;
|
||||
};
|
||||
|
||||
static void
|
||||
g_value_object_transform_value (const GValue *src_value,
|
||||
GValue *dest_value)
|
||||
{
|
||||
if (src_value->data[0].v_pointer && g_type_is_a (G_OBJECT_TYPE (src_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
|
||||
dest_value->data[0].v_pointer = g_object_ref (src_value->data[0].v_pointer);
|
||||
else
|
||||
dest_value->data[0].v_pointer = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_paintable_default_init (GdkPaintableInterface *iface)
|
||||
{
|
||||
@ -146,6 +156,9 @@ gdk_paintable_default_init (GdkPaintableInterface *iface)
|
||||
iface->get_intrinsic_height = gdk_paintable_default_get_intrinsic_height;
|
||||
iface->get_intrinsic_aspect_ratio = gdk_paintable_default_get_intrinsic_aspect_ratio;
|
||||
|
||||
g_value_register_transform_func (G_TYPE_OBJECT, GDK_TYPE_PAINTABLE, g_value_object_transform_value);
|
||||
g_value_register_transform_func (GDK_TYPE_PAINTABLE, G_TYPE_OBJECT, g_value_object_transform_value);
|
||||
|
||||
/**
|
||||
* GdkPaintable::invalidate-contents
|
||||
* @paintable: a #GdkPaintable
|
||||
|
@ -80,6 +80,42 @@ isinf (double x)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_DECL_ISNAN
|
||||
/* it seems of the supported compilers only
|
||||
* MSVC does not have isnan(), but it does
|
||||
* have _isnan() which does the same as isnan()
|
||||
*/
|
||||
static inline gboolean
|
||||
isnan (double x)
|
||||
{
|
||||
return _isnan (x);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_DECL_ISNANF
|
||||
#if 1
|
||||
#define isnanf(x) isnan(x)
|
||||
#else
|
||||
/* it seems of the supported compilers only
|
||||
* MSVC does not have isnanf(), but it does
|
||||
* have _isnanf() which does the same as isnanf()
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
static inline gboolean
|
||||
isnanf (float x)
|
||||
{
|
||||
return _isnanf (x);
|
||||
}
|
||||
#elif defined (__GNUC__)
|
||||
/* gcc has an intern function that it warns about when
|
||||
* using -Wshadow but no header properly declares it,
|
||||
* so we do it instead.
|
||||
*/
|
||||
extern int isnanf (float x);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef INFINITY
|
||||
/* define INFINITY for compilers that lack support for it */
|
||||
# ifdef HUGE_VALF
|
||||
|
@ -91,6 +91,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureSingle, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureSwipe, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureZoom, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGrid, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGridView, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkHeaderBar, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkIMContext, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkIMContextSimple, g_object_unref)
|
||||
@ -103,6 +104,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkInfoBar, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLevelBar, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLinkButton, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListStore, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListView, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLockButton, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkMenuButton, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkMessageDialog, g_object_unref)
|
||||
|
24
gtk/gtk.h
24
gtk/gtk.h
@ -52,6 +52,7 @@
|
||||
#include <gtk/gtkbox.h>
|
||||
#include <gtk/gtkbuildable.h>
|
||||
#include <gtk/gtkbuilder.h>
|
||||
#include <gtk/gtkbuilderlistitemfactory.h>
|
||||
#include <gtk/gtkbuilderscope.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkcalendar.h>
|
||||
@ -78,14 +79,19 @@
|
||||
#include <gtk/gtkcolorchooserdialog.h>
|
||||
#include <gtk/gtkcolorchooserwidget.h>
|
||||
#include <gtk/gtkcolorutils.h>
|
||||
#include <gtk/gtkcolumnview.h>
|
||||
#include <gtk/gtkcolumnviewcolumn.h>
|
||||
#include <gtk/gtkcombobox.h>
|
||||
#include <gtk/gtkcomboboxtext.h>
|
||||
#include <gtk/gtkconstraintlayout.h>
|
||||
#include <gtk/gtkconstraint.h>
|
||||
#include <gtk/gtkcoverflow.h>
|
||||
#include <gtk/gtkcssprovider.h>
|
||||
#include <gtk/gtkcustomlayout.h>
|
||||
#include <gtk/gtkcustomsorter.h>
|
||||
#include <gtk/gtkdebug.h>
|
||||
#include <gtk/gtkdialog.h>
|
||||
#include <gtk/gtkdirectorylist.h>
|
||||
#include <gtk/gtkdragicon.h>
|
||||
#include <gtk/gtkdragsource.h>
|
||||
#include <gtk/gtkdrawingarea.h>
|
||||
@ -105,6 +111,7 @@
|
||||
#include <gtk/gtkeventcontrollermotion.h>
|
||||
#include <gtk/gtkeventcontrollerscroll.h>
|
||||
#include <gtk/gtkexpander.h>
|
||||
#include <gtk/gtkexpression.h>
|
||||
#include <gtk/gtkfixed.h>
|
||||
#include <gtk/gtkfixedlayout.h>
|
||||
#include <gtk/gtkfilechooser.h>
|
||||
@ -113,7 +120,9 @@
|
||||
#include <gtk/gtkfilechoosernative.h>
|
||||
#include <gtk/gtkfilechooserwidget.h>
|
||||
#include <gtk/gtkfilefilter.h>
|
||||
#include <gtk/gtkfilter.h>
|
||||
#include <gtk/gtkfilterlistmodel.h>
|
||||
#include <gtk/gtkcustomfilter.h>
|
||||
#include <gtk/gtkflattenlistmodel.h>
|
||||
#include <gtk/gtkflowbox.h>
|
||||
#include <gtk/gtkfontbutton.h>
|
||||
@ -121,6 +130,7 @@
|
||||
#include <gtk/gtkfontchooserdialog.h>
|
||||
#include <gtk/gtkfontchooserwidget.h>
|
||||
#include <gtk/gtkframe.h>
|
||||
#include <gtk/gtkfunctionslistitemfactory.h>
|
||||
#include <gtk/gtkgesture.h>
|
||||
#include <gtk/gtkgestureclick.h>
|
||||
#include <gtk/gtkgesturedrag.h>
|
||||
@ -134,6 +144,7 @@
|
||||
#include <gtk/gtkglarea.h>
|
||||
#include <gtk/gtkgrid.h>
|
||||
#include <gtk/gtkgridlayout.h>
|
||||
#include <gtk/gtkgridview.h>
|
||||
#include <gtk/gtkheaderbar.h>
|
||||
#include <gtk/gtkicontheme.h>
|
||||
#include <gtk/gtkiconview.h>
|
||||
@ -146,9 +157,13 @@
|
||||
#include <gtk/gtklayoutmanager.h>
|
||||
#include <gtk/gtklayoutchild.h>
|
||||
#include <gtk/gtklevelbar.h>
|
||||
#include <gtk/gtklistbase.h>
|
||||
#include <gtk/gtklinkbutton.h>
|
||||
#include <gtk/gtklistbox.h>
|
||||
#include <gtk/gtklistitem.h>
|
||||
#include <gtk/gtklistitemfactory.h>
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtklistview.h>
|
||||
#include <gtk/gtklockbutton.h>
|
||||
#include <gtk/gtkmain.h>
|
||||
#include <gtk/gtkmaplistmodel.h>
|
||||
@ -158,10 +173,13 @@
|
||||
#include <gtk/gtkmenubutton.h>
|
||||
#include <gtk/gtkmessagedialog.h>
|
||||
#include <gtk/gtkmountoperation.h>
|
||||
#include <gtk/gtkmultifilter.h>
|
||||
#include <gtk/gtkmultisorter.h>
|
||||
#include <gtk/gtknative.h>
|
||||
#include <gtk/gtknativedialog.h>
|
||||
#include <gtk/gtknoselection.h>
|
||||
#include <gtk/gtknotebook.h>
|
||||
#include <gtk/gtknumericsorter.h>
|
||||
#include <gtk/gtkorientable.h>
|
||||
#include <gtk/gtkoverlay.h>
|
||||
#include <gtk/gtkoverlaylayout.h>
|
||||
@ -206,9 +224,11 @@
|
||||
#include <gtk/gtkshortcutswindow.h>
|
||||
#include <gtk/gtkshortcuttrigger.h>
|
||||
#include <gtk/gtkshow.h>
|
||||
#include <gtk/gtksignallistitemfactory.h>
|
||||
#include <gtk/gtksingleselection.h>
|
||||
#include <gtk/gtkslicelistmodel.h>
|
||||
#include <gtk/gtksnapshot.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
#include <gtk/gtksortlistmodel.h>
|
||||
#include <gtk/gtkstacksidebar.h>
|
||||
#include <gtk/gtksizegroup.h>
|
||||
@ -218,6 +238,8 @@
|
||||
#include <gtk/gtkstack.h>
|
||||
#include <gtk/gtkstackswitcher.h>
|
||||
#include <gtk/gtkstatusbar.h>
|
||||
#include <gtk/gtkstringfilter.h>
|
||||
#include <gtk/gtkstringsorter.h>
|
||||
#include <gtk/gtkstylecontext.h>
|
||||
#include <gtk/gtkstyleprovider.h>
|
||||
#include <gtk/gtkswitch.h>
|
||||
@ -233,7 +255,9 @@
|
||||
#include <gtk/gtktooltip.h>
|
||||
#include <gtk/gtktestutils.h>
|
||||
#include <gtk/gtktreednd.h>
|
||||
#include <gtk/gtktreeexpander.h>
|
||||
#include <gtk/gtktreelistmodel.h>
|
||||
#include <gtk/gtktreelistrowsorter.h>
|
||||
#include <gtk/gtktreemodel.h>
|
||||
#include <gtk/gtktreemodelfilter.h>
|
||||
#include <gtk/gtktreemodelsort.h>
|
||||
|
128
gtk/gtkbuilder.c
128
gtk/gtkbuilder.c
@ -217,8 +217,10 @@
|
||||
|
||||
#include "gdkpixbufutilsprivate.h"
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkbuilderlistitemfactory.h"
|
||||
#include "gtkbuilderscopeprivate.h"
|
||||
#include "gtkdebug.h"
|
||||
#include "gtkexpression.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkicontheme.h"
|
||||
#include "gtkintl.h"
|
||||
@ -527,7 +529,18 @@ gtk_builder_get_parameters (GtkBuilder *builder,
|
||||
const char *property_name = g_intern_string (prop->pspec->name);
|
||||
GValue property_value = G_VALUE_INIT;
|
||||
|
||||
if (prop->bound && (!prop->text || prop->text->len == 0))
|
||||
if (prop->value)
|
||||
{
|
||||
g_value_init (&property_value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec));
|
||||
|
||||
if (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) == GTK_TYPE_EXPRESSION)
|
||||
g_value_set_boxed (&property_value, prop->value);
|
||||
else
|
||||
{
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
else if (prop->bound && (!prop->text || prop->text->len == 0))
|
||||
{
|
||||
/* Ignore properties with a binding and no value since they are
|
||||
* only there for to express the binding.
|
||||
@ -674,13 +687,43 @@ gtk_builder_take_bindings (GtkBuilder *builder,
|
||||
|
||||
for (l = bindings; l; l = l->next)
|
||||
{
|
||||
BindingInfo *info = l->data;
|
||||
info->target = target;
|
||||
CommonInfo *common_info = l->data;
|
||||
|
||||
if (common_info->tag_type == TAG_BINDING)
|
||||
{
|
||||
BindingInfo *info = l->data;
|
||||
info->target = target;
|
||||
}
|
||||
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
|
||||
{
|
||||
BindingExpressionInfo *info = l->data;
|
||||
info->target = target;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
priv->bindings = g_slist_concat (priv->bindings, bindings);
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_special_construct_parameters (GtkBuilder *builder,
|
||||
GType object_type,
|
||||
ObjectProperties *construct_parameters)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
if (g_type_is_a (object_type, GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
|
||||
{
|
||||
g_value_init (&value, GTK_TYPE_BUILDER_SCOPE);
|
||||
g_value_set_object (&value, priv->scope);
|
||||
object_properties_add (construct_parameters, "scope", &value);
|
||||
}
|
||||
}
|
||||
|
||||
GObject *
|
||||
_gtk_builder_construct (GtkBuilder *builder,
|
||||
ObjectInfo *info,
|
||||
@ -778,6 +821,8 @@ _gtk_builder_construct (GtkBuilder *builder,
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure_special_construct_parameters (builder, info->type, construct_parameters);
|
||||
|
||||
obj = g_object_new_with_properties (info->type,
|
||||
construct_parameters->len,
|
||||
(const char **) construct_parameters->names->pdata,
|
||||
@ -1002,17 +1047,6 @@ gtk_builder_apply_delayed_properties (GtkBuilder *builder,
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void
|
||||
free_binding_info (gpointer data,
|
||||
gpointer user)
|
||||
{
|
||||
BindingInfo *info = data;
|
||||
|
||||
g_free (info->source);
|
||||
g_free (info->source_property);
|
||||
g_slice_free (BindingInfo, data);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
gtk_builder_create_bindings (GtkBuilder *builder,
|
||||
GError **error)
|
||||
@ -1023,26 +1057,68 @@ gtk_builder_create_bindings (GtkBuilder *builder,
|
||||
|
||||
for (l = priv->bindings; l; l = l->next)
|
||||
{
|
||||
BindingInfo *info = l->data;
|
||||
GObject *source;
|
||||
CommonInfo *common_info = l->data;
|
||||
|
||||
if (result)
|
||||
if (common_info->tag_type == TAG_BINDING)
|
||||
{
|
||||
BindingInfo *info = l->data;
|
||||
GObject *source;
|
||||
|
||||
source = gtk_builder_lookup_object (builder, info->source, info->line, info->col, error);
|
||||
if (source)
|
||||
g_object_bind_property (source, info->source_property,
|
||||
info->target, info->target_pspec->name,
|
||||
info->flags);
|
||||
else
|
||||
result = FALSE;
|
||||
}
|
||||
error = NULL;
|
||||
|
||||
free_binding_info (info, NULL);
|
||||
_free_binding_info (info, NULL);
|
||||
}
|
||||
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
|
||||
{
|
||||
BindingExpressionInfo *info = l->data;
|
||||
GtkExpression *expression;
|
||||
GObject *object;
|
||||
|
||||
if (info->object_name)
|
||||
{
|
||||
object = gtk_builder_lookup_object (builder, info->object_name, info->line, info->col, error);
|
||||
if (object == NULL)
|
||||
{
|
||||
error = NULL;
|
||||
result = FALSE;
|
||||
}
|
||||
}
|
||||
else if (priv->current_object)
|
||||
{
|
||||
object = priv->current_object;
|
||||
}
|
||||
else
|
||||
{
|
||||
object = info->target;
|
||||
}
|
||||
|
||||
if (object)
|
||||
{
|
||||
expression = expression_info_construct (builder, info->expr, error);
|
||||
if (expression == NULL)
|
||||
{
|
||||
g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col);
|
||||
error = NULL;
|
||||
result = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_expression_bind (expression, info->target, info->target_pspec->name, object);
|
||||
}
|
||||
}
|
||||
|
||||
free_binding_expression_info (info);
|
||||
}
|
||||
}
|
||||
|
||||
g_slist_free (priv->bindings);
|
||||
priv->bindings = NULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1204,7 +1280,7 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder,
|
||||
/**
|
||||
* gtk_builder_extend_with_template:
|
||||
* @builder: a #GtkBuilder
|
||||
* @widget: the widget that is being extended
|
||||
* @object: the object that is being extended
|
||||
* @template_type: the type that the template is for
|
||||
* @buffer: the string to parse
|
||||
* @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
|
||||
@ -1220,7 +1296,7 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder,
|
||||
*/
|
||||
gboolean
|
||||
gtk_builder_extend_with_template (GtkBuilder *builder,
|
||||
GtkWidget *widget,
|
||||
GObject *object,
|
||||
GType template_type,
|
||||
const gchar *buffer,
|
||||
gssize length,
|
||||
@ -1231,9 +1307,9 @@ gtk_builder_extend_with_template (GtkBuilder *builder,
|
||||
char *filename;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), 0);
|
||||
g_return_val_if_fail (g_type_name (template_type) != NULL, 0);
|
||||
g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (widget), template_type), 0);
|
||||
g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (object), template_type), 0);
|
||||
g_return_val_if_fail (buffer && buffer[0], 0);
|
||||
|
||||
tmp_error = NULL;
|
||||
@ -1245,7 +1321,7 @@ gtk_builder_extend_with_template (GtkBuilder *builder,
|
||||
priv->template_type = template_type;
|
||||
|
||||
filename = g_strconcat ("<", g_type_name (template_type), " template>", NULL);
|
||||
gtk_builder_expose_object (builder, g_type_name (template_type), G_OBJECT (widget));
|
||||
gtk_builder_expose_object (builder, g_type_name (template_type), object);
|
||||
_gtk_builder_parser_parse_buffer (builder, filename,
|
||||
buffer, length,
|
||||
NULL,
|
||||
|
@ -197,7 +197,7 @@ GClosure * gtk_builder_create_closure (GtkBuilder *builder,
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_builder_extend_with_template (GtkBuilder *builder,
|
||||
GtkWidget *widget,
|
||||
GObject *object,
|
||||
GType template_type,
|
||||
const gchar *buffer,
|
||||
gssize length,
|
||||
|
401
gtk/gtkbuilderlistitemfactory.c
Normal file
401
gtk/gtkbuilderlistitemfactory.c
Normal file
@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkbuilderlistitemfactory.h"
|
||||
|
||||
#include "gtkbuilder.h"
|
||||
#include "gtkbuilderprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkbuilderlistitemfactory
|
||||
* @Tiitle: GtkBuilderListItemFactory
|
||||
* @Short_description: A listitem factory using ui files
|
||||
*
|
||||
* #GtkBuilderListItemFactory is a #GtkListItemFactory that creates
|
||||
* widgets by instantiating #GtkBuilder UI templates. The templates
|
||||
* must be extending #GtkListItem, and typically use #GtkExpressions
|
||||
* to obtain data from the items in the model.
|
||||
*
|
||||
* Example:
|
||||
* |[
|
||||
* <interface>
|
||||
* <template class="GtkListItem">
|
||||
* <property name="child">
|
||||
* <object class="GtkLabel">
|
||||
* <property name="xalign">0</property>
|
||||
* <binding name="label">
|
||||
* <lookup name="name" type="SettingsKey">
|
||||
* <lookup name="item">GtkListItem</lookup>
|
||||
* </lookup>
|
||||
* </binding>
|
||||
* </object>
|
||||
* </property>
|
||||
* </template>
|
||||
* </interface>
|
||||
* ]|
|
||||
*/
|
||||
|
||||
struct _GtkBuilderListItemFactory
|
||||
{
|
||||
GtkListItemFactory parent_instance;
|
||||
|
||||
GtkBuilderScope *scope;
|
||||
GBytes *bytes;
|
||||
GBytes *data;
|
||||
char *resource;
|
||||
};
|
||||
|
||||
struct _GtkBuilderListItemFactoryClass
|
||||
{
|
||||
GtkListItemFactoryClass parent_class;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_BYTES,
|
||||
PROP_RESOURCE,
|
||||
PROP_SCOPE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkBuilderListItemFactory, gtk_builder_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_setup (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (factory);
|
||||
GtkBuilder *builder;
|
||||
GError *error = NULL;
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_builder_list_item_factory_parent_class)->setup (factory, widget, list_item);
|
||||
|
||||
builder = gtk_builder_new ();
|
||||
|
||||
gtk_builder_set_current_object (builder, G_OBJECT (list_item));
|
||||
if (self->scope)
|
||||
gtk_builder_set_scope (builder, self->scope);
|
||||
|
||||
if (!gtk_builder_extend_with_template (builder, G_OBJECT (list_item), G_OBJECT_TYPE (list_item),
|
||||
(const char *)g_bytes_get_data (self->data, NULL),
|
||||
g_bytes_get_size (self->data),
|
||||
&error))
|
||||
{
|
||||
g_critical ("Error building template for list item: %s", error->message);
|
||||
g_error_free (error);
|
||||
|
||||
/* This should never happen, if the template XML cannot be built
|
||||
* then it is a critical programming error.
|
||||
*/
|
||||
g_object_unref (builder);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_BYTES:
|
||||
g_value_set_boxed (value, self->bytes);
|
||||
break;
|
||||
|
||||
case PROP_RESOURCE:
|
||||
g_value_set_string (value, self->resource);
|
||||
break;
|
||||
|
||||
case PROP_SCOPE:
|
||||
g_value_set_object (value, self->scope);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_builder_list_item_factory_set_bytes (GtkBuilderListItemFactory *self,
|
||||
GBytes *bytes)
|
||||
{
|
||||
if (bytes == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (self->bytes)
|
||||
{
|
||||
g_critical ("Data for GtkBuilderListItemFactory has already been set.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->bytes = g_bytes_ref (bytes);
|
||||
|
||||
if (!_gtk_buildable_parser_is_precompiled (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)))
|
||||
{
|
||||
GError *error = NULL;
|
||||
GBytes *data;
|
||||
|
||||
data = _gtk_buildable_parser_precompile (g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes),
|
||||
&error);
|
||||
if (data == NULL)
|
||||
{
|
||||
g_warning ("Failed to precompile template for GtkBuilderListItemFactory: %s", error->message);
|
||||
g_error_free (error);
|
||||
self->data = g_bytes_ref (bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->data = data;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_BYTES:
|
||||
gtk_builder_list_item_factory_set_bytes (self, g_value_get_boxed (value));
|
||||
break;
|
||||
|
||||
case PROP_RESOURCE:
|
||||
{
|
||||
GError *error = NULL;
|
||||
GBytes *bytes;
|
||||
const char *resource;
|
||||
|
||||
resource = g_value_get_string (value);
|
||||
if (resource == NULL)
|
||||
break;
|
||||
|
||||
bytes = g_resources_lookup_data (resource, 0, &error);
|
||||
if (bytes)
|
||||
{
|
||||
if (gtk_builder_list_item_factory_set_bytes (self, bytes))
|
||||
self->resource = g_strdup (resource);
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_critical ("Unable to load resource for list item template: %s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_SCOPE:
|
||||
self->scope = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_finalize (GObject *object)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
|
||||
|
||||
g_clear_object (&self->scope);
|
||||
g_bytes_unref (self->bytes);
|
||||
g_bytes_unref (self->data);
|
||||
g_free (self->resource);
|
||||
|
||||
G_OBJECT_CLASS (gtk_builder_list_item_factory_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_class_init (GtkBuilderListItemFactoryClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = gtk_builder_list_item_factory_finalize;
|
||||
gobject_class->get_property = gtk_builder_list_item_factory_get_property;
|
||||
gobject_class->set_property = gtk_builder_list_item_factory_set_property;
|
||||
|
||||
factory_class->setup = gtk_builder_list_item_factory_setup;
|
||||
|
||||
/**
|
||||
* GtkBuilderListItemFactory:bytes:
|
||||
*
|
||||
* bytes containing the UI definition
|
||||
*/
|
||||
properties[PROP_BYTES] =
|
||||
g_param_spec_boxed ("bytes",
|
||||
P_("Bytes"),
|
||||
P_("bytes containing the UI definition"),
|
||||
G_TYPE_BYTES,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkBuilderListItemFactory:resource:
|
||||
*
|
||||
* resource containing the UI definition
|
||||
*/
|
||||
properties[PROP_RESOURCE] =
|
||||
g_param_spec_string ("resource",
|
||||
P_("Resource"),
|
||||
P_("resource containing the UI definition"),
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkBuilderListItemFactory:scope:
|
||||
*
|
||||
* scope to use when instantiating listitems
|
||||
*/
|
||||
properties[PROP_SCOPE] =
|
||||
g_param_spec_object ("scope",
|
||||
P_("Scope"),
|
||||
P_("scope to use when instantiating listitems"),
|
||||
GTK_TYPE_BUILDER_SCOPE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_init (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_list_item_factory_new_from_bytes:
|
||||
* @scope: (nullable) (transfer none): A scope to use when instantiating
|
||||
* @bytes: the bytes containing the ui file to instantiate
|
||||
*
|
||||
* Creates s new #GtkBuilderListItemFactory that instantiates widgets
|
||||
* using @bytes as the data to pass to #GtkBuilder.
|
||||
*
|
||||
* Returns: a new #GtkBuilderListItemFactory
|
||||
**/
|
||||
GtkListItemFactory *
|
||||
gtk_builder_list_item_factory_new_from_bytes (GtkBuilderScope *scope,
|
||||
GBytes *bytes)
|
||||
{
|
||||
g_return_val_if_fail (bytes != NULL, NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
|
||||
"bytes", bytes,
|
||||
"scope", scope,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_list_item_factory_new_from_resource:
|
||||
* @scope: (nullable) (transfer none): A scope to use when instantiating
|
||||
* @resource_path: valid path to a resource that contains the data
|
||||
*
|
||||
* Creates s new #GtkBuilderListItemFactory that instantiates widgets
|
||||
* using data read from the given @resource_path to pass to #GtkBuilder.
|
||||
*
|
||||
* Returns: a new #GtkBuilderListItemFactory
|
||||
**/
|
||||
GtkListItemFactory *
|
||||
gtk_builder_list_item_factory_new_from_resource (GtkBuilderScope *scope,
|
||||
const char *resource_path)
|
||||
{
|
||||
g_return_val_if_fail (scope == NULL || GTK_IS_BUILDER_SCOPE (scope), NULL);
|
||||
g_return_val_if_fail (resource_path != NULL, NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
|
||||
"resource", resource_path,
|
||||
"scope", scope,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_list_item_factory_get_bytes:
|
||||
* @self: a #GtkBuilderListItemFactory
|
||||
*
|
||||
* Gets the data used as the #GtkBuilder UI template for constructing
|
||||
* listitems.
|
||||
*
|
||||
* Returns: (transfer none): The GtkBuilder data
|
||||
*
|
||||
**/
|
||||
GBytes *
|
||||
gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
|
||||
|
||||
return self->bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_list_item_factory_get_resource:
|
||||
* @self: a #GtkBuilderListItemFactory
|
||||
*
|
||||
* If the data references a resource, gets the path of that resource.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The path to the resource or %NULL
|
||||
* if none
|
||||
**/
|
||||
const char *
|
||||
gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
|
||||
|
||||
return self->resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_list_item_factory_get_scope:
|
||||
* @self: a #GtkBuilderListItemFactory
|
||||
*
|
||||
* Gets the scope used when constructing listitems.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The scope used when constructing listitems
|
||||
**/
|
||||
GtkBuilderScope *
|
||||
gtk_builder_list_item_factory_get_scope (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
|
||||
|
||||
return self->scope;
|
||||
}
|
||||
|
60
gtk/gtkbuilderlistitemfactory.h
Normal file
60
gtk/gtkbuilderlistitemfactory.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_BUILDER_LIST_ITEM_FACTORY_H__
|
||||
#define __GTK_BUILDER_LIST_ITEM_FACTORY_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistitemfactory.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_BUILDER_LIST_ITEM_FACTORY (gtk_builder_list_item_factory_get_type ())
|
||||
#define GTK_BUILDER_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactory))
|
||||
#define GTK_BUILDER_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactoryClass))
|
||||
#define GTK_IS_BUILDER_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
|
||||
#define GTK_IS_BUILDER_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
|
||||
#define GTK_BUILDER_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactoryClass))
|
||||
|
||||
typedef struct _GtkBuilderListItemFactory GtkBuilderListItemFactory;
|
||||
typedef struct _GtkBuilderListItemFactoryClass GtkBuilderListItemFactoryClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_builder_list_item_factory_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory * gtk_builder_list_item_factory_new_from_bytes (GtkBuilderScope *scope,
|
||||
GBytes *bytes);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory * gtk_builder_list_item_factory_new_from_resource (GtkBuilderScope *scope,
|
||||
const char *resource_path);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GBytes * gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const char * gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBuilderScope * gtk_builder_list_item_factory_get_scope (GtkBuilderListItemFactory *self) G_GNUC_PURE;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_BUILDER_LIST_ITEM_FACTORY_H__ */
|
@ -913,7 +913,8 @@ parse_property (ParserData *data,
|
||||
{
|
||||
BindingInfo *binfo;
|
||||
|
||||
binfo = g_slice_new (BindingInfo);
|
||||
binfo = g_slice_new0 (BindingInfo);
|
||||
binfo->tag_type = TAG_BINDING;
|
||||
binfo->target = NULL;
|
||||
binfo->target_pspec = pspec;
|
||||
binfo->source = g_strdup (bind_source);
|
||||
@ -932,7 +933,7 @@ parse_property (ParserData *data,
|
||||
return;
|
||||
}
|
||||
|
||||
info = g_slice_new (PropertyInfo);
|
||||
info = g_slice_new0 (PropertyInfo);
|
||||
info->tag_type = TAG_PROPERTY;
|
||||
info->pspec = pspec;
|
||||
info->text = g_string_new ("");
|
||||
@ -945,14 +946,503 @@ parse_property (ParserData *data,
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_binding (ParserData *data,
|
||||
const gchar *element_name,
|
||||
const gchar **names,
|
||||
const gchar **values,
|
||||
GError **error)
|
||||
{
|
||||
BindingExpressionInfo *info;
|
||||
const char *name = NULL;
|
||||
const char *object_name = NULL;
|
||||
ObjectInfo *object_info;
|
||||
GParamSpec *pspec = NULL;
|
||||
|
||||
object_info = state_peek_info (data, ObjectInfo);
|
||||
if (!object_info ||
|
||||
!(object_info->tag_type == TAG_OBJECT ||
|
||||
object_info->tag_type == TAG_TEMPLATE))
|
||||
{
|
||||
error_invalid_tag (data, element_name, NULL, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_markup_collect_attributes (element_name, names, values, error,
|
||||
G_MARKUP_COLLECT_STRING, "name", &name,
|
||||
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", &object_name,
|
||||
G_MARKUP_COLLECT_INVALID))
|
||||
{
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
pspec = g_object_class_find_property (object_info->oclass, name);
|
||||
|
||||
if (!pspec)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_PROPERTY,
|
||||
"Invalid property: %s.%s",
|
||||
g_type_name (object_info->type), name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
else if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_PROPERTY,
|
||||
"%s.%s is a construct-only property",
|
||||
g_type_name (object_info->type), name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
else if (!(pspec->flags & G_PARAM_WRITABLE))
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_PROPERTY,
|
||||
"%s.%s is a non-writable property",
|
||||
g_type_name (object_info->type), name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
info = g_slice_new0 (BindingExpressionInfo);
|
||||
info->tag_type = TAG_BINDING_EXPRESSION;
|
||||
info->target = NULL;
|
||||
info->target_pspec = pspec;
|
||||
info->object_name = g_strdup (object_name);
|
||||
gtk_buildable_parse_context_get_position (&data->ctx, &info->line, &info->col);
|
||||
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
static void
|
||||
free_property_info (PropertyInfo *info)
|
||||
{
|
||||
if (info->value)
|
||||
{
|
||||
if (G_PARAM_SPEC_VALUE_TYPE (info->pspec) == GTK_TYPE_EXPRESSION)
|
||||
gtk_expression_unref (info->value);
|
||||
else
|
||||
g_assert_not_reached();
|
||||
}
|
||||
g_string_free (info->text, TRUE);
|
||||
g_free (info->context);
|
||||
g_slice_free (PropertyInfo, info);
|
||||
}
|
||||
|
||||
static void
|
||||
free_expression_info (ExpressionInfo *info)
|
||||
{
|
||||
switch (info->expression_type)
|
||||
{
|
||||
case EXPRESSION_EXPRESSION:
|
||||
g_clear_pointer (&info->expression, gtk_expression_unref);
|
||||
break;
|
||||
|
||||
case EXPRESSION_CONSTANT:
|
||||
g_string_free (info->constant.text, TRUE);
|
||||
break;
|
||||
|
||||
case EXPRESSION_CLOSURE:
|
||||
g_free (info->closure.function_name);
|
||||
g_free (info->closure.object_name);
|
||||
g_slist_free_full (info->closure.params, (GDestroyNotify) free_expression_info);
|
||||
break;
|
||||
|
||||
case EXPRESSION_PROPERTY:
|
||||
g_clear_pointer (&info->property.expression, free_expression_info);
|
||||
g_free (info->property.property_name);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
g_slice_free (ExpressionInfo, info);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_expression_parent (ParserData *data)
|
||||
{
|
||||
CommonInfo *common_info = state_peek_info (data, CommonInfo);
|
||||
|
||||
if (common_info == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (common_info->tag_type == TAG_PROPERTY)
|
||||
{
|
||||
PropertyInfo *prop_info = (PropertyInfo *) common_info;
|
||||
|
||||
return G_PARAM_SPEC_VALUE_TYPE (prop_info->pspec) == GTK_TYPE_EXPRESSION;
|
||||
}
|
||||
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
|
||||
{
|
||||
BindingExpressionInfo *expr_info = (BindingExpressionInfo *) common_info;
|
||||
|
||||
return expr_info->expr == NULL;
|
||||
}
|
||||
else if (common_info->tag_type == TAG_EXPRESSION)
|
||||
{
|
||||
ExpressionInfo *expr_info = (ExpressionInfo *) common_info;
|
||||
|
||||
switch (expr_info->expression_type)
|
||||
{
|
||||
case EXPRESSION_CLOSURE:
|
||||
return TRUE;
|
||||
case EXPRESSION_CONSTANT:
|
||||
return FALSE;
|
||||
case EXPRESSION_PROPERTY:
|
||||
return expr_info->property.expression == NULL;
|
||||
case EXPRESSION_EXPRESSION:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_constant_expression (ParserData *data,
|
||||
const gchar *element_name,
|
||||
const gchar **names,
|
||||
const gchar **values,
|
||||
GError **error)
|
||||
{
|
||||
ExpressionInfo *info;
|
||||
const char *type_name = NULL;
|
||||
GType type;
|
||||
|
||||
if (!check_expression_parent (data))
|
||||
{
|
||||
error_invalid_tag (data, element_name, NULL, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_markup_collect_attributes (element_name, names, values, error,
|
||||
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "type", &type_name,
|
||||
G_MARKUP_COLLECT_INVALID))
|
||||
{
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type_name == NULL)
|
||||
type = G_TYPE_INVALID;
|
||||
else
|
||||
{
|
||||
type = gtk_builder_get_type_from_name (data->builder, type_name);
|
||||
if (type == G_TYPE_INVALID)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_VALUE,
|
||||
"Invalid type '%s'", type_name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
info = g_slice_new0 (ExpressionInfo);
|
||||
info->tag_type = TAG_EXPRESSION;
|
||||
info->expression_type = EXPRESSION_CONSTANT;
|
||||
info->constant.type = type;
|
||||
info->constant.text = g_string_new (NULL);
|
||||
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_closure_expression (ParserData *data,
|
||||
const gchar *element_name,
|
||||
const gchar **names,
|
||||
const gchar **values,
|
||||
GError **error)
|
||||
{
|
||||
ExpressionInfo *info;
|
||||
const char *type_name;
|
||||
const char *function_name;
|
||||
const char *object_name = NULL;
|
||||
gboolean swapped = -1;
|
||||
GType type;
|
||||
|
||||
if (!check_expression_parent (data))
|
||||
{
|
||||
error_invalid_tag (data, element_name, NULL, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_markup_collect_attributes (element_name, names, values, error,
|
||||
G_MARKUP_COLLECT_STRING, "type", &type_name,
|
||||
G_MARKUP_COLLECT_STRING, "function", &function_name,
|
||||
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", &object_name,
|
||||
G_MARKUP_COLLECT_TRISTATE|G_MARKUP_COLLECT_OPTIONAL, "swapped", &swapped,
|
||||
G_MARKUP_COLLECT_INVALID))
|
||||
{
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
type = gtk_builder_get_type_from_name (data->builder, type_name);
|
||||
if (type == G_TYPE_INVALID)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_VALUE,
|
||||
"Invalid type '%s'", type_name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Swapped defaults to FALSE except when object is set */
|
||||
if (swapped == -1)
|
||||
{
|
||||
if (object_name)
|
||||
swapped = TRUE;
|
||||
else
|
||||
swapped = FALSE;
|
||||
}
|
||||
|
||||
info = g_slice_new0 (ExpressionInfo);
|
||||
info->tag_type = TAG_EXPRESSION;
|
||||
info->expression_type = EXPRESSION_CLOSURE;
|
||||
info->closure.type = type;
|
||||
info->closure.swapped = swapped;
|
||||
info->closure.function_name = g_strdup (function_name);
|
||||
info->closure.object_name = g_strdup (object_name);
|
||||
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_lookup_expression (ParserData *data,
|
||||
const gchar *element_name,
|
||||
const gchar **names,
|
||||
const gchar **values,
|
||||
GError **error)
|
||||
{
|
||||
ExpressionInfo *info;
|
||||
const char *property_name;
|
||||
const char *type_name = NULL;
|
||||
GType type;
|
||||
|
||||
if (!check_expression_parent (data))
|
||||
{
|
||||
error_invalid_tag (data, element_name, NULL, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_markup_collect_attributes (element_name, names, values, error,
|
||||
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "type", &type_name,
|
||||
G_MARKUP_COLLECT_STRING, "name", &property_name,
|
||||
G_MARKUP_COLLECT_INVALID))
|
||||
{
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type_name == NULL)
|
||||
{
|
||||
type = G_TYPE_INVALID;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = gtk_builder_get_type_from_name (data->builder, type_name);
|
||||
if (type == G_TYPE_INVALID)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_VALUE,
|
||||
"Invalid type '%s'", type_name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
info = g_slice_new0 (ExpressionInfo);
|
||||
info->tag_type = TAG_EXPRESSION;
|
||||
info->expression_type = EXPRESSION_PROPERTY;
|
||||
info->property.this_type = type;
|
||||
info->property.property_name = g_strdup (property_name);
|
||||
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
GtkExpression *
|
||||
expression_info_construct (GtkBuilder *builder,
|
||||
ExpressionInfo *info,
|
||||
GError **error)
|
||||
{
|
||||
switch (info->expression_type)
|
||||
{
|
||||
case EXPRESSION_EXPRESSION:
|
||||
break;
|
||||
|
||||
case EXPRESSION_CONSTANT:
|
||||
{
|
||||
GtkExpression *expr;
|
||||
|
||||
if (info->constant.type == G_TYPE_INVALID)
|
||||
{
|
||||
GObject *o = gtk_builder_lookup_object (builder, info->constant.text->str, 0, 0, error);
|
||||
if (o == NULL)
|
||||
return NULL;
|
||||
|
||||
expr = gtk_object_expression_new (o);
|
||||
}
|
||||
else
|
||||
{
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
if (!gtk_builder_value_from_string_type (builder,
|
||||
info->constant.type,
|
||||
info->constant.text->str,
|
||||
&value,
|
||||
error))
|
||||
return NULL;
|
||||
|
||||
if (G_VALUE_HOLDS_OBJECT (&value))
|
||||
expr = gtk_object_expression_new (g_value_get_object (&value));
|
||||
else
|
||||
expr = gtk_constant_expression_new_for_value (&value);
|
||||
|
||||
g_value_unset (&value);
|
||||
}
|
||||
|
||||
g_string_free (info->constant.text, TRUE);
|
||||
info->expression_type = EXPRESSION_EXPRESSION;
|
||||
info->expression = expr;
|
||||
}
|
||||
break;
|
||||
|
||||
case EXPRESSION_CLOSURE:
|
||||
{
|
||||
GObject *object;
|
||||
GClosure *closure;
|
||||
guint i, n_params;
|
||||
GtkExpression **params;
|
||||
GtkExpression *expression;
|
||||
GSList *l;
|
||||
|
||||
if (info->closure.object_name)
|
||||
{
|
||||
object = gtk_builder_lookup_object (builder, info->closure.object_name, 0, 0, error);
|
||||
if (object == NULL)
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
object = NULL;
|
||||
}
|
||||
|
||||
closure = gtk_builder_create_closure (builder,
|
||||
info->closure.function_name,
|
||||
info->closure.swapped,
|
||||
object,
|
||||
error);
|
||||
if (closure == NULL)
|
||||
return NULL;
|
||||
n_params = g_slist_length (info->closure.params);
|
||||
params = g_newa (GtkExpression *, n_params);
|
||||
i = n_params;
|
||||
for (l = info->closure.params; l; l = l->next)
|
||||
{
|
||||
params[--i] = expression_info_construct (builder, l->data, error);
|
||||
if (params[i] == NULL)
|
||||
return NULL;
|
||||
}
|
||||
expression = gtk_closure_expression_new (info->closure.type, closure, n_params, params);
|
||||
g_free (info->closure.function_name);
|
||||
g_free (info->closure.object_name);
|
||||
g_slist_free_full (info->closure.params, (GDestroyNotify) free_expression_info);
|
||||
info->expression_type = EXPRESSION_EXPRESSION;
|
||||
info->expression = expression;
|
||||
}
|
||||
break;
|
||||
|
||||
case EXPRESSION_PROPERTY:
|
||||
{
|
||||
GtkExpression *expression;
|
||||
GType type;
|
||||
GParamSpec *pspec;
|
||||
|
||||
if (info->property.expression)
|
||||
{
|
||||
expression = expression_info_construct (builder, info->property.expression, error);
|
||||
if (expression == NULL)
|
||||
return NULL;
|
||||
g_clear_pointer (&info->property.expression, free_expression_info);
|
||||
}
|
||||
else
|
||||
expression = NULL;
|
||||
|
||||
if (info->property.this_type != G_TYPE_INVALID)
|
||||
type = info->property.this_type;
|
||||
else if (expression != NULL)
|
||||
type = gtk_expression_get_value_type (expression);
|
||||
else
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
|
||||
"Lookups require a type attribute if they don't have an expression.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (g_type_is_a (type, G_TYPE_OBJECT))
|
||||
{
|
||||
GObjectClass *class = g_type_class_ref (type);
|
||||
pspec = g_object_class_find_property (class, info->property.property_name);
|
||||
g_type_class_unref (class);
|
||||
}
|
||||
else if (g_type_is_a (type, G_TYPE_INTERFACE))
|
||||
{
|
||||
GTypeInterface *iface = g_type_default_interface_ref (type);
|
||||
pspec = g_object_interface_find_property (iface, info->property.property_name);
|
||||
g_type_default_interface_unref (iface);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
|
||||
"Type `%s` does not support properties",
|
||||
g_type_name (type));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pspec == NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
|
||||
"Type `%s` does not have a property name `%s`",
|
||||
g_type_name (type), info->property.property_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
expression = gtk_property_expression_new_for_pspec (expression, pspec);
|
||||
|
||||
g_free (info->property.property_name);
|
||||
info->expression_type = EXPRESSION_EXPRESSION;
|
||||
info->expression = expression;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
return gtk_expression_ref (info->expression);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_signal (ParserData *data,
|
||||
const gchar *element_name,
|
||||
@ -1037,6 +1527,24 @@ _free_signal_info (SignalInfo *info,
|
||||
g_slice_free (SignalInfo, info);
|
||||
}
|
||||
|
||||
void
|
||||
_free_binding_info (BindingInfo *info,
|
||||
gpointer user)
|
||||
{
|
||||
g_free (info->source);
|
||||
g_free (info->source_property);
|
||||
g_slice_free (BindingInfo, info);
|
||||
}
|
||||
|
||||
void
|
||||
free_binding_expression_info (BindingExpressionInfo *info)
|
||||
{
|
||||
if (info->expr)
|
||||
free_expression_info (info->expr);
|
||||
g_free (info->object_name);
|
||||
g_slice_free (BindingExpressionInfo, info);
|
||||
}
|
||||
|
||||
static void
|
||||
free_requires_info (RequiresInfo *info,
|
||||
gpointer user_data)
|
||||
@ -1277,6 +1785,8 @@ start_element (GtkBuildableParseContext *context,
|
||||
}
|
||||
else if (strcmp (element_name, "property") == 0)
|
||||
parse_property (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "binding") == 0)
|
||||
parse_binding (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "child") == 0)
|
||||
parse_child (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "signal") == 0)
|
||||
@ -1287,6 +1797,12 @@ start_element (GtkBuildableParseContext *context,
|
||||
parse_requires (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "interface") == 0)
|
||||
parse_interface (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "constant") == 0)
|
||||
parse_constant_expression (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "closure") == 0)
|
||||
parse_closure_expression (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "lookup") == 0)
|
||||
parse_lookup_expression (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "menu") == 0)
|
||||
_gtk_builder_menu_start (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "placeholder") == 0)
|
||||
@ -1362,6 +1878,30 @@ end_element (GtkBuildableParseContext *context,
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
else if (strcmp (element_name, "binding") == 0)
|
||||
{
|
||||
BindingExpressionInfo *binfo = state_pop_info (data, BindingExpressionInfo);
|
||||
CommonInfo *info = state_peek_info (data, CommonInfo);
|
||||
|
||||
g_assert (info != NULL);
|
||||
|
||||
if (binfo->expr == NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_TAG,
|
||||
"Binding tag requires an expression");
|
||||
free_binding_expression_info (binfo);
|
||||
}
|
||||
else if (info->tag_type == TAG_OBJECT ||
|
||||
info->tag_type == TAG_TEMPLATE)
|
||||
{
|
||||
ObjectInfo *object_info = (ObjectInfo*)info;
|
||||
object_info->bindings = g_slist_prepend (object_info->bindings, binfo);
|
||||
}
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
else if (strcmp (element_name, "object") == 0 ||
|
||||
strcmp (element_name, "template") == 0)
|
||||
{
|
||||
@ -1422,6 +1962,50 @@ end_element (GtkBuildableParseContext *context,
|
||||
signal_info->object_name = g_strdup (object_info->id);
|
||||
object_info->signals = g_slist_prepend (object_info->signals, signal_info);
|
||||
}
|
||||
else if (strcmp (element_name, "constant") == 0 ||
|
||||
strcmp (element_name, "closure") == 0 ||
|
||||
strcmp (element_name, "lookup") == 0)
|
||||
{
|
||||
ExpressionInfo *expression_info = state_pop_info (data, ExpressionInfo);
|
||||
CommonInfo *parent_info = state_peek_info (data, CommonInfo);
|
||||
g_assert (parent_info != NULL);
|
||||
|
||||
if (parent_info->tag_type == TAG_BINDING_EXPRESSION)
|
||||
{
|
||||
BindingExpressionInfo *expr_info = (BindingExpressionInfo *) parent_info;
|
||||
|
||||
expr_info->expr = expression_info;
|
||||
}
|
||||
else if (parent_info->tag_type == TAG_PROPERTY)
|
||||
{
|
||||
PropertyInfo *prop_info = (PropertyInfo *) parent_info;
|
||||
|
||||
prop_info->value = expression_info_construct (data->builder, expression_info, error);
|
||||
}
|
||||
else if (parent_info->tag_type == TAG_EXPRESSION)
|
||||
{
|
||||
ExpressionInfo *expr_info = (ExpressionInfo *) parent_info;
|
||||
|
||||
switch (expr_info->expression_type)
|
||||
{
|
||||
case EXPRESSION_CLOSURE:
|
||||
expr_info->closure.params = g_slist_prepend (expr_info->closure.params, expression_info);
|
||||
break;
|
||||
case EXPRESSION_PROPERTY:
|
||||
expr_info->property.expression = expression_info;
|
||||
break;
|
||||
case EXPRESSION_EXPRESSION:
|
||||
case EXPRESSION_CONSTANT:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
else if (strcmp (element_name, "requires") == 0)
|
||||
{
|
||||
RequiresInfo *req_info = state_pop_info (data, RequiresInfo);
|
||||
@ -1502,6 +2086,33 @@ text (GtkBuildableParseContext *context,
|
||||
|
||||
g_string_append_len (prop_info->text, text, text_len);
|
||||
}
|
||||
else if (strcmp (gtk_buildable_parse_context_get_element (context), "constant") == 0)
|
||||
{
|
||||
ExpressionInfo *expr_info = (ExpressionInfo *) info;
|
||||
|
||||
g_string_append_len (expr_info->constant.text, text, text_len);
|
||||
}
|
||||
else if (strcmp (gtk_buildable_parse_context_get_element (context), "lookup") == 0)
|
||||
{
|
||||
ExpressionInfo *expr_info = (ExpressionInfo *) info;
|
||||
|
||||
while (g_ascii_isspace (*text) && text_len > 0)
|
||||
{
|
||||
text++;
|
||||
text_len--;
|
||||
}
|
||||
while (text_len > 0 && g_ascii_isspace (text[text_len - 1]))
|
||||
text_len--;
|
||||
if (expr_info->property.expression == NULL && text_len > 0)
|
||||
{
|
||||
ExpressionInfo *constant = g_slice_new0 (ExpressionInfo);
|
||||
constant->tag_type = TAG_EXPRESSION;
|
||||
constant->expression_type = EXPRESSION_CONSTANT;
|
||||
constant->constant.type = G_TYPE_INVALID;
|
||||
constant->constant.text = g_string_new_len (text, text_len);
|
||||
expr_info->property.expression = constant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1516,6 +2127,12 @@ free_info (CommonInfo *info)
|
||||
case TAG_CHILD:
|
||||
free_child_info ((ChildInfo *)info);
|
||||
break;
|
||||
case TAG_BINDING:
|
||||
_free_binding_info ((BindingInfo *)info, NULL);
|
||||
break;
|
||||
case TAG_BINDING_EXPRESSION:
|
||||
free_binding_expression_info ((BindingExpressionInfo *) info);
|
||||
break;
|
||||
case TAG_PROPERTY:
|
||||
free_property_info ((PropertyInfo *)info);
|
||||
break;
|
||||
@ -1525,6 +2142,9 @@ free_info (CommonInfo *info)
|
||||
case TAG_REQUIRES:
|
||||
free_requires_info ((RequiresInfo *)info, NULL);
|
||||
break;
|
||||
case TAG_EXPRESSION:
|
||||
free_expression_info ((ExpressionInfo *)info);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
@ -21,16 +21,19 @@
|
||||
|
||||
#include "gtkbuilder.h"
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkexpression.h"
|
||||
|
||||
enum {
|
||||
TAG_PROPERTY,
|
||||
TAG_MENU,
|
||||
TAG_BINDING,
|
||||
TAG_BINDING_EXPRESSION,
|
||||
TAG_REQUIRES,
|
||||
TAG_OBJECT,
|
||||
TAG_CHILD,
|
||||
TAG_SIGNAL,
|
||||
TAG_INTERFACE,
|
||||
TAG_TEMPLATE,
|
||||
TAG_EXPRESSION,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@ -64,6 +67,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
guint tag_type;
|
||||
GParamSpec *pspec;
|
||||
gpointer value;
|
||||
GString *text;
|
||||
gboolean translatable:1;
|
||||
gboolean bound:1;
|
||||
@ -72,6 +76,36 @@ typedef struct {
|
||||
gint col;
|
||||
} PropertyInfo;
|
||||
|
||||
typedef struct _ExpressionInfo ExpressionInfo;
|
||||
struct _ExpressionInfo {
|
||||
guint tag_type;
|
||||
enum {
|
||||
EXPRESSION_EXPRESSION,
|
||||
EXPRESSION_CONSTANT,
|
||||
EXPRESSION_CLOSURE,
|
||||
EXPRESSION_PROPERTY
|
||||
} expression_type;
|
||||
union {
|
||||
GtkExpression *expression;
|
||||
struct {
|
||||
GType type;
|
||||
GString *text;
|
||||
} constant;
|
||||
struct {
|
||||
GType type;
|
||||
char *function_name;
|
||||
char *object_name;
|
||||
gboolean swapped;
|
||||
GSList *params;
|
||||
} closure;
|
||||
struct {
|
||||
GType this_type;
|
||||
char *property_name;
|
||||
ExpressionInfo *expression;
|
||||
} property;
|
||||
};
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
guint tag_type;
|
||||
gchar *object_name;
|
||||
@ -84,6 +118,7 @@ typedef struct {
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint tag_type;
|
||||
GObject *target;
|
||||
GParamSpec *target_pspec;
|
||||
gchar *source;
|
||||
@ -93,6 +128,17 @@ typedef struct
|
||||
gint col;
|
||||
} BindingInfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint tag_type;
|
||||
GObject *target;
|
||||
GParamSpec *target_pspec;
|
||||
char *object_name;
|
||||
ExpressionInfo *expr;
|
||||
gint line;
|
||||
gint col;
|
||||
} BindingExpressionInfo;
|
||||
|
||||
typedef struct {
|
||||
guint tag_type;
|
||||
gchar *library;
|
||||
@ -179,6 +225,12 @@ gboolean _gtk_builder_finish (GtkBuilder *builder,
|
||||
GError **error);
|
||||
void _free_signal_info (SignalInfo *info,
|
||||
gpointer user_data);
|
||||
void _free_binding_info (BindingInfo *info,
|
||||
gpointer user_data);
|
||||
void free_binding_expression_info (BindingExpressionInfo *info);
|
||||
GtkExpression * expression_info_construct (GtkBuilder *builder,
|
||||
ExpressionInfo *info,
|
||||
GError **error);
|
||||
|
||||
/* Internal API which might be made public at some point */
|
||||
gboolean _gtk_builder_boolean_from_string (const gchar *string,
|
||||
|
148
gtk/gtkcolumnlistitemfactory.c
Normal file
148
gtk/gtkcolumnlistitemfactory.c
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnlistitemfactoryprivate.h"
|
||||
|
||||
#include "gtkboxlayout.h"
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkcolumnviewlayoutprivate.h"
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
struct _GtkColumnListItemFactory
|
||||
{
|
||||
GtkListItemFactory parent_instance;
|
||||
|
||||
GtkColumnView *view; /* no reference, the view references us */
|
||||
};
|
||||
|
||||
struct _GtkColumnListItemFactoryClass
|
||||
{
|
||||
GtkListItemFactoryClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnListItemFactory, gtk_column_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
|
||||
|
||||
static void
|
||||
gtk_column_list_item_factory_setup (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkColumnListItemFactory *self = GTK_COLUMN_LIST_ITEM_FACTORY (factory);
|
||||
GListModel *columns;
|
||||
guint i;
|
||||
|
||||
/* FIXME: evil */
|
||||
gtk_widget_set_layout_manager (GTK_WIDGET (widget),
|
||||
gtk_column_view_layout_new (self->view));
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->setup (factory, widget, list_item);
|
||||
|
||||
columns = gtk_column_view_get_columns (self->view);
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (columns); i++)
|
||||
{
|
||||
GtkColumnViewColumn *column = g_list_model_get_item (columns, i);
|
||||
|
||||
gtk_column_list_item_factory_add_column (self,
|
||||
list_item->owner,
|
||||
column,
|
||||
FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_list_item_factory_teardown (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *child;
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->teardown (factory, widget, list_item);
|
||||
|
||||
while ((child = gtk_widget_get_first_child (GTK_WIDGET (widget))))
|
||||
{
|
||||
gtk_list_item_widget_remove_child (GTK_LIST_ITEM_WIDGET (widget), child);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_list_item_factory_update (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
GtkWidget *child;
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->update (factory, widget, list_item, position, item, selected);
|
||||
|
||||
for (child = gtk_widget_get_first_child (GTK_WIDGET (widget));
|
||||
child;
|
||||
child = gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (child), position, item, selected);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_list_item_factory_class_init (GtkColumnListItemFactoryClass *klass)
|
||||
{
|
||||
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
|
||||
|
||||
factory_class->setup = gtk_column_list_item_factory_setup;
|
||||
factory_class->teardown = gtk_column_list_item_factory_teardown;
|
||||
factory_class->update = gtk_column_list_item_factory_update;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_list_item_factory_init (GtkColumnListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkColumnListItemFactory *
|
||||
gtk_column_list_item_factory_new (GtkColumnView *view)
|
||||
{
|
||||
GtkColumnListItemFactory *result;
|
||||
|
||||
result = g_object_new (GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, NULL);
|
||||
|
||||
result->view = view;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_list_item_factory_add_column (GtkColumnListItemFactory *factory,
|
||||
GtkListItemWidget *list_item,
|
||||
GtkColumnViewColumn *column,
|
||||
gboolean check_bind)
|
||||
{
|
||||
GtkWidget *cell;
|
||||
|
||||
cell = gtk_column_view_cell_new (column);
|
||||
gtk_list_item_widget_add_child (GTK_LIST_ITEM_WIDGET (list_item), GTK_WIDGET (cell));
|
||||
gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (cell),
|
||||
gtk_list_item_widget_get_position (list_item),
|
||||
gtk_list_item_widget_get_item (list_item),
|
||||
gtk_list_item_widget_get_selected (list_item));
|
||||
}
|
62
gtk/gtkcolumnlistitemfactoryprivate.h
Normal file
62
gtk/gtkcolumnlistitemfactoryprivate.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_LIST_ITEM_FACTORY_H__
|
||||
#define __GTK_COLUMN_LIST_ITEM_FACTORY_H__
|
||||
|
||||
#include <gtk/gtklistitemwidgetprivate.h>
|
||||
#include <gtk/gtkcolumnview.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_LIST_ITEM_FACTORY (gtk_column_list_item_factory_get_type ())
|
||||
#define GTK_COLUMN_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactory))
|
||||
#define GTK_COLUMN_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactoryClass))
|
||||
#define GTK_IS_COLUMN_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY))
|
||||
#define GTK_IS_COLUMN_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY))
|
||||
#define GTK_COLUMN_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactoryClass))
|
||||
|
||||
/**
|
||||
* GtkColumnListItemFactory:
|
||||
*
|
||||
* The object for the #GtkColumnListItemFactory.
|
||||
**/
|
||||
typedef struct _GtkColumnListItemFactory GtkColumnListItemFactory;
|
||||
typedef struct _GtkColumnListItemFactoryClass GtkColumnListItemFactoryClass;
|
||||
|
||||
|
||||
GType gtk_column_list_item_factory_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkColumnListItemFactory *
|
||||
gtk_column_list_item_factory_new (GtkColumnView *view);
|
||||
|
||||
void gtk_column_list_item_factory_add_column (GtkColumnListItemFactory *factory,
|
||||
GtkListItemWidget *list_item,
|
||||
GtkColumnViewColumn *column,
|
||||
gboolean check_bind);
|
||||
void gtk_column_list_item_factory_remove_column
|
||||
(GtkColumnListItemFactory *factory,
|
||||
GtkListItemWidget *list_item,
|
||||
guint col_pos,
|
||||
GtkColumnViewColumn *column);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_LIST_ITEM_FACTORY_H__ */
|
831
gtk/gtkcolumnview.c
Normal file
831
gtk/gtkcolumnview.c
Normal file
@ -0,0 +1,831 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewprivate.h"
|
||||
|
||||
#include "gtkboxlayout.h"
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkcolumnlistitemfactoryprivate.h"
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkcolumnviewlayoutprivate.h"
|
||||
#include "gtkcolumnviewsorterprivate.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistview.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkscrollable.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcolumnview
|
||||
* @title: GtkColumnView
|
||||
* @short_description: A widget for displaying lists in multiple columns
|
||||
* @see_also: #GtkColumnViewColumn, #GtkTreeView
|
||||
*
|
||||
* GtkColumnView is a widget to present a view into a large dynamic list of items
|
||||
* using multiple columns.
|
||||
*
|
||||
* It supports sorting that can be customized by the user by clicking on column
|
||||
* view headers. To set this up, the #GtkSorter returned by gtk_column_view_get_sorter()
|
||||
* must be attached to a sort model for the data that the view is showing, and the
|
||||
* columns must have sorters attached to them by calling gtk_column_view_column_set_sorter().
|
||||
* The initial sort order can be set with gtk_column_view_sort_by_column().
|
||||
*/
|
||||
|
||||
struct _GtkColumnView
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GListStore *columns;
|
||||
|
||||
GtkWidget *header;
|
||||
|
||||
GtkListView *listview;
|
||||
GtkColumnListItemFactory *factory;
|
||||
|
||||
GtkSorter *sorter;
|
||||
};
|
||||
|
||||
struct _GtkColumnViewClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_COLUMNS,
|
||||
PROP_HADJUSTMENT,
|
||||
PROP_HSCROLL_POLICY,
|
||||
PROP_MODEL,
|
||||
PROP_SHOW_SEPARATORS,
|
||||
PROP_SORTER,
|
||||
PROP_VADJUSTMENT,
|
||||
PROP_VSCROLL_POLICY,
|
||||
PROP_SINGLE_CLICK_ACTIVATE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTIVATE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static GtkBuildableIface *parent_buildable_iface;
|
||||
|
||||
static void
|
||||
gtk_column_view_buildable_add_child (GtkBuildable *buildable,
|
||||
GtkBuilder *builder,
|
||||
GObject *child,
|
||||
const gchar *type)
|
||||
{
|
||||
if (GTK_IS_COLUMN_VIEW_COLUMN (child))
|
||||
{
|
||||
if (type != NULL)
|
||||
{
|
||||
GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_column_view_append_column (GTK_COLUMN_VIEW (buildable),
|
||||
GTK_COLUMN_VIEW_COLUMN (child));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parent_buildable_iface->add_child (buildable, builder, child, type);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_buildable_interface_init (GtkBuildableIface *iface)
|
||||
{
|
||||
parent_buildable_iface = g_type_interface_peek_parent (iface);
|
||||
|
||||
iface->add_child = gtk_column_view_buildable_add_child;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkColumnView, gtk_column_view, GTK_TYPE_WIDGET,
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_column_view_buildable_interface_init)
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
gtk_column_view_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
gtk_column_view_measure_across (self, minimum, natural);
|
||||
}
|
||||
else
|
||||
{
|
||||
int header_min, header_nat, list_min, list_nat;
|
||||
|
||||
gtk_widget_measure (GTK_WIDGET (self->listview),
|
||||
orientation, for_size,
|
||||
&header_min, &header_nat,
|
||||
NULL, NULL);
|
||||
gtk_widget_measure (GTK_WIDGET (self->listview),
|
||||
orientation, for_size,
|
||||
&list_min, &list_nat,
|
||||
NULL, NULL);
|
||||
*minimum = header_min + list_min;
|
||||
*natural = header_nat + list_nat;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_column_view_allocate_columns (GtkColumnView *self,
|
||||
int width)
|
||||
{
|
||||
GtkScrollablePolicy scroll_policy;
|
||||
int col_min, col_nat, widget_min, widget_nat, extra, col_size, x;
|
||||
guint i;
|
||||
|
||||
gtk_column_view_measure_across (self, &col_min, &col_nat);
|
||||
gtk_widget_measure (GTK_WIDGET (self),
|
||||
GTK_ORIENTATION_HORIZONTAL, -1,
|
||||
&widget_min, &widget_nat,
|
||||
NULL, NULL);
|
||||
|
||||
scroll_policy = gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview));
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
{
|
||||
extra = widget_min - col_min;
|
||||
col_size = col_min;
|
||||
}
|
||||
else
|
||||
{
|
||||
extra = widget_nat - col_nat;
|
||||
col_size = col_nat;
|
||||
}
|
||||
width -= extra;
|
||||
width = MAX (width, col_size);
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
gtk_column_view_column_measure (column, &col_min, &col_nat);
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
col_size = col_min;
|
||||
else
|
||||
col_size = col_nat;
|
||||
|
||||
gtk_column_view_column_allocate (column, x, col_size);
|
||||
x += col_size;
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
return width + extra;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
|
||||
int full_width, header_height, min, nat;
|
||||
|
||||
full_width = gtk_column_view_allocate_columns (self, width);
|
||||
|
||||
gtk_widget_measure (self->header, GTK_ORIENTATION_VERTICAL, full_width, &min, &nat, NULL, NULL);
|
||||
if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (self->listview)) == GTK_SCROLL_MINIMUM)
|
||||
header_height = min;
|
||||
else
|
||||
header_height = nat;
|
||||
gtk_widget_allocate (self->header, full_width, header_height, -1, NULL);
|
||||
|
||||
gtk_widget_allocate (GTK_WIDGET (self->listview),
|
||||
full_width, height - header_height, -1,
|
||||
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (0, header_height)));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_activate_cb (GtkListView *listview,
|
||||
guint pos,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
g_signal_emit (self, signals[ACTIVATE], 0, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
while (g_list_model_get_n_items (G_LIST_MODEL (self->columns)) > 0)
|
||||
{
|
||||
GtkColumnViewColumn *column = g_list_model_get_item (G_LIST_MODEL (self->columns), 0);
|
||||
gtk_column_view_remove_column (self, column);
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
g_clear_pointer (&self->header, gtk_widget_unparent);
|
||||
|
||||
g_clear_pointer ((GtkWidget **) &self->listview, gtk_widget_unparent);
|
||||
g_clear_object (&self->factory);
|
||||
|
||||
g_clear_object (&self->sorter);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_finalize (GObject *object)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
g_object_unref (self->columns);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_COLUMNS:
|
||||
g_value_set_object (value, self->columns);
|
||||
break;
|
||||
|
||||
case PROP_HADJUSTMENT:
|
||||
g_value_set_object (value, gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)));
|
||||
break;
|
||||
|
||||
case PROP_HSCROLL_POLICY:
|
||||
g_value_set_enum (value, gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview)));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, gtk_list_view_get_model (self->listview));
|
||||
break;
|
||||
|
||||
case PROP_SHOW_SEPARATORS:
|
||||
g_value_set_boolean (value, gtk_list_view_get_show_separators (self->listview));
|
||||
break;
|
||||
|
||||
case PROP_VADJUSTMENT:
|
||||
g_value_set_object (value, gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->listview)));
|
||||
break;
|
||||
|
||||
case PROP_VSCROLL_POLICY:
|
||||
g_value_set_enum (value, gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (self->listview)));
|
||||
break;
|
||||
|
||||
case PROP_SORTER:
|
||||
g_value_set_object (value, self->sorter);
|
||||
break;
|
||||
|
||||
case PROP_SINGLE_CLICK_ACTIVATE:
|
||||
g_value_set_boolean (value, gtk_column_view_get_single_click_activate (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_HADJUSTMENT:
|
||||
if (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)) != g_value_get_object (value))
|
||||
{
|
||||
gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (self->listview), g_value_get_object (value));
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HADJUSTMENT]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_HSCROLL_POLICY:
|
||||
if (gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview)) != g_value_get_enum (value))
|
||||
{
|
||||
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (self->listview), g_value_get_enum (value));
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HSCROLL_POLICY]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_column_view_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SHOW_SEPARATORS:
|
||||
gtk_column_view_set_show_separators (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_VADJUSTMENT:
|
||||
if (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->listview)) != g_value_get_object (value))
|
||||
{
|
||||
gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (self->listview), g_value_get_object (value));
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VADJUSTMENT]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_VSCROLL_POLICY:
|
||||
if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (self->listview)) != g_value_get_enum (value))
|
||||
{
|
||||
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (self->listview), g_value_get_enum (value));
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VSCROLL_POLICY]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_SINGLE_CLICK_ACTIVATE:
|
||||
gtk_column_view_set_single_click_activate (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_class_init (GtkColumnViewClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
gpointer iface;
|
||||
|
||||
widget_class->measure = gtk_column_view_measure;
|
||||
widget_class->size_allocate = gtk_column_view_allocate;
|
||||
|
||||
gobject_class->dispose = gtk_column_view_dispose;
|
||||
gobject_class->finalize = gtk_column_view_finalize;
|
||||
gobject_class->get_property = gtk_column_view_get_property;
|
||||
gobject_class->set_property = gtk_column_view_set_property;
|
||||
|
||||
/* GtkScrollable implementation */
|
||||
iface = g_type_default_interface_peek (GTK_TYPE_SCROLLABLE);
|
||||
properties[PROP_HADJUSTMENT] =
|
||||
g_param_spec_override ("hadjustment",
|
||||
g_object_interface_find_property (iface, "hadjustment"));
|
||||
properties[PROP_HSCROLL_POLICY] =
|
||||
g_param_spec_override ("hscroll-policy",
|
||||
g_object_interface_find_property (iface, "hscroll-policy"));
|
||||
properties[PROP_VADJUSTMENT] =
|
||||
g_param_spec_override ("vadjustment",
|
||||
g_object_interface_find_property (iface, "vadjustment"));
|
||||
properties[PROP_VSCROLL_POLICY] =
|
||||
g_param_spec_override ("vscroll-policy",
|
||||
g_object_interface_find_property (iface, "vscroll-policy"));
|
||||
|
||||
/**
|
||||
* GtkColumnView:columns:
|
||||
*
|
||||
* The list of columns
|
||||
*/
|
||||
properties[PROP_COLUMNS] =
|
||||
g_param_spec_object ("columns",
|
||||
P_("Columns"),
|
||||
P_("List of columns"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnView:model:
|
||||
*
|
||||
* Model for the items displayed
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("Model for the items displayed"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnView:show-separators:
|
||||
*
|
||||
* Show separators between rows
|
||||
*/
|
||||
properties[PROP_SHOW_SEPARATORS] =
|
||||
g_param_spec_boolean ("show-separators",
|
||||
P_("Show separators"),
|
||||
P_("Show separators between rows"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkColumnView:sorter:
|
||||
*
|
||||
* Sorter with the sorting choices of the user
|
||||
*/
|
||||
properties[PROP_SORTER] =
|
||||
g_param_spec_object ("sorter",
|
||||
P_("Sorter"),
|
||||
P_("Sorter with sorting choices of the user"),
|
||||
GTK_TYPE_SORTER,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnView:single-click-activate:
|
||||
*
|
||||
* Activate rows on single click and select them on hover
|
||||
*/
|
||||
properties[PROP_SINGLE_CLICK_ACTIVATE] =
|
||||
g_param_spec_boolean ("single-click-activate",
|
||||
P_("Single click activate"),
|
||||
P_("Activate rows on single click"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
* GtkColumnView::activate:
|
||||
* @self: The #GtkColumnView
|
||||
* @position: position of item to activate
|
||||
*
|
||||
* The ::activate signal is emitted when a row has been activated by the user,
|
||||
* usually via activating the GtkListBase|list.activate-item action.
|
||||
*
|
||||
* This allows for a convenient way to handle activation in a columnview.
|
||||
* See gtk_list_item_set_activatable() for details on how to use this signal.
|
||||
*/
|
||||
signals[ACTIVATE] =
|
||||
g_signal_new (I_("activate"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_UINT);
|
||||
g_signal_set_va_marshaller (signals[ACTIVATE],
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
g_cclosure_marshal_VOID__UINTv);
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("treeview"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_init (GtkColumnView *self)
|
||||
{
|
||||
self->columns = g_list_store_new (GTK_TYPE_COLUMN_VIEW_COLUMN);
|
||||
|
||||
self->header = gtk_list_item_widget_new (NULL, "header");
|
||||
gtk_widget_set_can_focus (self->header, FALSE);
|
||||
gtk_widget_set_layout_manager (self->header, gtk_column_view_layout_new (self));
|
||||
gtk_widget_set_parent (self->header, GTK_WIDGET (self));
|
||||
|
||||
self->sorter = gtk_column_view_sorter_new ();
|
||||
self->factory = gtk_column_list_item_factory_new (self);
|
||||
self->listview = GTK_LIST_VIEW (gtk_list_view_new_with_factory (
|
||||
GTK_LIST_ITEM_FACTORY (g_object_ref (self->factory))));
|
||||
gtk_widget_set_hexpand (GTK_WIDGET (self->listview), TRUE);
|
||||
gtk_widget_set_vexpand (GTK_WIDGET (self->listview), TRUE);
|
||||
g_signal_connect (self->listview, "activate", G_CALLBACK (gtk_column_view_activate_cb), self);
|
||||
gtk_widget_set_parent (GTK_WIDGET (self->listview), GTK_WIDGET (self));
|
||||
|
||||
gtk_css_node_add_class (gtk_widget_get_css_node (GTK_WIDGET (self)),
|
||||
g_quark_from_static_string (I_("view")));
|
||||
|
||||
gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_new:
|
||||
*
|
||||
* Creates a new empty #GtkColumnView.
|
||||
*
|
||||
* You most likely want to call gtk_column_view_set_factory() to
|
||||
* set up a way to map its items to widgets and gtk_column_view_set_model()
|
||||
* to set a model to provide items next.
|
||||
*
|
||||
* Returns: a new #GtkColumnView
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_column_view_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_COLUMN_VIEW, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_model:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Gets the model that's currently used to read the items displayed.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model in use
|
||||
**/
|
||||
GListModel *
|
||||
gtk_column_view_get_model (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
|
||||
|
||||
return gtk_list_view_get_model (self->listview);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_set_model:
|
||||
* @self: a #GtkColumnView
|
||||
* @model: (allow-none) (transfer none): the model to use or %NULL for none
|
||||
*
|
||||
* Sets the #GListModel to use.
|
||||
*
|
||||
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
|
||||
* Otherwise, @self creates a #GtkSingleSelection for the selection.
|
||||
**/
|
||||
void
|
||||
gtk_column_view_set_model (GtkColumnView *self,
|
||||
GListModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (gtk_list_view_get_model (self->listview) == model)
|
||||
return;
|
||||
|
||||
gtk_list_view_set_model (self->listview, model);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_columns:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Gets the list of columns in this column view. This list is constant over
|
||||
* the lifetime of @self and can be used to monitor changes to the columns
|
||||
* of @self by connecting to the GListModel:items-changed signal.
|
||||
*
|
||||
* Returns: (transfer none): The list managing the columns
|
||||
**/
|
||||
GListModel *
|
||||
gtk_column_view_get_columns (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
|
||||
|
||||
return G_LIST_MODEL (self->columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_set_show_separators:
|
||||
* @self: a #GtkColumnView
|
||||
* @show_separators: %TRUE to show separators
|
||||
*
|
||||
* Sets whether the list should show separators
|
||||
* between rows.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_set_show_separators (GtkColumnView *self,
|
||||
gboolean show_separators)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
|
||||
if (gtk_list_view_get_show_separators (self->listview) == show_separators)
|
||||
return;
|
||||
|
||||
gtk_list_view_set_show_separators (self->listview, show_separators);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SEPARATORS]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_show_separators:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Returns whether the list box should show separators
|
||||
* between rows.
|
||||
*
|
||||
* Returns: %TRUE if the list box shows separators
|
||||
*/
|
||||
gboolean
|
||||
gtk_column_view_get_show_separators (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), FALSE);
|
||||
|
||||
return gtk_list_view_get_show_separators (self->listview);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_append_column:
|
||||
* @self: a #GtkColumnView
|
||||
* @column: a #GtkColumnViewColumn that hasn't been added to a
|
||||
* #GtkColumnView yet
|
||||
*
|
||||
* Appends the @column to the end of the columns in @self.
|
||||
**/
|
||||
void
|
||||
gtk_column_view_append_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column));
|
||||
g_return_if_fail (gtk_column_view_column_get_column_view (column) == NULL);
|
||||
|
||||
gtk_column_view_column_set_column_view (column, self);
|
||||
g_list_store_append (self->columns, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_remove_column:
|
||||
* @self: a #GtkColumnView
|
||||
* @column: a #GtkColumnViewColumn that's part of @self
|
||||
*
|
||||
* Removes the @column from the list of columns of @self.
|
||||
**/
|
||||
void
|
||||
gtk_column_view_remove_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column));
|
||||
g_return_if_fail (gtk_column_view_column_get_column_view (column) == self);
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
{
|
||||
GtkColumnViewColumn *item = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
|
||||
g_object_unref (item);
|
||||
if (item == column)
|
||||
break;
|
||||
}
|
||||
|
||||
gtk_column_view_sorter_remove_column (GTK_COLUMN_VIEW_SORTER (self->sorter), column);
|
||||
gtk_column_view_column_set_column_view (column, NULL);
|
||||
g_list_store_remove (self->columns, i);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_measure_across (GtkColumnView *self,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
guint i;
|
||||
int min, nat;
|
||||
|
||||
min = 0;
|
||||
nat = 0;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
int col_min, col_nat;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
gtk_column_view_column_measure (column, &col_min, &col_nat);
|
||||
min += col_min;
|
||||
nat += col_nat;
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
*minimum = min;
|
||||
*natural = nat;
|
||||
}
|
||||
|
||||
GtkListItemWidget *
|
||||
gtk_column_view_get_header_widget (GtkColumnView *self)
|
||||
{
|
||||
return GTK_LIST_ITEM_WIDGET (self->header);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_sorter:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Returns the sorter associated with users sorting choices in
|
||||
* the column view.
|
||||
*
|
||||
* To allow users to customizable sorting by clicking on column
|
||||
* headers, this sorter needs to be set on the sort
|
||||
* model(s) underneath the model that is displayed
|
||||
* by the view.
|
||||
*
|
||||
* See gtk_column_view_column_get_sorter() for setting up
|
||||
* per-column sorting.
|
||||
*
|
||||
* Returns: (transfer none): the #GtkSorter of @self
|
||||
*/
|
||||
GtkSorter *
|
||||
gtk_column_view_get_sorter (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
|
||||
|
||||
return self->sorter;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_sort_by_column:
|
||||
* @self: a #GtkColumnView
|
||||
* @column: (allow-none): the #GtkColumnViewColumn to sort by, or %NULL
|
||||
* @direction: the direction to sort in
|
||||
*
|
||||
* Sets the sorting of the view.
|
||||
*
|
||||
* This function should be used to set up the initial sorting. At runtime,
|
||||
* users can change the sorting of a column view by clicking on the list headers.
|
||||
*
|
||||
* This call only has an effect if the sorter returned by gtk_column_view_get_sorter()
|
||||
* is set on a sort model, and gtk_column_view_column_set_sorter() has been called
|
||||
* on @column to associate a sorter with the column.
|
||||
*
|
||||
* If @column is %NULL, the view will be unsorted.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_sort_by_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column,
|
||||
GtkSortType direction)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (column == NULL || GTK_IS_COLUMN_VIEW_COLUMN (column));
|
||||
g_return_if_fail (column == NULL || gtk_column_view_column_get_column_view (column) == self);
|
||||
|
||||
if (column == NULL)
|
||||
gtk_column_view_sorter_clear (GTK_COLUMN_VIEW_SORTER (self->sorter));
|
||||
else
|
||||
gtk_column_view_sorter_set_column (GTK_COLUMN_VIEW_SORTER (self->sorter),
|
||||
column,
|
||||
direction == GTK_SORT_DESCENDING);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_set_single_click_activate:
|
||||
* @self: a #GtkColumnView
|
||||
* @single_click_activate: %TRUE to activate items on single click
|
||||
*
|
||||
* Sets whether rows should be activated on single click and
|
||||
* selected on hover.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_set_single_click_activate (GtkColumnView *self,
|
||||
gboolean single_click_activate)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
|
||||
if (single_click_activate == gtk_list_view_get_single_click_activate (self->listview))
|
||||
return;
|
||||
|
||||
gtk_list_view_set_single_click_activate (self->listview, single_click_activate);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SINGLE_CLICK_ACTIVATE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_single_click_activate:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Returns whether rows will be activated on single click and
|
||||
* selected on hover.
|
||||
*
|
||||
* Returns: %TRUE if rows are activated on single click
|
||||
*/
|
||||
gboolean
|
||||
gtk_column_view_get_single_click_activate (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), FALSE);
|
||||
|
||||
return gtk_list_view_get_single_click_activate (self->listview);
|
||||
}
|
92
gtk/gtkcolumnview.h
Normal file
92
gtk/gtkcolumnview.h
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_H__
|
||||
#define __GTK_COLUMN_VIEW_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
#include <gtk/gtksortlistmodel.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW (gtk_column_view_get_type ())
|
||||
#define GTK_COLUMN_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW, GtkColumnView))
|
||||
#define GTK_COLUMN_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW, GtkColumnViewClass))
|
||||
#define GTK_IS_COLUMN_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW))
|
||||
#define GTK_IS_COLUMN_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW))
|
||||
#define GTK_COLUMN_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW, GtkColumnViewClass))
|
||||
|
||||
/**
|
||||
* GtkColumnView:
|
||||
*
|
||||
* GtkColumnView is the simple list implementation for GTK's list widgets.
|
||||
*/
|
||||
typedef struct _GtkColumnView GtkColumnView;
|
||||
typedef struct _GtkColumnViewClass GtkColumnViewClass;
|
||||
/* forward declaration */
|
||||
typedef struct _GtkColumnViewColumn GtkColumnViewColumn;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_column_view_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_column_view_new (void);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_column_view_get_columns (GtkColumnView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_append_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_remove_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_column_view_get_model (GtkColumnView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_set_model (GtkColumnView *self,
|
||||
GListModel *model);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_get_show_separators (GtkColumnView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_set_show_separators (GtkColumnView *self,
|
||||
gboolean show_separators);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSorter * gtk_column_view_get_sorter (GtkColumnView *self);
|
||||
|
||||
void gtk_column_view_sort_by_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column,
|
||||
GtkSortType direction);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_set_single_click_activate (GtkColumnView *self,
|
||||
gboolean single_click_activate);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_get_single_click_activate (GtkColumnView *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_H__ */
|
190
gtk/gtkcolumnviewcell.c
Normal file
190
gtk/gtkcolumnviewcell.c
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewcellprivate.h"
|
||||
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistitemwidgetprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
struct _GtkColumnViewCell
|
||||
{
|
||||
GtkListItemWidget parent_instance;
|
||||
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
/* This list isn't sorted - next/prev refer to list elements, not rows in the list */
|
||||
GtkColumnViewCell *next_cell;
|
||||
GtkColumnViewCell *prev_cell;
|
||||
};
|
||||
|
||||
struct _GtkColumnViewCellClass
|
||||
{
|
||||
GtkListItemWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnViewCell, gtk_column_view_cell, GTK_TYPE_LIST_ITEM_WIDGET)
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_allocate (child, width, height, baseline, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_root (GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_column_view_cell_parent_class)->root (widget);
|
||||
|
||||
self->next_cell = gtk_column_view_column_get_first_cell (self->column);
|
||||
if (self->next_cell)
|
||||
self->next_cell->prev_cell = self;
|
||||
|
||||
gtk_column_view_column_add_cell (self->column, self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_unroot (GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget);
|
||||
|
||||
gtk_column_view_column_remove_cell (self->column, self);
|
||||
|
||||
if (self->prev_cell)
|
||||
self->prev_cell->next_cell = self->next_cell;
|
||||
if (self->next_cell)
|
||||
self->next_cell->prev_cell = self->prev_cell;
|
||||
|
||||
self->prev_cell = NULL;
|
||||
self->next_cell = NULL;
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_column_view_cell_parent_class)->unroot (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (object);
|
||||
|
||||
g_clear_object (&self->column);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_cell_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_class_init (GtkColumnViewCellClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
widget_class->root = gtk_column_view_cell_root;
|
||||
widget_class->unroot = gtk_column_view_cell_unroot;
|
||||
widget_class->measure = gtk_column_view_cell_measure;
|
||||
widget_class->size_allocate = gtk_column_view_cell_size_allocate;
|
||||
|
||||
gobject_class->dispose = gtk_column_view_cell_dispose;
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("cell"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_resize_func (GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget);
|
||||
|
||||
if (self->column)
|
||||
gtk_column_view_column_queue_resize (self->column);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_init (GtkColumnViewCell *self)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
|
||||
gtk_widget_set_can_focus (widget, FALSE);
|
||||
/* FIXME: Figure out if settting the manager class to INVALID should work */
|
||||
gtk_widget_set_layout_manager (widget, NULL);
|
||||
widget->priv->resize_func = gtk_column_view_cell_resize_func;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_column_view_cell_new (GtkColumnViewColumn *column)
|
||||
{
|
||||
GtkColumnViewCell *cell;
|
||||
|
||||
cell = g_object_new (GTK_TYPE_COLUMN_VIEW_CELL,
|
||||
"factory", gtk_column_view_column_get_factory (column),
|
||||
NULL);
|
||||
|
||||
cell->column = g_object_ref (column);
|
||||
|
||||
return GTK_WIDGET (cell);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_cell_remove (GtkColumnViewCell *self)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
|
||||
gtk_list_item_widget_remove_child (GTK_LIST_ITEM_WIDGET (gtk_widget_get_parent (widget)), widget);
|
||||
}
|
||||
|
||||
GtkColumnViewCell *
|
||||
gtk_column_view_cell_get_next (GtkColumnViewCell *self)
|
||||
{
|
||||
return self->next_cell;
|
||||
}
|
||||
|
||||
GtkColumnViewCell *
|
||||
gtk_column_view_cell_get_prev (GtkColumnViewCell *self)
|
||||
{
|
||||
return self->prev_cell;
|
||||
}
|
||||
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_cell_get_column (GtkColumnViewCell *self)
|
||||
{
|
||||
return self->column;
|
||||
}
|
49
gtk/gtkcolumnviewcellprivate.h
Normal file
49
gtk/gtkcolumnviewcellprivate.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_CELL_PRIVATE_H__
|
||||
#define __GTK_COLUMN_VIEW_CELL_PRIVATE_H__
|
||||
|
||||
#include "gtkcolumnviewcolumn.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW_CELL (gtk_column_view_cell_get_type ())
|
||||
#define GTK_COLUMN_VIEW_CELL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCell))
|
||||
#define GTK_COLUMN_VIEW_CELL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCellClass))
|
||||
#define GTK_IS_COLUMN_VIEW_CELL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_CELL))
|
||||
#define GTK_IS_COLUMN_VIEW_CELL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_CELL))
|
||||
#define GTK_COLUMN_VIEW_CELL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCellClass))
|
||||
|
||||
typedef struct _GtkColumnViewCell GtkColumnViewCell;
|
||||
typedef struct _GtkColumnViewCellClass GtkColumnViewCellClass;
|
||||
|
||||
GType gtk_column_view_cell_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkWidget * gtk_column_view_cell_new (GtkColumnViewColumn *column);
|
||||
|
||||
void gtk_column_view_cell_remove (GtkColumnViewCell *self);
|
||||
|
||||
GtkColumnViewCell * gtk_column_view_cell_get_next (GtkColumnViewCell *self);
|
||||
GtkColumnViewCell * gtk_column_view_cell_get_prev (GtkColumnViewCell *self);
|
||||
GtkColumnViewColumn * gtk_column_view_cell_get_column (GtkColumnViewCell *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_CELL_PRIVATE_H__ */
|
649
gtk/gtkcolumnviewcolumn.c
Normal file
649
gtk/gtkcolumnviewcolumn.c
Normal file
@ -0,0 +1,649 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkcolumnviewsorterprivate.h"
|
||||
|
||||
#include "gtkcolumnviewprivate.h"
|
||||
#include "gtkcolumnviewtitleprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
#include "gtklistitemwidgetprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtksizegroup.h"
|
||||
#include "gtkstylecontext.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtksorter.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcolumnviewcolumn
|
||||
* @title: GtkColumnViewColumn
|
||||
* @short_description: The column added to GtkColumnView
|
||||
* @see_also: #GtkColumnView
|
||||
*
|
||||
* GtkColumnViewColumn represents the columns being added to #GtkColumnView.
|
||||
*/
|
||||
|
||||
struct _GtkColumnViewColumn
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkListItemFactory *factory;
|
||||
char *title;
|
||||
GtkSorter *sorter;
|
||||
|
||||
/* data for the view */
|
||||
GtkColumnView *view;
|
||||
GtkWidget *header;
|
||||
|
||||
int minimum_size_request;
|
||||
int natural_size_request;
|
||||
int allocation_offset;
|
||||
int allocation_size;
|
||||
|
||||
/* This list isn't sorted - this is just caching for performance */
|
||||
GtkColumnViewCell *first_cell; /* no reference, just caching */
|
||||
};
|
||||
|
||||
struct _GtkColumnViewColumnClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_COLUMN_VIEW,
|
||||
PROP_FACTORY,
|
||||
PROP_TITLE,
|
||||
PROP_SORTER,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnViewColumn, gtk_column_view_column, G_TYPE_OBJECT)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_column_view_column_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
|
||||
|
||||
g_assert (self->view == NULL); /* would hold a ref otherwise */
|
||||
g_assert (self->first_cell == NULL); /* no view = no children */
|
||||
|
||||
g_clear_object (&self->factory);
|
||||
g_clear_object (&self->sorter);
|
||||
g_clear_pointer (&self->title, g_free);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_column_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_COLUMN_VIEW:
|
||||
g_value_set_object (value, self->view);
|
||||
break;
|
||||
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, self->factory);
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
g_value_set_string (value, self->title);
|
||||
break;
|
||||
|
||||
case PROP_SORTER:
|
||||
g_value_set_object (value, self->sorter);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
gtk_column_view_column_set_factory (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
gtk_column_view_column_set_title (self, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_SORTER:
|
||||
gtk_column_view_column_set_sorter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gtk_column_view_column_dispose;
|
||||
gobject_class->get_property = gtk_column_view_column_get_property;
|
||||
gobject_class->set_property = gtk_column_view_column_set_property;
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:column-view:
|
||||
*
|
||||
* #GtkColumnView this column is a part of
|
||||
*/
|
||||
properties[PROP_COLUMN_VIEW] =
|
||||
g_param_spec_object ("column-view",
|
||||
P_("Column view"),
|
||||
P_("Column view this column is a part of"),
|
||||
GTK_TYPE_COLUMN_VIEW,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:factory:
|
||||
*
|
||||
* Factory for populating list items
|
||||
*/
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
P_("Factory"),
|
||||
P_("Factory for populating list items"),
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:title:
|
||||
*
|
||||
* Title displayed in the header
|
||||
*/
|
||||
properties[PROP_TITLE] =
|
||||
g_param_spec_string ("title",
|
||||
P_("Title"),
|
||||
P_("Title displayed in the header"),
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:sorter:
|
||||
*
|
||||
* Sorter for sorting items according to this column
|
||||
*/
|
||||
properties[PROP_SORTER] =
|
||||
g_param_spec_object ("sorter",
|
||||
P_("Sorter"),
|
||||
P_("Sorter for sorting items according to this column"),
|
||||
GTK_TYPE_SORTER,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_init (GtkColumnViewColumn *self)
|
||||
{
|
||||
self->minimum_size_request = -1;
|
||||
self->natural_size_request = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_new:
|
||||
* @title: (nullable): Title to use for this column
|
||||
*
|
||||
* Creates a new #GtkColumnViewColumn.
|
||||
*
|
||||
* You most likely want to call gtk_column_add_column() next.
|
||||
*
|
||||
* Returns: a new #GtkColumnViewColumn
|
||||
**/
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_column_new (const char *title)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_COLUMN_VIEW_COLUMN,
|
||||
"title", title,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_new_with_factory:
|
||||
* @title: (nullable): Title to use for this column
|
||||
* @factory: (transfer full): The factory to populate items with
|
||||
*
|
||||
* Creates a new #GtkColumnViewColumn that uses the given @factory for
|
||||
* mapping items to widgets.
|
||||
*
|
||||
* You most likely want to call gtk_column_add_column() next.
|
||||
*
|
||||
* The function takes ownership of the
|
||||
* argument, so you can write code like
|
||||
* ```
|
||||
* column = gtk_column_view_column_new_with_factory (_("Name"),
|
||||
* gtk_builder_list_item_factory_new_from_resource ("/name.ui"));
|
||||
* ```
|
||||
*
|
||||
* Returns: a new #GtkColumnViewColumn using the given @factory
|
||||
**/
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_column_new_with_factory (const char *title,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
GtkColumnViewColumn *result;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_COLUMN_VIEW_COLUMN,
|
||||
"factory", factory,
|
||||
"title", title,
|
||||
NULL);
|
||||
|
||||
g_object_unref (factory);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
GtkColumnViewCell *
|
||||
gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self)
|
||||
{
|
||||
return self->first_cell;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_add_cell (GtkColumnViewColumn *self,
|
||||
GtkColumnViewCell *cell)
|
||||
{
|
||||
self->first_cell = cell;
|
||||
|
||||
gtk_column_view_column_queue_resize (self);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_remove_cell (GtkColumnViewColumn *self,
|
||||
GtkColumnViewCell *cell)
|
||||
{
|
||||
if (cell == self->first_cell)
|
||||
self->first_cell = gtk_column_view_cell_get_next (cell);
|
||||
|
||||
gtk_column_view_column_queue_resize (self);
|
||||
gtk_widget_queue_resize (GTK_WIDGET (cell));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_queue_resize (GtkColumnViewColumn *self)
|
||||
{
|
||||
GtkColumnViewCell *cell;
|
||||
|
||||
if (self->minimum_size_request < 0)
|
||||
return;
|
||||
|
||||
self->minimum_size_request = -1;
|
||||
self->natural_size_request = -1;
|
||||
|
||||
if (self->header)
|
||||
gtk_widget_queue_resize (self->header);
|
||||
|
||||
for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell))
|
||||
{
|
||||
gtk_widget_queue_resize (GTK_WIDGET (cell));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_measure (GtkColumnViewColumn *self,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
if (self->minimum_size_request < 0)
|
||||
{
|
||||
GtkColumnViewCell *cell;
|
||||
int min, nat, cell_min, cell_nat;
|
||||
|
||||
if (self->header)
|
||||
{
|
||||
gtk_widget_measure (self->header, GTK_ORIENTATION_HORIZONTAL, -1, &min, &nat, NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
min = 0;
|
||||
nat = 0;
|
||||
}
|
||||
|
||||
for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell))
|
||||
{
|
||||
gtk_widget_measure (GTK_WIDGET (cell),
|
||||
GTK_ORIENTATION_HORIZONTAL,
|
||||
-1,
|
||||
&cell_min, &cell_nat,
|
||||
NULL, NULL);
|
||||
|
||||
min = MAX (min, cell_min);
|
||||
nat = MAX (nat, cell_nat);
|
||||
}
|
||||
|
||||
self->minimum_size_request = min;
|
||||
self->natural_size_request = nat;
|
||||
}
|
||||
|
||||
*minimum = self->minimum_size_request;
|
||||
*natural = self->natural_size_request;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_allocate (GtkColumnViewColumn *self,
|
||||
int offset,
|
||||
int size)
|
||||
{
|
||||
self->allocation_offset = offset;
|
||||
self->allocation_size = size;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_get_allocation (GtkColumnViewColumn *self,
|
||||
int *offset,
|
||||
int *size)
|
||||
{
|
||||
if (offset)
|
||||
*offset = self->allocation_offset;
|
||||
if (size)
|
||||
*size = self->allocation_size;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_create_cells (GtkColumnViewColumn *self)
|
||||
{
|
||||
GtkWidget *row;
|
||||
|
||||
if (self->first_cell)
|
||||
return;
|
||||
|
||||
for (row = gtk_widget_get_first_child (GTK_WIDGET (self->view));
|
||||
row != NULL;
|
||||
row = gtk_widget_get_next_sibling (row))
|
||||
{
|
||||
GtkListItemWidget *list_item;
|
||||
GtkWidget *cell;
|
||||
|
||||
if (!gtk_widget_get_root (row))
|
||||
continue;
|
||||
|
||||
list_item = GTK_LIST_ITEM_WIDGET (row);
|
||||
cell = gtk_column_view_cell_new (self);
|
||||
gtk_list_item_widget_add_child (list_item, cell);
|
||||
gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (cell),
|
||||
gtk_list_item_widget_get_position (list_item),
|
||||
gtk_list_item_widget_get_item (list_item),
|
||||
gtk_list_item_widget_get_selected (list_item));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_remove_cells (GtkColumnViewColumn *self)
|
||||
{
|
||||
while (self->first_cell)
|
||||
gtk_column_view_cell_remove (self->first_cell);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_create_header (GtkColumnViewColumn *self)
|
||||
{
|
||||
if (self->header != NULL)
|
||||
return;
|
||||
|
||||
self->header = gtk_column_view_title_new (self);
|
||||
gtk_list_item_widget_add_child (gtk_column_view_get_header_widget (self->view),
|
||||
self->header);
|
||||
gtk_column_view_column_queue_resize (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_remove_header (GtkColumnViewColumn *self)
|
||||
{
|
||||
if (self->header == NULL)
|
||||
return;
|
||||
|
||||
gtk_list_item_widget_remove_child (gtk_column_view_get_header_widget (self->view),
|
||||
self->header);
|
||||
self->header = NULL;
|
||||
gtk_column_view_column_queue_resize (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_ensure_cells (GtkColumnViewColumn *self)
|
||||
{
|
||||
if (self->view && gtk_widget_get_root (GTK_WIDGET (self->view)))
|
||||
gtk_column_view_column_create_cells (self);
|
||||
else
|
||||
gtk_column_view_column_remove_cells (self);
|
||||
|
||||
if (self->view)
|
||||
gtk_column_view_column_create_header (self);
|
||||
else
|
||||
gtk_column_view_column_remove_header (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_column_view:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Gets the column view that's currently displaying this column.
|
||||
*
|
||||
* If @self has not been added to a column view yet, %NULL is returned.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The column view displaying @self.
|
||||
**/
|
||||
GtkColumnView *
|
||||
gtk_column_view_column_get_column_view (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
|
||||
|
||||
return self->view;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_set_column_view (GtkColumnViewColumn *self,
|
||||
GtkColumnView *view)
|
||||
{
|
||||
if (self->view == view)
|
||||
return;
|
||||
|
||||
gtk_column_view_column_remove_cells (self);
|
||||
gtk_column_view_column_remove_header (self);
|
||||
|
||||
self->view = view;
|
||||
|
||||
gtk_column_view_column_ensure_cells (self);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLUMN_VIEW]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_factory:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Gets the factory that's currently used to populate list items for
|
||||
* this column.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The factory in use
|
||||
**/
|
||||
GtkListItemFactory *
|
||||
gtk_column_view_column_get_factory (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
|
||||
|
||||
return self->factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_factory:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
|
||||
*
|
||||
* Sets the #GtkListItemFactory to use for populating list items for this
|
||||
* column.
|
||||
**/
|
||||
void
|
||||
gtk_column_view_column_set_factory (GtkColumnViewColumn *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
|
||||
|
||||
if (!g_set_object (&self->factory, factory))
|
||||
return;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_title:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @title: (nullable): Title to use for this column
|
||||
*
|
||||
* Sets the title of this column. The title is displayed in the header of a
|
||||
* #GtkColumnView for this column and is therefor user-facing text that should
|
||||
* be translated.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_column_set_title (GtkColumnViewColumn *self,
|
||||
const char *title)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
|
||||
if (g_strcmp0 (self->title, title) == 0)
|
||||
return;
|
||||
|
||||
g_free (self->title);
|
||||
self->title = g_strdup (title);
|
||||
|
||||
if (self->header)
|
||||
gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_title:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Returns the title set with gtk_column_view_column_set_title().
|
||||
*
|
||||
* Returns: (nullable) The column's title
|
||||
*/
|
||||
const char *
|
||||
gtk_column_view_column_get_title (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), FALSE);
|
||||
|
||||
return self->title;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
gtk_column_view_column_add_to_sorter (GtkColumnViewColumn *self)
|
||||
{
|
||||
if (self->view == NULL)
|
||||
return;
|
||||
|
||||
gtk_column_view_sorter_add_column (GTK_COLUMN_VIEW_SORTER (gtk_column_view_get_sorter (self->view)), self);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
gtk_column_view_column_remove_from_sorter (GtkColumnViewColumn *self)
|
||||
{
|
||||
if (self->view == NULL)
|
||||
return;
|
||||
|
||||
gtk_column_view_sorter_remove_column (GTK_COLUMN_VIEW_SORTER (gtk_column_view_get_sorter (self->view)), self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_sorter:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @sorter: (nullable): the #GtkSorter to associate with @column
|
||||
*
|
||||
* Associates a sorter with the column.
|
||||
*
|
||||
* This sorter can be made active by clicking on the column
|
||||
* header, or by calling gtk_column_view_sort_by_column().
|
||||
*/
|
||||
void
|
||||
gtk_column_view_column_set_sorter (GtkColumnViewColumn *self,
|
||||
GtkSorter *sorter)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter));
|
||||
|
||||
if (!g_set_object (&self->sorter, sorter))
|
||||
return;
|
||||
|
||||
gtk_column_view_column_remove_from_sorter (self);
|
||||
|
||||
if (self->header)
|
||||
gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_sorter:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Returns the sorter that is associated with the column.
|
||||
*
|
||||
* Returns: (transfer none): the #GtkSorter of @self
|
||||
*/
|
||||
GtkSorter *
|
||||
gtk_column_view_column_get_sorter (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
|
||||
|
||||
return self->sorter;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_notify_sort (GtkColumnViewColumn *self)
|
||||
{
|
||||
if (self->header)
|
||||
gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header));
|
||||
}
|
77
gtk/gtkcolumnviewcolumn.h
Normal file
77
gtk/gtkcolumnviewcolumn.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_COLUMN_H__
|
||||
#define __GTK_COLUMN_VIEW_COLUMN_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkcolumnview.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW_COLUMN (gtk_column_view_column_get_type ())
|
||||
#define GTK_COLUMN_VIEW_COLUMN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumn))
|
||||
#define GTK_COLUMN_VIEW_COLUMN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumnClass))
|
||||
#define GTK_IS_COLUMN_VIEW_COLUMN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_COLUMN))
|
||||
#define GTK_IS_COLUMN_VIEW_COLUMN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_COLUMN))
|
||||
#define GTK_COLUMN_VIEW_COLUMN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumnClass))
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:
|
||||
*
|
||||
* GtkColumnViewColumns are added to #GtkColumnViews.
|
||||
*/
|
||||
typedef struct _GtkColumnViewColumnClass GtkColumnViewColumnClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_column_view_column_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkColumnViewColumn * gtk_column_view_column_new (const char *title);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkColumnViewColumn * gtk_column_view_column_new_with_factory (const char *title,
|
||||
GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkColumnView * gtk_column_view_column_get_column_view (GtkColumnViewColumn *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_factory (GtkColumnViewColumn *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory * gtk_column_view_column_get_factory (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_title (GtkColumnViewColumn *self,
|
||||
const char *title);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const char * gtk_column_view_column_get_title (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_sorter (GtkColumnViewColumn *self,
|
||||
GtkSorter *sorter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSorter * gtk_column_view_column_get_sorter (GtkColumnViewColumn *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */
|
50
gtk/gtkcolumnviewcolumnprivate.h
Normal file
50
gtk/gtkcolumnviewcolumnprivate.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__
|
||||
#define __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__
|
||||
|
||||
#include "gtk/gtkcolumnviewcolumn.h"
|
||||
|
||||
#include "gtk/gtkcolumnviewcellprivate.h"
|
||||
|
||||
|
||||
void gtk_column_view_column_set_column_view (GtkColumnViewColumn *self,
|
||||
GtkColumnView *view);
|
||||
|
||||
void gtk_column_view_column_add_cell (GtkColumnViewColumn *self,
|
||||
GtkColumnViewCell *cell);
|
||||
void gtk_column_view_column_remove_cell (GtkColumnViewColumn *self,
|
||||
GtkColumnViewCell *cell);
|
||||
GtkColumnViewCell * gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self);
|
||||
|
||||
void gtk_column_view_column_queue_resize (GtkColumnViewColumn *self);
|
||||
void gtk_column_view_column_measure (GtkColumnViewColumn *self,
|
||||
int *minimum,
|
||||
int *natural);
|
||||
void gtk_column_view_column_allocate (GtkColumnViewColumn *self,
|
||||
int offset,
|
||||
int size);
|
||||
void gtk_column_view_column_get_allocation (GtkColumnViewColumn *self,
|
||||
int *offset,
|
||||
int *size);
|
||||
|
||||
void gtk_column_view_column_notify_sort (GtkColumnViewColumn *self);
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__ */
|
154
gtk/gtkcolumnviewlayout.c
Normal file
154
gtk/gtkcolumnviewlayout.c
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewlayoutprivate.h"
|
||||
|
||||
#include "gtkcolumnviewcellprivate.h"
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkcolumnviewprivate.h"
|
||||
#include "gtkcolumnviewtitleprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
struct _GtkColumnViewLayout
|
||||
{
|
||||
GtkLayoutManager parent_instance;
|
||||
|
||||
GtkColumnView *view; /* no reference */
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnViewLayout, gtk_column_view_layout, GTK_TYPE_LAYOUT_MANAGER)
|
||||
|
||||
static void
|
||||
gtk_column_view_layout_measure_along (GtkColumnViewLayout *self,
|
||||
GtkWidget *widget,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkOrientation orientation = GTK_ORIENTATION_VERTICAL;
|
||||
GtkWidget *child;
|
||||
|
||||
for (child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
int child_min = 0;
|
||||
int child_nat = 0;
|
||||
int child_min_baseline = -1;
|
||||
int child_nat_baseline = -1;
|
||||
|
||||
gtk_widget_measure (child, orientation, for_size,
|
||||
&child_min, &child_nat,
|
||||
&child_min_baseline, &child_nat_baseline);
|
||||
|
||||
*minimum = MAX (*minimum, child_min);
|
||||
*natural = MAX (*natural, child_nat);
|
||||
|
||||
if (child_min_baseline > -1)
|
||||
*minimum_baseline = MAX (*minimum_baseline, child_min_baseline);
|
||||
if (child_nat_baseline > -1)
|
||||
*natural_baseline = MAX (*natural_baseline, child_nat_baseline);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_layout_measure (GtkLayoutManager *layout,
|
||||
GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkColumnViewLayout *self = GTK_COLUMN_VIEW_LAYOUT (layout);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
gtk_column_view_measure_across (GTK_COLUMN_VIEW (self->view),
|
||||
minimum,
|
||||
natural);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_column_view_layout_measure_along (self,
|
||||
widget,
|
||||
for_size,
|
||||
minimum,
|
||||
natural,
|
||||
minimum_baseline,
|
||||
natural_baseline);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_layout_allocate (GtkLayoutManager *layout_manager,
|
||||
GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkWidget *child;
|
||||
|
||||
for (child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
int col_x, col_width;
|
||||
|
||||
if (GTK_IS_COLUMN_VIEW_CELL (child))
|
||||
column = gtk_column_view_cell_get_column (GTK_COLUMN_VIEW_CELL (child));
|
||||
else
|
||||
column = gtk_column_view_title_get_column (GTK_COLUMN_VIEW_TITLE (child));
|
||||
|
||||
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
|
||||
gtk_widget_size_allocate (child, &(GtkAllocation) { col_x, 0, col_width, height }, baseline);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_layout_class_init (GtkColumnViewLayoutClass *klass)
|
||||
{
|
||||
GtkLayoutManagerClass *layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (klass);
|
||||
|
||||
layout_manager_class->measure = gtk_column_view_layout_measure;
|
||||
layout_manager_class->allocate = gtk_column_view_layout_allocate;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_layout_init (GtkColumnViewLayout *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkLayoutManager *
|
||||
gtk_column_view_layout_new (GtkColumnView *view)
|
||||
{
|
||||
GtkColumnViewLayout *result;
|
||||
|
||||
result = g_object_new (GTK_TYPE_COLUMN_VIEW_LAYOUT, NULL);
|
||||
|
||||
result->view = view;
|
||||
|
||||
return GTK_LAYOUT_MANAGER (result);
|
||||
}
|
35
gtk/gtkcolumnviewlayoutprivate.h
Normal file
35
gtk/gtkcolumnviewlayoutprivate.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__
|
||||
#define __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__
|
||||
|
||||
#include <gtk/gtkcolumnview.h>
|
||||
#include <gtk/gtklayoutmanager.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW_LAYOUT (gtk_column_view_layout_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GtkColumnViewLayout, gtk_column_view_layout, GTK, COLUMN_VIEW_LAYOUT, GtkLayoutManager)
|
||||
|
||||
GtkLayoutManager * gtk_column_view_layout_new (GtkColumnView *view);
|
||||
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__ */
|
34
gtk/gtkcolumnviewprivate.h
Normal file
34
gtk/gtkcolumnviewprivate.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_PRIVATE_H__
|
||||
#define __GTK_COLUMN_VIEW_PRIVATE_H__
|
||||
|
||||
#include "gtk/gtkcolumnview.h"
|
||||
|
||||
#include "gtk/gtkcolumnviewsorterprivate.h"
|
||||
#include "gtk/gtklistitemwidgetprivate.h"
|
||||
|
||||
GtkListItemWidget * gtk_column_view_get_header_widget (GtkColumnView *self);
|
||||
|
||||
void gtk_column_view_measure_across (GtkColumnView *self,
|
||||
int *minimum,
|
||||
int *natural);
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_PRIVATE_H__ */
|
323
gtk/gtkcolumnviewsorter.c
Normal file
323
gtk/gtkcolumnviewsorter.c
Normal file
@ -0,0 +1,323 @@
|
||||
/*
|
||||
* Copyright © 2019 Matthias Clasen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewsorterprivate.h"
|
||||
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
GtkSorter *sorter;
|
||||
gboolean inverted;
|
||||
gulong changed_id;
|
||||
} Sorter;
|
||||
|
||||
static void
|
||||
free_sorter (gpointer data)
|
||||
{
|
||||
Sorter *s = data;
|
||||
|
||||
g_signal_handler_disconnect (s->sorter, s->changed_id);
|
||||
g_object_unref (s->sorter);
|
||||
g_object_unref (s->column);
|
||||
g_free (s);
|
||||
}
|
||||
|
||||
struct _GtkColumnViewSorter
|
||||
{
|
||||
GtkSorter parent_instance;
|
||||
|
||||
GSequence *sorters;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnViewSorter, gtk_column_view_sorter, GTK_TYPE_SORTER)
|
||||
|
||||
static GtkOrdering
|
||||
gtk_column_view_sorter_compare (GtkSorter *sorter,
|
||||
gpointer item1,
|
||||
gpointer item2)
|
||||
{
|
||||
GtkColumnViewSorter *self = GTK_COLUMN_VIEW_SORTER (sorter);
|
||||
GtkOrdering result = GTK_ORDERING_EQUAL;
|
||||
GSequenceIter *iter;
|
||||
|
||||
for (iter = g_sequence_get_begin_iter (self->sorters);
|
||||
!g_sequence_iter_is_end (iter);
|
||||
iter = g_sequence_iter_next (iter))
|
||||
{
|
||||
Sorter *s = g_sequence_get (iter);
|
||||
|
||||
result = gtk_sorter_compare (s->sorter, item1, item2);
|
||||
if (s->inverted)
|
||||
result = - result;
|
||||
|
||||
if (result != GTK_ORDERING_EQUAL)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GtkSorterOrder
|
||||
gtk_column_view_sorter_get_order (GtkSorter *sorter)
|
||||
{
|
||||
GtkColumnViewSorter *self = GTK_COLUMN_VIEW_SORTER (sorter);
|
||||
GtkSorterOrder result = GTK_SORTER_ORDER_NONE;
|
||||
GSequenceIter *iter;
|
||||
|
||||
for (iter = g_sequence_get_begin_iter (self->sorters);
|
||||
!g_sequence_iter_is_end (iter);
|
||||
iter = g_sequence_iter_next (iter))
|
||||
{
|
||||
Sorter *s = g_sequence_get (iter);
|
||||
|
||||
switch (gtk_sorter_get_order (s->sorter))
|
||||
{
|
||||
case GTK_SORTER_ORDER_PARTIAL:
|
||||
result = GTK_SORTER_ORDER_PARTIAL;
|
||||
break;
|
||||
case GTK_SORTER_ORDER_NONE:
|
||||
break;
|
||||
case GTK_SORTER_ORDER_TOTAL:
|
||||
return GTK_SORTER_ORDER_TOTAL;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_sorter_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnViewSorter *self = GTK_COLUMN_VIEW_SORTER (object);
|
||||
|
||||
g_clear_pointer (&self->sorters, g_sequence_free);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_sorter_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_sorter_class_init (GtkColumnViewSorterClass *class)
|
||||
{
|
||||
GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
sorter_class->compare = gtk_column_view_sorter_compare;
|
||||
sorter_class->get_order = gtk_column_view_sorter_get_order;
|
||||
|
||||
object_class->dispose = gtk_column_view_sorter_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_sorter_init (GtkColumnViewSorter *self)
|
||||
{
|
||||
self->sorters = g_sequence_new (free_sorter);
|
||||
}
|
||||
|
||||
GtkSorter *
|
||||
gtk_column_view_sorter_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_COLUMN_VIEW_SORTER, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_sorter_changed_cb (GtkSorter *sorter, int change, gpointer data)
|
||||
{
|
||||
gtk_sorter_changed (GTK_SORTER (data), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
remove_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
|
||||
for (iter = g_sequence_get_begin_iter (self->sorters);
|
||||
!g_sequence_iter_is_end (iter);
|
||||
iter = g_sequence_iter_next (iter))
|
||||
{
|
||||
Sorter *s = g_sequence_get (iter);
|
||||
|
||||
if (s->column == column)
|
||||
{
|
||||
g_sequence_remove (iter);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_column_view_sorter_add_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
GtkSorter *sorter;
|
||||
Sorter *s, *first;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), FALSE);
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column), FALSE);
|
||||
|
||||
sorter = gtk_column_view_column_get_sorter (column);
|
||||
if (sorter == NULL)
|
||||
return FALSE;
|
||||
|
||||
iter = g_sequence_get_begin_iter (self->sorters);
|
||||
if (!g_sequence_iter_is_end (iter))
|
||||
{
|
||||
first = g_sequence_get (iter);
|
||||
if (first->column == column)
|
||||
{
|
||||
first->inverted = !first->inverted;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
first = NULL;
|
||||
|
||||
remove_column (self, column);
|
||||
|
||||
s = g_new (Sorter, 1);
|
||||
s->column = g_object_ref (column);
|
||||
s->sorter = g_object_ref (sorter);
|
||||
s->changed_id = g_signal_connect (sorter, "changed", G_CALLBACK (gtk_column_view_sorter_changed_cb), self);
|
||||
s->inverted = FALSE;
|
||||
|
||||
g_sequence_insert_before (iter, s);
|
||||
|
||||
/* notify the previous first column to stop drawing an arrow */
|
||||
if (first)
|
||||
gtk_column_view_column_notify_sort (first->column);
|
||||
|
||||
out:
|
||||
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
|
||||
gtk_column_view_column_notify_sort (column);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_column_view_sorter_remove_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), FALSE);
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column), FALSE);
|
||||
|
||||
if (remove_column (self, column))
|
||||
{
|
||||
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
gtk_column_view_column_notify_sort (column);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_column_view_sorter_set_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column,
|
||||
gboolean inverted)
|
||||
{
|
||||
GtkSorter *sorter;
|
||||
Sorter *s;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), FALSE);
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column), FALSE);
|
||||
|
||||
sorter = gtk_column_view_column_get_sorter (column);
|
||||
if (sorter == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_object_ref (column);
|
||||
|
||||
g_sequence_remove_range (g_sequence_get_begin_iter (self->sorters),
|
||||
g_sequence_get_end_iter (self->sorters));
|
||||
|
||||
s = g_new (Sorter, 1);
|
||||
s->column = g_object_ref (column);
|
||||
s->sorter = g_object_ref (sorter);
|
||||
s->changed_id = g_signal_connect (sorter, "changed", G_CALLBACK (gtk_column_view_sorter_changed_cb), self);
|
||||
s->inverted = inverted;
|
||||
|
||||
g_sequence_prepend (self->sorters, s);
|
||||
|
||||
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
|
||||
gtk_column_view_column_notify_sort (column);
|
||||
|
||||
g_object_unref (column);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_sorter_clear (GtkColumnViewSorter *self)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
Sorter *s;
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self));
|
||||
|
||||
if (g_sequence_is_empty (self->sorters))
|
||||
return;
|
||||
|
||||
iter = g_sequence_get_begin_iter (self->sorters);
|
||||
s = g_sequence_get (iter);
|
||||
|
||||
column = g_object_ref (s->column);
|
||||
|
||||
g_sequence_remove_range (iter, g_sequence_get_end_iter (self->sorters));
|
||||
|
||||
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
|
||||
gtk_column_view_column_notify_sort (column);
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_sorter_get_sort_column (GtkColumnViewSorter *self,
|
||||
gboolean *inverted)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
Sorter *s;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), NULL);
|
||||
|
||||
if (g_sequence_is_empty (self->sorters))
|
||||
return NULL;
|
||||
|
||||
iter = g_sequence_get_begin_iter (self->sorters);
|
||||
s = g_sequence_get (iter);
|
||||
|
||||
*inverted = s->inverted;
|
||||
|
||||
return s->column;
|
||||
}
|
57
gtk/gtkcolumnviewsorterprivate.h
Normal file
57
gtk/gtkcolumnviewsorterprivate.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright © 2019 Matthias Clasen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_SORTER_H__
|
||||
#define __GTK_COLUMN_VIEW_SORTER_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
#include <gtk/gtkcolumnviewcolumn.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW_SORTER (gtk_column_view_sorter_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GtkColumnViewSorter, gtk_column_view_sorter, GTK, COLUMN_VIEW_SORTER, GtkSorter)
|
||||
|
||||
GtkSorter * gtk_column_view_sorter_new (void);
|
||||
|
||||
gboolean gtk_column_view_sorter_add_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column);
|
||||
gboolean gtk_column_view_sorter_remove_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column);
|
||||
|
||||
void gtk_column_view_sorter_clear (GtkColumnViewSorter *self);
|
||||
|
||||
GtkColumnViewColumn * gtk_column_view_sorter_get_sort_column (GtkColumnViewSorter *self,
|
||||
gboolean *inverted);
|
||||
|
||||
gboolean gtk_column_view_sorter_set_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column,
|
||||
gboolean inverted);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_SORTER_H__ */
|
||||
|
208
gtk/gtkcolumnviewtitle.c
Normal file
208
gtk/gtkcolumnviewtitle.c
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewtitleprivate.h"
|
||||
|
||||
#include "gtkcolumnviewprivate.h"
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkcolumnviewsorterprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklabel.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkbox.h"
|
||||
#include "gtkimage.h"
|
||||
#include "gtkgestureclick.h"
|
||||
|
||||
struct _GtkColumnViewTitle
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
GtkWidget *box;
|
||||
GtkWidget *title;
|
||||
GtkWidget *sort;
|
||||
};
|
||||
|
||||
struct _GtkColumnViewTitleClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnViewTitle, gtk_column_view_title, GTK_TYPE_WIDGET)
|
||||
|
||||
static void
|
||||
gtk_column_view_title_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_title_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_allocate (child, width, height, baseline, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_title_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (object);
|
||||
|
||||
g_clear_pointer (&self->box, gtk_widget_unparent);
|
||||
|
||||
g_clear_object (&self->column);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_title_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_title_class_init (GtkColumnViewTitleClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
widget_class->measure = gtk_column_view_title_measure;
|
||||
widget_class->size_allocate = gtk_column_view_title_size_allocate;
|
||||
|
||||
gobject_class->dispose = gtk_column_view_title_dispose;
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("button"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_title_resize_func (GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
|
||||
|
||||
if (self->column)
|
||||
gtk_column_view_column_queue_resize (self->column);
|
||||
}
|
||||
|
||||
static void
|
||||
click_pressed_cb (GtkGestureClick *gesture,
|
||||
guint n_press,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
|
||||
GtkSorter *sorter;
|
||||
GtkColumnView *view;
|
||||
GtkColumnViewSorter *view_sorter;
|
||||
|
||||
sorter = gtk_column_view_column_get_sorter (self->column);
|
||||
if (sorter == NULL)
|
||||
return;
|
||||
|
||||
view = gtk_column_view_column_get_column_view (self->column);
|
||||
view_sorter = GTK_COLUMN_VIEW_SORTER (gtk_column_view_get_sorter (view));
|
||||
gtk_column_view_sorter_add_column (view_sorter, self->column);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_title_init (GtkColumnViewTitle *self)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
GtkGesture *gesture;
|
||||
|
||||
widget->priv->resize_func = gtk_column_view_title_resize_func;
|
||||
|
||||
self->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_widget_set_parent (self->box, widget);
|
||||
|
||||
self->title = gtk_label_new (NULL);
|
||||
gtk_box_append (GTK_BOX (self->box), self->title);
|
||||
|
||||
self->sort = gtk_image_new ();
|
||||
gtk_box_append (GTK_BOX (self->box), self->sort);
|
||||
|
||||
gesture = gtk_gesture_click_new ();
|
||||
g_signal_connect (gesture, "pressed", G_CALLBACK (click_pressed_cb), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_column_view_title_new (GtkColumnViewColumn *column)
|
||||
{
|
||||
GtkColumnViewTitle *title;
|
||||
|
||||
title = g_object_new (GTK_TYPE_COLUMN_VIEW_TITLE, NULL);
|
||||
|
||||
title->column = g_object_ref (column);
|
||||
gtk_column_view_title_update (title);
|
||||
|
||||
return GTK_WIDGET (title);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_title_update (GtkColumnViewTitle *self)
|
||||
{
|
||||
GtkSorter *sorter;
|
||||
GtkColumnView *view;
|
||||
GtkColumnViewSorter *view_sorter;
|
||||
gboolean inverted;
|
||||
GtkColumnViewColumn *active;
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (self->title), gtk_column_view_column_get_title (self->column));
|
||||
|
||||
sorter = gtk_column_view_column_get_sorter (self->column);
|
||||
|
||||
if (sorter)
|
||||
{
|
||||
view = gtk_column_view_column_get_column_view (self->column);
|
||||
view_sorter = GTK_COLUMN_VIEW_SORTER (gtk_column_view_get_sorter (view));
|
||||
active = gtk_column_view_sorter_get_sort_column (view_sorter, &inverted);
|
||||
|
||||
gtk_widget_show (self->sort);
|
||||
if (self->column == active)
|
||||
{
|
||||
if (inverted)
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (self->sort), "pan-down-symbolic");
|
||||
else
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (self->sort), "pan-up-symbolic");
|
||||
}
|
||||
else
|
||||
gtk_image_clear (GTK_IMAGE (self->sort));
|
||||
}
|
||||
else
|
||||
gtk_widget_hide (self->sort);
|
||||
}
|
||||
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_title_get_column (GtkColumnViewTitle *self)
|
||||
{
|
||||
return self->column;
|
||||
}
|
47
gtk/gtkcolumnviewtitleprivate.h
Normal file
47
gtk/gtkcolumnviewtitleprivate.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_TITLE_PRIVATE_H__
|
||||
#define __GTK_COLUMN_VIEW_TITLE_PRIVATE_H__
|
||||
|
||||
#include "gtkcolumnviewcolumn.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW_TITLE (gtk_column_view_title_get_type ())
|
||||
#define GTK_COLUMN_VIEW_TITLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_TITLE, GtkColumnViewTitle))
|
||||
#define GTK_COLUMN_VIEW_TITLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_TITLE, GtkColumnViewTitleClass))
|
||||
#define GTK_IS_COLUMN_VIEW_TITLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_TITLE))
|
||||
#define GTK_IS_COLUMN_VIEW_TITLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_TITLE))
|
||||
#define GTK_COLUMN_VIEW_TITLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_TITLE, GtkColumnViewTitleClass))
|
||||
|
||||
typedef struct _GtkColumnViewTitle GtkColumnViewTitle;
|
||||
typedef struct _GtkColumnViewTitleClass GtkColumnViewTitleClass;
|
||||
|
||||
GType gtk_column_view_title_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkWidget * gtk_column_view_title_new (GtkColumnViewColumn *column);
|
||||
|
||||
void gtk_column_view_title_update (GtkColumnViewTitle *self);
|
||||
|
||||
GtkColumnViewColumn * gtk_column_view_title_get_column (GtkColumnViewTitle *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_TITLE_PRIVATE_H__ */
|
704
gtk/gtkcoverflow.c
Normal file
704
gtk/gtkcoverflow.c
Normal file
@ -0,0 +1,704 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcoverflow.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkorientable.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/* Extra items to display at most above + below the central item */
|
||||
#define GTK_COVER_FLOW_DISPLAY_ITEMS 5
|
||||
|
||||
/* Number of extra space we leave around the covers */
|
||||
#define GTK_COVER_FLOW_SCALE_ALONG 3
|
||||
#define GTK_COVER_FLOW_SCALE_ACROSS 2
|
||||
|
||||
/**
|
||||
* SECTION:gtkcoverflow
|
||||
* @title: GtkCoverFlow
|
||||
* @short_description: A coverflow list widget
|
||||
* @see_also: #GListModel
|
||||
*
|
||||
* GtkCoverFlow is a widget to present a coverflow list
|
||||
*/
|
||||
|
||||
struct _GtkCoverFlow
|
||||
{
|
||||
GtkListBase parent_instance;
|
||||
|
||||
int size_across;
|
||||
int size_along;
|
||||
};
|
||||
|
||||
struct _GtkCoverFlowClass
|
||||
{
|
||||
GtkListBaseClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_FACTORY,
|
||||
PROP_MODEL,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTIVATE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkCoverFlow, gtk_cover_flow, GTK_TYPE_LIST_BASE)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static gboolean
|
||||
gtk_cover_flow_get_allocation_along (GtkListBase *base,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (base);
|
||||
|
||||
if (offset)
|
||||
*offset = pos * self->size_along;
|
||||
if (size)
|
||||
*size = self->size_along;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_cover_flow_get_allocation_across (GtkListBase *base,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (base);
|
||||
|
||||
if (offset)
|
||||
*offset = pos * self->size_across;
|
||||
if (size)
|
||||
*size = self->size_across;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_cover_flow_move_focus_along (GtkListBase *base,
|
||||
guint pos,
|
||||
int steps)
|
||||
{
|
||||
if (steps < 0)
|
||||
return pos - MIN (pos, -steps);
|
||||
else
|
||||
{
|
||||
pos += MIN (gtk_list_base_get_n_items (base) - pos - 1, steps);
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_cover_flow_move_focus_across (GtkListBase *base,
|
||||
guint pos,
|
||||
int steps)
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_cover_flow_get_position_from_allocation (GtkListBase *base,
|
||||
int across,
|
||||
int along,
|
||||
guint *pos,
|
||||
cairo_rectangle_int_t *area)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (base);
|
||||
|
||||
if (across >= self->size_across ||
|
||||
along >= self->size_along * gtk_list_base_get_n_items (base))
|
||||
return FALSE;
|
||||
|
||||
*pos = along / self->size_along;
|
||||
|
||||
if (area)
|
||||
{
|
||||
area->x = 0;
|
||||
area->width = self->size_across;
|
||||
area->y = *pos * self->size_along;
|
||||
area->height = self->size_along;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure_children (GtkCoverFlow *self,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkListItemManagerItem *item;
|
||||
int min, nat, child_min, child_nat;
|
||||
|
||||
min = 0;
|
||||
nat = 0;
|
||||
|
||||
for (item = gtk_list_item_manager_get_first (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
|
||||
item != NULL;
|
||||
item = gtk_rb_tree_node_get_next (item))
|
||||
{
|
||||
/* ignore unavailable items */
|
||||
if (item->widget == NULL)
|
||||
continue;
|
||||
|
||||
gtk_widget_measure (item->widget,
|
||||
orientation, for_size,
|
||||
&child_min, &child_nat, NULL, NULL);
|
||||
min = MAX (min, child_min);
|
||||
nat = MAX (nat, child_nat);
|
||||
}
|
||||
|
||||
*minimum = min;
|
||||
*natural = nat;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure_across (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
|
||||
if (for_size > 0)
|
||||
for_size /= GTK_COVER_FLOW_SCALE_ALONG;
|
||||
|
||||
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
|
||||
|
||||
*minimum *= GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
*natural *= GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure_along (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
|
||||
if (for_size > 0)
|
||||
for_size /= GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
|
||||
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
|
||||
|
||||
*minimum *= GTK_COVER_FLOW_SCALE_ALONG;
|
||||
*natural *= GTK_COVER_FLOW_SCALE_ALONG;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
|
||||
if (orientation == gtk_list_base_get_orientation (GTK_LIST_BASE (self)))
|
||||
gtk_cover_flow_measure_along (widget, orientation, for_size, minimum, natural);
|
||||
else
|
||||
gtk_cover_flow_measure_across (widget, orientation, for_size, minimum, natural);
|
||||
}
|
||||
|
||||
static GskTransform *
|
||||
transform_translate_oriented (GskTransform *transform,
|
||||
GtkOrientation orientation,
|
||||
GtkTextDirection dir,
|
||||
float across,
|
||||
float along)
|
||||
{
|
||||
if (orientation == GTK_ORIENTATION_VERTICAL)
|
||||
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (across, along));
|
||||
else if (dir == GTK_TEXT_DIR_LTR)
|
||||
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (along, across));
|
||||
else
|
||||
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-along, across));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_size_allocate_child (GtkWidget *child,
|
||||
GtkOrientation orientation,
|
||||
GtkTextDirection dir,
|
||||
GskTransform *transform,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
if (orientation == GTK_ORIENTATION_VERTICAL)
|
||||
{
|
||||
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-width / 2, -height / 2));
|
||||
gtk_widget_allocate (child, width, height, -1, transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-height / 2, -width / 2));
|
||||
gtk_widget_allocate (child, height, width, -1, transform);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
GtkOrientation orientation, opposite_orientation;
|
||||
GtkTextDirection dir;
|
||||
GtkListItemManagerItem *item;
|
||||
GtkListItemManager *manager;
|
||||
guint i, pos;
|
||||
int x, y, along, across;
|
||||
|
||||
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
|
||||
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
|
||||
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
|
||||
|
||||
/* step 0: exit early if list is empty */
|
||||
if (gtk_list_item_manager_get_root (manager) == NULL)
|
||||
return;
|
||||
|
||||
/* step 1: determine size of children */
|
||||
along = orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
|
||||
across = opposite_orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
|
||||
self->size_along = along / GTK_COVER_FLOW_SCALE_ALONG;
|
||||
self->size_across = across / GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
|
||||
/* step 2: update the adjustments */
|
||||
gtk_list_base_update_adjustments (GTK_LIST_BASE (self),
|
||||
self->size_across,
|
||||
self->size_along * gtk_list_base_get_n_items (GTK_LIST_BASE (self)),
|
||||
self->size_across,
|
||||
self->size_along,
|
||||
&x, &y);
|
||||
pos = gtk_list_base_get_anchor (GTK_LIST_BASE (self));
|
||||
|
||||
/* step 4: actually allocate the widgets */
|
||||
dir = _gtk_widget_get_direction (widget);
|
||||
i = 0;
|
||||
|
||||
for (item = gtk_list_item_manager_get_first (manager);
|
||||
item != NULL;
|
||||
item = gtk_rb_tree_node_get_next (item))
|
||||
{
|
||||
if (item->widget)
|
||||
{
|
||||
/* start at the center */
|
||||
GskTransform *transform = transform_translate_oriented (NULL, orientation, dir, across / 2, along / 2);
|
||||
|
||||
if (i == pos)
|
||||
{
|
||||
/* nothing to do, we're already centered */
|
||||
}
|
||||
else if (MAX (pos, i) - MIN (pos, i) < GTK_COVER_FLOW_DISPLAY_ITEMS) /* ABS() doesn't work with guint */
|
||||
{
|
||||
int diff = i - pos;
|
||||
transform = gsk_transform_perspective (transform, MAX (width, height) * 2);
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
0,
|
||||
diff * self->size_along / 4);
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
0,
|
||||
(diff < 0 ? -1 : 1) * self->size_along / 2);
|
||||
if (orientation == GTK_ORIENTATION_VERTICAL)
|
||||
transform = gsk_transform_rotate_3d (transform,
|
||||
diff > 0 ? 60 : -60,
|
||||
graphene_vec3_x_axis ());
|
||||
else
|
||||
transform = gsk_transform_rotate_3d (transform,
|
||||
diff < 0 ? 60 : -60,
|
||||
graphene_vec3_y_axis ());
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
0,
|
||||
(diff < 0 ? 1 : -1) * self->size_along / 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
- 2 * self->size_across,
|
||||
- 2 * self->size_along);
|
||||
}
|
||||
gtk_cover_flow_size_allocate_child (item->widget,
|
||||
orientation, dir,
|
||||
transform,
|
||||
self->size_across,
|
||||
self->size_along);
|
||||
}
|
||||
|
||||
i += item->n_items;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListBase *base = GTK_LIST_BASE (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, gtk_list_item_manager_get_factory (gtk_list_base_get_manager (base)));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, gtk_list_base_get_model (base));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkWidget *child;
|
||||
GtkListItemManagerItem *item;
|
||||
|
||||
item = gtk_list_item_manager_get_nth (gtk_list_base_get_manager (GTK_LIST_BASE (widget)),
|
||||
gtk_list_base_get_anchor (GTK_LIST_BASE (widget)),
|
||||
NULL);
|
||||
if (item == NULL || item->widget == NULL)
|
||||
{
|
||||
GTK_WIDGET_CLASS (gtk_cover_flow_parent_class)->snapshot (widget, snapshot);
|
||||
return;
|
||||
}
|
||||
|
||||
for (child = _gtk_widget_get_first_child (widget);
|
||||
child != item->widget;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
gtk_widget_snapshot_child (widget, child, snapshot);
|
||||
|
||||
for (child = _gtk_widget_get_last_child (widget);
|
||||
child != item->widget;
|
||||
child = _gtk_widget_get_prev_sibling (child))
|
||||
gtk_widget_snapshot_child (widget, child, snapshot);
|
||||
|
||||
gtk_widget_snapshot_child (widget, item->widget, snapshot);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
gtk_cover_flow_set_factory (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_cover_flow_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_activate_item (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
guint pos;
|
||||
|
||||
if (!g_variant_check_format_string (parameter, "u", FALSE))
|
||||
return;
|
||||
|
||||
g_variant_get (parameter, "u", &pos);
|
||||
if (pos >= gtk_list_base_get_n_items (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
g_signal_emit (widget, signals[ACTIVATE], 0, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_class_init (GtkCoverFlowClass *klass)
|
||||
{
|
||||
GtkListBaseClass *list_base_class = GTK_LIST_BASE_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
list_base_class->list_item_name = "cover";
|
||||
list_base_class->list_item_size = sizeof (GtkListItemManagerItem);
|
||||
list_base_class->list_item_augment_size = sizeof (GtkListItemManagerItemAugment);
|
||||
list_base_class->list_item_augment_func = gtk_list_item_manager_augment_node;
|
||||
list_base_class->get_allocation_along = gtk_cover_flow_get_allocation_along;
|
||||
list_base_class->get_allocation_across = gtk_cover_flow_get_allocation_across;
|
||||
list_base_class->get_position_from_allocation = gtk_cover_flow_get_position_from_allocation;
|
||||
list_base_class->move_focus_along = gtk_cover_flow_move_focus_along;
|
||||
list_base_class->move_focus_across = gtk_cover_flow_move_focus_across;
|
||||
|
||||
widget_class->measure = gtk_cover_flow_measure;
|
||||
widget_class->size_allocate = gtk_cover_flow_size_allocate;
|
||||
widget_class->snapshot = gtk_cover_flow_snapshot;
|
||||
|
||||
gobject_class->get_property = gtk_cover_flow_get_property;
|
||||
gobject_class->set_property = gtk_cover_flow_set_property;
|
||||
|
||||
/**
|
||||
* GtkCoverFlow:factory:
|
||||
*
|
||||
* Factory for populating list items
|
||||
*/
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
P_("Factory"),
|
||||
P_("Factory for populating list items"),
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkCoverFlow:model:
|
||||
*
|
||||
* Model for the items displayed
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("Model for the items displayed"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
* GtkCoverFlow::activate:
|
||||
* @self: The #GtkCoverFlow
|
||||
* @position: position of item to activate
|
||||
*
|
||||
* The ::activate signal is emitted when an item has been activated by the user,
|
||||
* usually via activating the GtkCoverFlow|list.activate-item action.
|
||||
*
|
||||
* This allows for a convenient way to handle activation in a listview.
|
||||
* See gtk_list_item_set_activatable() for details on how to use this signal.
|
||||
*/
|
||||
signals[ACTIVATE] =
|
||||
g_signal_new (I_("activate"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_UINT);
|
||||
g_signal_set_va_marshaller (signals[ACTIVATE],
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
g_cclosure_marshal_VOID__UINTv);
|
||||
|
||||
/**
|
||||
* GtkCoverFlow|list.activate-item:
|
||||
* @position: position of item to activate
|
||||
*
|
||||
* Activates the item given in @position by emitting the GtkCoverFlow::activate
|
||||
* signal.
|
||||
*/
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"list.activate-item",
|
||||
"u",
|
||||
gtk_cover_flow_activate_item);
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("coverflow"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_init (GtkCoverFlow *self)
|
||||
{
|
||||
gtk_list_base_set_anchor_max_widgets (GTK_LIST_BASE (self),
|
||||
0,
|
||||
GTK_COVER_FLOW_DISPLAY_ITEMS);
|
||||
|
||||
/* FIXME: this should overwrite the property default, but gobject properties... */
|
||||
gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_HORIZONTAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_new:
|
||||
*
|
||||
* Creates a new empty #GtkCoverFlow.
|
||||
*
|
||||
* You most likely want to call gtk_cover_flow_set_factory() to
|
||||
* set up a way to map its items to widgets and gtk_cover_flow_set_model()
|
||||
* to set a model to provide items next.
|
||||
*
|
||||
* Returns: a new #GtkCoverFlow
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_cover_flow_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_COVER_FLOW, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_new_with_factory:
|
||||
* @factory: (transfer full): The factory to populate items with
|
||||
*
|
||||
* Creates a new #GtkCoverFlow that uses the given @factory for
|
||||
* mapping items to widgets.
|
||||
*
|
||||
* You most likely want to call gtk_cover_flow_set_model() to set
|
||||
* a model next.
|
||||
*
|
||||
* The function takes ownership of the
|
||||
* argument, so you can write code like
|
||||
* ```
|
||||
* cover_flow = gtk_cover_flow_new_with_factory (
|
||||
* gtk_builder_list_item_factory_newfrom_resource ("/resource.ui"));
|
||||
* ```
|
||||
*
|
||||
* Returns: a new #GtkCoverFlow using the given @factory
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_cover_flow_new_with_factory (GtkListItemFactory *factory)
|
||||
{
|
||||
GtkWidget *result;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_COVER_FLOW,
|
||||
"factory", factory,
|
||||
NULL);
|
||||
|
||||
g_object_unref (factory);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_get_model:
|
||||
* @self: a #GtkCoverFlow
|
||||
*
|
||||
* Gets the model that's currently used to read the items displayed.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model in use
|
||||
**/
|
||||
GListModel *
|
||||
gtk_cover_flow_get_model (GtkCoverFlow *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
|
||||
|
||||
return gtk_list_base_get_model (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_set_model:
|
||||
* @self: a #GtkCoverFlow
|
||||
* @model: (allow-none) (transfer none): the model to use or %NULL for none
|
||||
*
|
||||
* Sets the #GListModel to use.
|
||||
*
|
||||
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
|
||||
* Otherwise, @self creates a #GtkSingleSelection for the selection.
|
||||
**/
|
||||
void
|
||||
gtk_cover_flow_set_model (GtkCoverFlow *self,
|
||||
GListModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COVER_FLOW (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (!gtk_list_base_set_model (GTK_LIST_BASE (self), model))
|
||||
return;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_get_factory:
|
||||
* @self: a #GtkCoverFlow
|
||||
*
|
||||
* Gets the factory that's currently used to populate list items.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The factory in use
|
||||
**/
|
||||
GtkListItemFactory *
|
||||
gtk_cover_flow_get_factory (GtkCoverFlow *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
|
||||
|
||||
return gtk_list_item_manager_get_factory (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_set_factory:
|
||||
* @self: a #GtkCoverFlow
|
||||
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
|
||||
*
|
||||
* Sets the #GtkListItemFactory to use for populating list items.
|
||||
**/
|
||||
void
|
||||
gtk_cover_flow_set_factory (GtkCoverFlow *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
GtkListItemManager *manager;
|
||||
|
||||
g_return_if_fail (GTK_IS_COVER_FLOW (self));
|
||||
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
|
||||
|
||||
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
|
||||
|
||||
if (factory == gtk_list_item_manager_get_factory (manager))
|
||||
return;
|
||||
|
||||
gtk_list_item_manager_set_factory (manager, factory);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
|
||||
}
|
68
gtk/gtkcoverflow.h
Normal file
68
gtk/gtkcoverflow.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COVER_FLOW_H__
|
||||
#define __GTK_COVER_FLOW_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistbase.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COVER_FLOW (gtk_cover_flow_get_type ())
|
||||
#define GTK_COVER_FLOW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COVER_FLOW, GtkCoverFlow))
|
||||
#define GTK_COVER_FLOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COVER_FLOW, GtkCoverFlowClass))
|
||||
#define GTK_IS_COVER_FLOW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COVER_FLOW))
|
||||
#define GTK_IS_COVER_FLOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COVER_FLOW))
|
||||
#define GTK_COVER_FLOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COVER_FLOW, GtkCoverFlowClass))
|
||||
|
||||
/**
|
||||
* GtkCoverFlow:
|
||||
*
|
||||
* GtkCoverFlow is the simple list implementation for GTK's list widgets.
|
||||
*/
|
||||
typedef struct _GtkCoverFlow GtkCoverFlow;
|
||||
typedef struct _GtkCoverFlowClass GtkCoverFlowClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_cover_flow_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_cover_flow_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_cover_flow_new_with_factory (GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_cover_flow_get_model (GtkCoverFlow *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_cover_flow_set_model (GtkCoverFlow *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_cover_flow_set_factory (GtkCoverFlow *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory *
|
||||
gtk_cover_flow_get_factory (GtkCoverFlow *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COVER_FLOW_H__ */
|
157
gtk/gtkcustomfilter.c
Normal file
157
gtk/gtkcustomfilter.c
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcustomfilter.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcustomfilter
|
||||
* @Title: GtkCustomFilter
|
||||
* @Short_description: Filtering with callbacks
|
||||
*
|
||||
* #GtkCustomFilter is a #GtkFilter that uses a callback to determine whether
|
||||
* to include an item or not.
|
||||
*/
|
||||
struct _GtkCustomFilter
|
||||
{
|
||||
GtkFilter parent_instance;
|
||||
|
||||
GtkCustomFilterFunc match_func;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkCustomFilter, gtk_custom_filter, GTK_TYPE_FILTER)
|
||||
|
||||
static gboolean
|
||||
gtk_custom_filter_match (GtkFilter *filter,
|
||||
gpointer item)
|
||||
{
|
||||
GtkCustomFilter *self = GTK_CUSTOM_FILTER (filter);
|
||||
|
||||
if (!self->match_func)
|
||||
return TRUE;
|
||||
|
||||
return self->match_func (item, self->user_data);
|
||||
}
|
||||
|
||||
static GtkFilterMatch
|
||||
gtk_custom_filter_get_strictness (GtkFilter *filter)
|
||||
{
|
||||
GtkCustomFilter *self = GTK_CUSTOM_FILTER (filter);
|
||||
|
||||
if (!self->match_func)
|
||||
return GTK_FILTER_MATCH_ALL;
|
||||
|
||||
return GTK_FILTER_MATCH_SOME;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_filter_dispose (GObject *object)
|
||||
{
|
||||
GtkCustomFilter *self = GTK_CUSTOM_FILTER (object);
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
G_OBJECT_CLASS (gtk_custom_filter_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_filter_class_init (GtkCustomFilterClass *class)
|
||||
{
|
||||
GtkFilterClass *filter_class = GTK_FILTER_CLASS (class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
filter_class->match = gtk_custom_filter_match;
|
||||
filter_class->get_strictness = gtk_custom_filter_get_strictness;
|
||||
|
||||
object_class->dispose = gtk_custom_filter_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_filter_init (GtkCustomFilter *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_custom_filter_new:
|
||||
* @match_func: (nullable): function to filter items
|
||||
* @user_data: (nullable): user data to pass to @match_func
|
||||
* @user_destroy: destory notify
|
||||
*
|
||||
* Creates a new filter using the given @match_func to filter
|
||||
* items.
|
||||
*
|
||||
* If the filter func changes its filtering behavior,
|
||||
* gtk_filter_changed() needs to be called.
|
||||
*
|
||||
* Returns: a new #GtkFilter
|
||||
**/
|
||||
GtkFilter *
|
||||
gtk_custom_filter_new (GtkCustomFilterFunc match_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
GtkCustomFilter *result;
|
||||
|
||||
result = g_object_new (GTK_TYPE_CUSTOM_FILTER, NULL);
|
||||
|
||||
gtk_custom_filter_set_filter_func (result, match_func, user_data, user_destroy);
|
||||
|
||||
return GTK_FILTER (result);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_custom_filter_set_filter_func:
|
||||
* @self: a #GtkCustomFilter
|
||||
* @match_func: (nullable): function to filter items
|
||||
* @user_data: (nullable): user data to pass to @match_func
|
||||
* @user_destroy: destory notify
|
||||
*
|
||||
* Sets (or unsets) the function used for filtering items.
|
||||
*
|
||||
* If the filter func changes its filtering behavior,
|
||||
* gtk_filter_changed() needs to be called.
|
||||
*
|
||||
* If a previous function was set, its @user_destroy will be
|
||||
* called now.
|
||||
**/
|
||||
void
|
||||
gtk_custom_filter_set_filter_func (GtkCustomFilter *self,
|
||||
GtkCustomFilterFunc match_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_CUSTOM_FILTER (self));
|
||||
g_return_if_fail (match_func || (user_data == NULL && !user_destroy));
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
self->match_func = match_func;
|
||||
self->user_data = user_data;
|
||||
self->user_destroy = user_destroy;
|
||||
|
||||
gtk_filter_changed (GTK_FILTER (self), GTK_FILTER_CHANGE_DIFFERENT);
|
||||
}
|
60
gtk/gtkcustomfilter.h
Normal file
60
gtk/gtkcustomfilter.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_CUSTOM_FILTER_H__
|
||||
#define __GTK_CUSTOM_FILTER_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkfilter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GtkCustomFilterFunc:
|
||||
* @item: (type GObject): The item to be matched
|
||||
* @user_data: user data
|
||||
*
|
||||
* User function that is called to determine if the @item should be matched.
|
||||
* If the filter matches the item, this function must return %TRUE. If the
|
||||
* item should be filtered out, %FALSE must be returned.
|
||||
*
|
||||
* Returns: %TRUE to keep the item around
|
||||
*/
|
||||
typedef gboolean (* GtkCustomFilterFunc) (gpointer item, gpointer user_data);
|
||||
|
||||
#define GTK_TYPE_CUSTOM_FILTER (gtk_custom_filter_get_type ())
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkCustomFilter, gtk_custom_filter, GTK, CUSTOM_FILTER, GtkFilter)
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_custom_filter_new (GtkCustomFilterFunc match_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_custom_filter_set_filter_func (GtkCustomFilter *self,
|
||||
GtkCustomFilterFunc match_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_CUSTOM_FILTER_H__ */
|
159
gtk/gtkcustomsorter.c
Normal file
159
gtk/gtkcustomsorter.c
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright © 2019 Matthias Clasen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcustomsorter.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcustomsorter
|
||||
* @Title: GtkCustomSorter
|
||||
* @Short_description: Sorting with a callback
|
||||
*
|
||||
* GtkCustomSorter is a #GtkSorter implementation that sorts
|
||||
* via a traditional #GCompareDataFunc callback.
|
||||
*/
|
||||
struct _GtkCustomSorter
|
||||
{
|
||||
GtkSorter parent_instance;
|
||||
|
||||
GCompareDataFunc sort_func;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkCustomSorter, gtk_custom_sorter, GTK_TYPE_SORTER)
|
||||
|
||||
static GtkOrdering
|
||||
gtk_custom_sorter_compare (GtkSorter *sorter,
|
||||
gpointer item1,
|
||||
gpointer item2)
|
||||
{
|
||||
GtkCustomSorter *self = GTK_CUSTOM_SORTER (sorter);
|
||||
|
||||
if (!self->sort_func)
|
||||
return GTK_ORDERING_EQUAL;
|
||||
|
||||
return gtk_ordering_from_cmpfunc (self->sort_func (item1, item2, self->user_data));
|
||||
}
|
||||
|
||||
static GtkSorterOrder
|
||||
gtk_custom_sorter_get_order (GtkSorter *sorter)
|
||||
{
|
||||
GtkCustomSorter *self = GTK_CUSTOM_SORTER (sorter);
|
||||
|
||||
if (!self->sort_func)
|
||||
return GTK_SORTER_ORDER_NONE;
|
||||
|
||||
return GTK_SORTER_ORDER_PARTIAL;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_sorter_dispose (GObject *object)
|
||||
{
|
||||
GtkCustomSorter *self = GTK_CUSTOM_SORTER (object);
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
self->sort_func = NULL;
|
||||
self->user_destroy = NULL;
|
||||
self->user_data = NULL;
|
||||
|
||||
G_OBJECT_CLASS (gtk_custom_sorter_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_sorter_class_init (GtkCustomSorterClass *class)
|
||||
{
|
||||
GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
sorter_class->compare = gtk_custom_sorter_compare;
|
||||
sorter_class->get_order = gtk_custom_sorter_get_order;
|
||||
|
||||
object_class->dispose = gtk_custom_sorter_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_sorter_init (GtkCustomSorter *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_custom_sorter_new:
|
||||
* @sort_func: the #GCompareDataFunc to use for sorting
|
||||
* @user_data: (nullable): user data to pass to @sort_func
|
||||
* @user_destroy: (nullable): destroy notify for @user_data
|
||||
*
|
||||
* Creates a new #GtkSorter that works by calling
|
||||
* @sort_func to compare items.
|
||||
*
|
||||
* Returns: a new #GTkSorter
|
||||
*/
|
||||
GtkSorter *
|
||||
gtk_custom_sorter_new (GCompareDataFunc sort_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
GtkCustomSorter *sorter;
|
||||
|
||||
sorter = g_object_new (GTK_TYPE_CUSTOM_SORTER, NULL);
|
||||
|
||||
gtk_custom_sorter_set_sort_func (sorter, sort_func, user_data, user_destroy);
|
||||
|
||||
return GTK_SORTER (sorter);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_custom_sorter_set_sort_func:
|
||||
* @self: a #GtkCustomSorter
|
||||
* @sort_func: (nullable): function to sort items
|
||||
* @user_data: (nullable): user data to pass to @match_func
|
||||
* @user_destroy: destory notify
|
||||
*
|
||||
* Sets (or unsets) the function used for sorting items.
|
||||
*
|
||||
* If the sort func changes its sorting behavior,
|
||||
* gtk_sorter_changed() needs to be called.
|
||||
*
|
||||
* If a previous function was set, its @user_destroy will be
|
||||
* called now.
|
||||
**/
|
||||
void
|
||||
gtk_custom_sorter_set_sort_func (GtkCustomSorter *self,
|
||||
GCompareDataFunc sort_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_CUSTOM_SORTER (self));
|
||||
g_return_if_fail (sort_func || (user_data == NULL && !user_destroy));
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
self->sort_func = sort_func;
|
||||
self->user_data = user_data;
|
||||
self->user_destroy = user_destroy;
|
||||
|
||||
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
}
|
49
gtk/gtkcustomsorter.h
Normal file
49
gtk/gtkcustomsorter.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright © 2019 Matthias Clasen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_CUSTOM_SORTER_H__
|
||||
#define __GTK_CUSTOM_SORTER_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkexpression.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CUSTOM_SORTER (gtk_custom_sorter_get_type ())
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkCustomSorter, gtk_custom_sorter, GTK, CUSTOM_SORTER, GtkSorter)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSorter * gtk_custom_sorter_new (GCompareDataFunc sort_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_custom_sorter_set_sort_func (GtkCustomSorter *self,
|
||||
GCompareDataFunc sort_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_CUSTOM_SORTER_H__ */
|
681
gtk/gtkdirectorylist.c
Normal file
681
gtk/gtkdirectorylist.c
Normal file
@ -0,0 +1,681 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkdirectorylist.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkdirectorylist
|
||||
* @title: GtkDirectoryList
|
||||
* @short_description: A list model for directory listings
|
||||
* @see_also: #GListModel, g_file_enumerate_children()
|
||||
*
|
||||
* #GtkDirectoryList is a list model that wraps g_file_enumerate_children_async().
|
||||
* It presents a #GListModel and fills it asynchronously with the #GFileInfos
|
||||
* returned from that function.
|
||||
*
|
||||
* Enumeration will start automatically when a the GtkDirectoryList:file property
|
||||
* is set.
|
||||
*
|
||||
* While the #GtkDirectoryList is being filled, the GtkDirectoryList:loading
|
||||
* property will be set to %TRUE. You can listen to that property if you want
|
||||
* to show information like a #GtkSpinner or a "Loading..." text.
|
||||
* If loading fails at any point, the GtkDirectoryList:error property will be set
|
||||
* to give more indication about the failure.
|
||||
*
|
||||
* The #GFileInfos returned from a #GtkDirectoryList have the "standard::file"
|
||||
* attribute set to the #GFile they refer to. This way you can get at the file
|
||||
* that is referred to in the same way you would via g_file_enumerator_get_child().
|
||||
* This means you do not need access to the #GtkDirectoryList but can access
|
||||
* the #GFile directly from the #GFileInfo when operating with a #GtkListView or
|
||||
* similar.
|
||||
*/
|
||||
|
||||
/* random number that everyone else seems to use, too */
|
||||
#define FILES_PER_QUERY 100
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_ATTRIBUTES,
|
||||
PROP_ERROR,
|
||||
PROP_FILE,
|
||||
PROP_IO_PRIORITY,
|
||||
PROP_ITEM_TYPE,
|
||||
PROP_LOADING,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
|
||||
struct _GtkDirectoryList
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
char *attributes;
|
||||
int io_priority;
|
||||
GFile *file;
|
||||
|
||||
GCancellable *cancellable;
|
||||
GError *error; /* Error while loading */
|
||||
GSequence *items; /* Use GPtrArray or GListStore here? */
|
||||
};
|
||||
|
||||
struct _GtkDirectoryListClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static GType
|
||||
gtk_directory_list_get_item_type (GListModel *list)
|
||||
{
|
||||
return G_TYPE_FILE_INFO;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_directory_list_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkDirectoryList *self = GTK_DIRECTORY_LIST (list);
|
||||
|
||||
return g_sequence_get_length (self->items);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_directory_list_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkDirectoryList *self = GTK_DIRECTORY_LIST (list);
|
||||
GSequenceIter *iter;
|
||||
|
||||
iter = g_sequence_get_iter_at_pos (self->items, position);
|
||||
|
||||
if (g_sequence_iter_is_end (iter))
|
||||
return NULL;
|
||||
else
|
||||
return g_object_ref (g_sequence_get (iter));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_directory_list_get_item_type;
|
||||
iface->get_n_items = gtk_directory_list_get_n_items;
|
||||
iface->get_item = gtk_directory_list_get_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkDirectoryList, gtk_directory_list, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_directory_list_model_init))
|
||||
|
||||
static void
|
||||
gtk_directory_list_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkDirectoryList *self = GTK_DIRECTORY_LIST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ATTRIBUTES:
|
||||
gtk_directory_list_set_attributes (self, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_FILE:
|
||||
gtk_directory_list_set_file (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_IO_PRIORITY:
|
||||
gtk_directory_list_set_io_priority (self, g_value_get_int (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkDirectoryList *self = GTK_DIRECTORY_LIST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ATTRIBUTES:
|
||||
g_value_set_string (value, self->attributes);
|
||||
break;
|
||||
|
||||
case PROP_ERROR:
|
||||
g_value_set_boxed (value, self->error);
|
||||
break;
|
||||
|
||||
case PROP_FILE:
|
||||
g_value_set_object (value, self->file);
|
||||
break;
|
||||
|
||||
case PROP_IO_PRIORITY:
|
||||
g_value_set_int (value, self->io_priority);
|
||||
break;
|
||||
|
||||
case PROP_ITEM_TYPE:
|
||||
g_value_set_gtype (value, G_TYPE_FILE_INFO);
|
||||
break;
|
||||
|
||||
case PROP_LOADING:
|
||||
g_value_set_boolean (value, gtk_directory_list_is_loading (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_directory_list_stop_loading (GtkDirectoryList *self)
|
||||
{
|
||||
if (self->cancellable == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_clear_object (&self->cancellable);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_dispose (GObject *object)
|
||||
{
|
||||
GtkDirectoryList *self = GTK_DIRECTORY_LIST (object);
|
||||
|
||||
gtk_directory_list_stop_loading (self);
|
||||
|
||||
g_clear_object (&self->file);
|
||||
g_clear_pointer (&self->attributes, g_free);
|
||||
|
||||
g_clear_error (&self->error);
|
||||
g_clear_pointer (&self->items, g_sequence_free);
|
||||
|
||||
G_OBJECT_CLASS (gtk_directory_list_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_class_init (GtkDirectoryListClass *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
gobject_class->set_property = gtk_directory_list_set_property;
|
||||
gobject_class->get_property = gtk_directory_list_get_property;
|
||||
gobject_class->dispose = gtk_directory_list_dispose;
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:attributes:
|
||||
*
|
||||
* The attributes to query
|
||||
*/
|
||||
properties[PROP_ATTRIBUTES] =
|
||||
g_param_spec_string ("attributes",
|
||||
P_("attributes"),
|
||||
P_("Attributes to query"),
|
||||
NULL,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:error:
|
||||
*
|
||||
* Error encountered while loading files
|
||||
*/
|
||||
properties[PROP_ERROR] =
|
||||
g_param_spec_boxed ("error",
|
||||
P_("error"),
|
||||
P_("Error encountered while loading files"),
|
||||
G_TYPE_ERROR,
|
||||
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:file:
|
||||
*
|
||||
* File to query
|
||||
*/
|
||||
properties[PROP_FILE] =
|
||||
g_param_spec_object ("file",
|
||||
P_("File"),
|
||||
P_("The file to query"),
|
||||
G_TYPE_FILE,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:io-priority:
|
||||
*
|
||||
* Priority used when loading
|
||||
*/
|
||||
properties[PROP_IO_PRIORITY] =
|
||||
g_param_spec_int ("io-priority",
|
||||
P_("IO priority"),
|
||||
P_("Priority used when loading"),
|
||||
-G_MAXINT, G_MAXINT, G_PRIORITY_DEFAULT,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:item-type:
|
||||
*
|
||||
* The #GType for elements of this object
|
||||
*/
|
||||
properties[PROP_ITEM_TYPE] =
|
||||
g_param_spec_gtype ("item-type",
|
||||
P_("Item type"),
|
||||
P_("The type of elements of this object"),
|
||||
G_TYPE_FILE_INFO,
|
||||
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:loading:
|
||||
*
|
||||
* %TRUE if files are being loaded
|
||||
*/
|
||||
properties[PROP_LOADING] =
|
||||
g_param_spec_boolean ("loading",
|
||||
P_("loading"),
|
||||
P_("TRUE if files are being loaded"),
|
||||
FALSE,
|
||||
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_init (GtkDirectoryList *self)
|
||||
{
|
||||
self->items = g_sequence_new (g_object_unref);
|
||||
self->io_priority = G_PRIORITY_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_new:
|
||||
* @file: (allow-none): The file to query
|
||||
* @attributes: (allow-none): The attributes to query with
|
||||
*
|
||||
* Creates a new #GtkDirectoryList querying the given @file with the given
|
||||
* @attributes.
|
||||
*
|
||||
* Returns: a new #GtkDirectoryList
|
||||
**/
|
||||
GtkDirectoryList *
|
||||
gtk_directory_list_new (const char *attributes,
|
||||
GFile *file)
|
||||
|
||||
{
|
||||
g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_DIRECTORY_LIST,
|
||||
"attributes", attributes,
|
||||
"file", file,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_clear_items (GtkDirectoryList *self)
|
||||
{
|
||||
guint n_items;
|
||||
|
||||
n_items = g_sequence_get_length (self->items);
|
||||
if (n_items > 0)
|
||||
{
|
||||
g_sequence_remove_range (g_sequence_get_begin_iter (self->items),
|
||||
g_sequence_get_end_iter (self->items));
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, 0);
|
||||
}
|
||||
|
||||
if (self->error)
|
||||
{
|
||||
g_clear_error (&self->error);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ERROR]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_enumerator_closed_cb (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_file_enumerator_close_finish (G_FILE_ENUMERATOR (source), res, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_got_files_cb (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkDirectoryList *self = user_data; /* invalid if cancelled */
|
||||
GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source);
|
||||
GError *error = NULL;
|
||||
GList *l, *files;
|
||||
guint n;
|
||||
|
||||
files = g_file_enumerator_next_files_finish (enumerator, res, &error);
|
||||
|
||||
if (files == NULL)
|
||||
{
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
{
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_file_enumerator_close_async (enumerator,
|
||||
self->io_priority,
|
||||
NULL,
|
||||
gtk_directory_list_enumerator_closed_cb,
|
||||
NULL);
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
g_clear_object (&self->cancellable);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
|
||||
|
||||
if (error)
|
||||
{
|
||||
self->error = error;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ERROR]);
|
||||
}
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
return;
|
||||
}
|
||||
|
||||
n = 0;
|
||||
for (l = files; l; l = l->next)
|
||||
{
|
||||
GFileInfo *info;
|
||||
GFile *file;
|
||||
|
||||
info = l->data;
|
||||
file = g_file_enumerator_get_child (enumerator, info);
|
||||
g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file));
|
||||
g_object_unref (file);
|
||||
g_sequence_append (self->items, info);
|
||||
n++;
|
||||
}
|
||||
g_list_free (files);
|
||||
|
||||
g_file_enumerator_next_files_async (enumerator,
|
||||
g_file_is_native (self->file) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
|
||||
self->io_priority,
|
||||
self->cancellable,
|
||||
gtk_directory_list_got_files_cb,
|
||||
self);
|
||||
|
||||
if (n > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), g_sequence_get_length (self->items) - n, 0, n);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_got_enumerator_cb (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkDirectoryList *self = user_data; /* invalid if cancelled */
|
||||
GFile *file = G_FILE (source);
|
||||
GFileEnumerator *enumerator;
|
||||
GError *error = NULL;
|
||||
|
||||
enumerator = g_file_enumerate_children_finish (file, res, &error);
|
||||
if (enumerator == NULL)
|
||||
{
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
{
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
self->error = error;
|
||||
g_clear_object (&self->cancellable);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ERROR]);
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
return;
|
||||
}
|
||||
|
||||
g_file_enumerator_next_files_async (enumerator,
|
||||
g_file_is_native (file) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
|
||||
self->io_priority,
|
||||
self->cancellable,
|
||||
gtk_directory_list_got_files_cb,
|
||||
self);
|
||||
g_object_unref (enumerator);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_start_loading (GtkDirectoryList *self)
|
||||
{
|
||||
gboolean was_loading;
|
||||
|
||||
was_loading = gtk_directory_list_stop_loading (self);
|
||||
gtk_directory_list_clear_items (self);
|
||||
|
||||
if (self->file == NULL)
|
||||
{
|
||||
if (was_loading)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
|
||||
return;
|
||||
}
|
||||
|
||||
self->cancellable = g_cancellable_new ();
|
||||
g_file_enumerate_children_async (self->file,
|
||||
self->attributes,
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
self->io_priority,
|
||||
self->cancellable,
|
||||
gtk_directory_list_got_enumerator_cb,
|
||||
self);
|
||||
|
||||
if (!was_loading)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_set_file:
|
||||
* @self: a #GtkDirectoryList
|
||||
* @file: (allow-none): the #GFile to be enumerated
|
||||
*
|
||||
* Sets the @file to be enumerated and starts the enumeration.
|
||||
*
|
||||
* If @file is %NULL, the result will be an empty list.
|
||||
*/
|
||||
void
|
||||
gtk_directory_list_set_file (GtkDirectoryList *self,
|
||||
GFile *file)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_DIRECTORY_LIST (self));
|
||||
g_return_if_fail (file == NULL || G_IS_FILE (file));
|
||||
|
||||
if (self->file == file ||
|
||||
(self->file && file && g_file_equal (self->file, file)))
|
||||
return;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
g_set_object (&self->file, file);
|
||||
|
||||
gtk_directory_list_start_loading (self);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILE]);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_get_file:
|
||||
* @self: a #GtkDirectoryList
|
||||
*
|
||||
* Gets the file whose children are currently enumerated.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The file whose children are enumerated
|
||||
**/
|
||||
GFile *
|
||||
gtk_directory_list_get_file (GtkDirectoryList *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), NULL);
|
||||
|
||||
return self->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_set_attributes:
|
||||
* @self: a #GtkDirectoryList
|
||||
* @attributes: (allow-none): the attributes to enumerate
|
||||
*
|
||||
* Sets the @attributes to be enumerated and starts the enumeration.
|
||||
*
|
||||
* If @attributes is %NULL, no attributes will be queried, but a list
|
||||
* of #GFileInfos will still be created.
|
||||
*/
|
||||
void
|
||||
gtk_directory_list_set_attributes (GtkDirectoryList *self,
|
||||
const char *attributes)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_DIRECTORY_LIST (self));
|
||||
|
||||
if (self->attributes == attributes)
|
||||
return;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
g_free (self->attributes);
|
||||
self->attributes = g_strdup (attributes);
|
||||
|
||||
gtk_directory_list_start_loading (self);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ATTRIBUTES]);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_get_attributes:
|
||||
* @self: a #GtkDirectoryList
|
||||
*
|
||||
* Gets the attributes queried on the children.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The queried attributes
|
||||
*/
|
||||
const char *
|
||||
gtk_directory_list_get_attributes (GtkDirectoryList *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), NULL);
|
||||
|
||||
return self->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_set_io_priority:
|
||||
* @self: a #GtkDirectoryList
|
||||
* @io_priority: IO priority to use
|
||||
*
|
||||
* Sets the IO priority to use while loading directories.
|
||||
*
|
||||
* Setting the priority while @self is loading will reprioritize the
|
||||
* ongoing load as soon as possible.
|
||||
*
|
||||
* The default IO priority is %G_PRIORITY_DEFAULT, which is higher than
|
||||
* the GTK redraw priority. If you are loading a lot of directories in
|
||||
* parrallel, lowering it to something like %G_PRIORITY_DEFAULT_IDLE
|
||||
* may increase responsiveness.
|
||||
*/
|
||||
void
|
||||
gtk_directory_list_set_io_priority (GtkDirectoryList *self,
|
||||
int io_priority)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_DIRECTORY_LIST (self));
|
||||
|
||||
if (self->io_priority == io_priority)
|
||||
return;
|
||||
|
||||
self->io_priority = io_priority;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_IO_PRIORITY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_get_io_priority:
|
||||
* @self: a #GtkDirectoryList
|
||||
*
|
||||
* Gets the IO priority set via gtk_directory_list_set_io_priority().
|
||||
*
|
||||
* Returns: The IO priority.
|
||||
*/
|
||||
int
|
||||
gtk_directory_list_get_io_priority (GtkDirectoryList *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), G_PRIORITY_DEFAULT);
|
||||
|
||||
return self->io_priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_is_loading:
|
||||
* @self: a #GtkDirectoryList
|
||||
*
|
||||
* Returns %TRUE if the children enumeration is currently in
|
||||
* progress.
|
||||
* Files will be added to @self from time to time while loading is
|
||||
* going on. The order in which are added is undefined and may change
|
||||
* inbetween runs.
|
||||
*
|
||||
* Returns: %TRUE if @self is loading
|
||||
*/
|
||||
gboolean
|
||||
gtk_directory_list_is_loading (GtkDirectoryList *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), FALSE);
|
||||
|
||||
return self->cancellable != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_get_error:
|
||||
* @self: a #GtkDirectoryList
|
||||
*
|
||||
* Gets the loading error, if any.
|
||||
*
|
||||
* If an error occurs during the loading process, the loading process
|
||||
* will finish and this property allows querying the error that happened.
|
||||
* This error will persist until a file is loaded again.
|
||||
*
|
||||
* An error being set does not mean that no files were loaded, and all
|
||||
* successfully queried files will remain in the list.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The loading error or %NULL if
|
||||
* loading finished successfully.
|
||||
*/
|
||||
const GError *
|
||||
gtk_directory_list_get_error (GtkDirectoryList *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), FALSE);
|
||||
|
||||
return self->error;
|
||||
}
|
||||
|
67
gtk/gtkdirectorylist.h
Normal file
67
gtk/gtkdirectorylist.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_DIRECTORY_LIST_H__
|
||||
#define __GTK_DIRECTORY_LIST_H__
|
||||
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gio.h>
|
||||
/* for GDK_AVAILABLE_IN_ALL */
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_DIRECTORY_LIST (gtk_directory_list_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkDirectoryList, gtk_directory_list, GTK, DIRECTORY_LIST, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkDirectoryList * gtk_directory_list_new (const char *attributes,
|
||||
GFile *file);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_directory_list_set_file (GtkDirectoryList *self,
|
||||
GFile *file);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GFile * gtk_directory_list_get_file (GtkDirectoryList *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_directory_list_set_attributes (GtkDirectoryList *self,
|
||||
const char *attributes);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const char * gtk_directory_list_get_attributes (GtkDirectoryList *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_directory_list_set_io_priority (GtkDirectoryList *self,
|
||||
int io_priority);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gtk_directory_list_get_io_priority (GtkDirectoryList *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_directory_list_is_loading (GtkDirectoryList *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GError * gtk_directory_list_get_error (GtkDirectoryList *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_DIRECTORY_LIST_H__ */
|
@ -550,6 +550,39 @@ typedef enum
|
||||
GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_RIGHT_TO_LEFT /*< nick=btrl >*/
|
||||
} GtkNumberUpLayout;
|
||||
|
||||
/**
|
||||
* GtkOrdering:
|
||||
* @GTK_ORDERING_SMALLER: the first value is smaller than the second
|
||||
* @GTK_ORDERING_EQUAL: the two values are equal
|
||||
* @GTK_ORDERING_LARGER: the first value is larger than the second
|
||||
*
|
||||
* Describes the way two values can be compared.
|
||||
*
|
||||
* These values can be used with a #GCompareFunc. However, a
|
||||
* #GCompareFunc is allowed to return any integer values.
|
||||
* For converting such a value to a #GtkOrdering, use
|
||||
* gtk_ordering_from_cmpfunc().
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_ORDERING_SMALLER = -1,
|
||||
GTK_ORDERING_EQUAL = 0,
|
||||
GTK_ORDERING_LARGER = 1
|
||||
} GtkOrdering;
|
||||
|
||||
/**
|
||||
* gtk_ordering_from_cmpfunc:
|
||||
* @cmpfunc_result: Result of a comparison function
|
||||
*
|
||||
* Converts the result of a #GCompareFunc like strcmp() to a #GtkOrdering.
|
||||
*
|
||||
* Returns: the corresponding #GtkOrdering
|
||||
**/
|
||||
static inline GtkOrdering
|
||||
gtk_ordering_from_cmpfunc (int cmpfunc_result)
|
||||
{
|
||||
return (GtkOrdering) ((cmpfunc_result > 0) - (cmpfunc_result < 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* GtkPageOrientation:
|
||||
* @GTK_PAGE_ORIENTATION_PORTRAIT: Portrait mode.
|
||||
|
@ -127,7 +127,7 @@ update_pointer_focus (GtkEventController *controller,
|
||||
}
|
||||
|
||||
if (leave)
|
||||
g_signal_emit (controller, signals[LEAVE], 0, crossing->mode);
|
||||
g_signal_emit (controller, signals[LEAVE], 0);
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (motion));
|
||||
if (motion->is_pointer != is_pointer)
|
||||
@ -143,7 +143,7 @@ update_pointer_focus (GtkEventController *controller,
|
||||
g_object_thaw_notify (G_OBJECT (motion));
|
||||
|
||||
if (enter)
|
||||
g_signal_emit (controller, signals[ENTER], 0, x, y, crossing->mode);
|
||||
g_signal_emit (controller, signals[ENTER], 0, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -232,7 +232,6 @@ gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass)
|
||||
* @controller: the object which received the signal
|
||||
* @x: coordinates of pointer location
|
||||
* @y: coordinates of pointer location
|
||||
* @mode: crossing mode
|
||||
*
|
||||
* Signals that the pointer has entered the widget.
|
||||
*/
|
||||
@ -242,15 +241,13 @@ gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass)
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE, 3,
|
||||
G_TYPE_NONE, 2,
|
||||
G_TYPE_DOUBLE,
|
||||
G_TYPE_DOUBLE,
|
||||
GDK_TYPE_CROSSING_MODE);
|
||||
G_TYPE_DOUBLE);
|
||||
|
||||
/**
|
||||
* GtkEventControllerMotion::leave:
|
||||
* @controller: the object which received the signal
|
||||
* @mode: crossing mode
|
||||
*
|
||||
* Signals that the pointer has left the widget.
|
||||
*/
|
||||
@ -260,8 +257,7 @@ gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass)
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE, 1,
|
||||
GDK_TYPE_CROSSING_MODE);
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
/**
|
||||
* GtkEventControllerMotion::motion:
|
||||
|
1447
gtk/gtkexpression.c
Normal file
1447
gtk/gtkexpression.c
Normal file
File diff suppressed because it is too large
Load Diff
112
gtk/gtkexpression.h
Normal file
112
gtk/gtkexpression.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GTK_EXPRESSION_H__
|
||||
#define __GTK_EXPRESSION_H__
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GtkExpression GtkExpression;
|
||||
typedef struct _GtkExpressionWatch GtkExpressionWatch;
|
||||
|
||||
/**
|
||||
* GtkExpressionNotify:
|
||||
* @user_data: data passed to gtk_expression_watch()
|
||||
*
|
||||
* Callback called by gtk_expression_watch() when the
|
||||
* expression value changes.
|
||||
*/
|
||||
typedef void (* GtkExpressionNotify) (gpointer user_data);
|
||||
|
||||
#define GTK_IS_EXPRESSION(expr) ((expr) != NULL)
|
||||
|
||||
#define GTK_TYPE_EXPRESSION (gtk_expression_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_expression_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_expression_ref (GtkExpression *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_expression_unref (GtkExpression *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_expression_get_value_type (GtkExpression *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_expression_is_static (GtkExpression *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_expression_evaluate (GtkExpression *self,
|
||||
gpointer this_,
|
||||
GValue *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpressionWatch * gtk_expression_watch (GtkExpression *self,
|
||||
gpointer this_,
|
||||
GtkExpressionNotify notify,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpressionWatch * gtk_expression_bind (GtkExpression *self,
|
||||
gpointer target,
|
||||
const char * property,
|
||||
gpointer this_);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpressionWatch * gtk_expression_watch_ref (GtkExpressionWatch *watch);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_expression_watch_unref (GtkExpressionWatch *watch);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_expression_watch_evaluate (GtkExpressionWatch *watch,
|
||||
GValue *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_expression_watch_unwatch (GtkExpressionWatch *watch);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_property_expression_new (GType this_type,
|
||||
GtkExpression *expression,
|
||||
const char *property_name);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_property_expression_new_for_pspec (GtkExpression *expression,
|
||||
GParamSpec *pspec);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_constant_expression_new (GType value_type,
|
||||
...);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_constant_expression_new_for_value (const GValue *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_object_expression_new (GObject *object);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_closure_expression_new (GType value_type,
|
||||
GClosure *closure,
|
||||
guint n_params,
|
||||
GtkExpression **params);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_cclosure_expression_new (GType value_type,
|
||||
GClosureMarshal marshal,
|
||||
guint n_params,
|
||||
GtkExpression **params,
|
||||
GCallback callback_func,
|
||||
gpointer user_data,
|
||||
GClosureNotify user_destroy);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_EXPRESSION_H__ */
|
181
gtk/gtkfilter.c
Normal file
181
gtk/gtkfilter.c
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkfilter.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkfilter
|
||||
* @Title: GtkFilter
|
||||
* @Short_description: Filtering items
|
||||
* @See_also: #GtkFilerListModel
|
||||
*
|
||||
* #GtkFilter is the way to describe filters to be used in #GtkFilterListModel.
|
||||
*
|
||||
* The model will use a filter to determine if it should filter items or not
|
||||
* by calling gtk_filter_match() for each item and only keeping the ones
|
||||
* visible that the function returns %TRUE for.
|
||||
*
|
||||
* Filters may change what items they match through their lifetime. In that
|
||||
* case, they can call gtk_filter_changed() which will emit the #GtkFilter:changed
|
||||
* signal to notify that previous filter results are no longer valid and that
|
||||
* items should be checked via gtk_filter_match() again.
|
||||
*
|
||||
* GTK provides various premade filter implementations for common filtering
|
||||
* operations. These filters often include properties that can be linked to
|
||||
* various widgets to easily allow searches.
|
||||
*
|
||||
* However, in particular for large lists or complex search methods, it is
|
||||
* also possible to subclass #GtkFilter and provide one's own filter.
|
||||
*/
|
||||
|
||||
enum {
|
||||
CHANGED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkFilter, gtk_filter, G_TYPE_OBJECT)
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static gboolean
|
||||
gtk_filter_default_match (GtkFilter *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_critical ("Filter of type '%s' does not implement GtkFilter::match", G_OBJECT_TYPE_NAME (self));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GtkFilterMatch
|
||||
gtk_filter_default_get_strictness (GtkFilter *self)
|
||||
{
|
||||
return GTK_FILTER_MATCH_SOME;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_class_init (GtkFilterClass *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
class->match = gtk_filter_default_match;
|
||||
class->get_strictness = gtk_filter_default_get_strictness;
|
||||
|
||||
/**
|
||||
* GtkFilter:changed:
|
||||
* @self: The #GtkFilter
|
||||
* @change: how the filter changed
|
||||
*
|
||||
* This signal is emitted whenever the filter changed. Users of the filter
|
||||
* should then check items again via gtk_filter_match().
|
||||
*
|
||||
* #GtkFilterListModel handles this signal automatically.
|
||||
*
|
||||
* Depending on the @change parameter, not all items need to be changed, but
|
||||
* only some. Refer to the #GtkFilterChange documentation for details.
|
||||
*/
|
||||
signals[CHANGED] =
|
||||
g_signal_new (I_("changed"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__ENUM,
|
||||
G_TYPE_NONE, 1,
|
||||
GTK_TYPE_FILTER_CHANGE);
|
||||
g_signal_set_va_marshaller (signals[CHANGED],
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
g_cclosure_marshal_VOID__ENUMv);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_init (GtkFilter *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_match:
|
||||
* @self: a #GtkFilter
|
||||
* @item: (type GObject) (transfer none): The item to check
|
||||
*
|
||||
* Checks if the given @item is matched by the filter or not.
|
||||
*
|
||||
* Returns: %TRUE if the filter matches the item and a filter model should
|
||||
* keep it, %FALSE if not.
|
||||
*/
|
||||
gboolean
|
||||
gtk_filter_match (GtkFilter *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER (self), FALSE);
|
||||
g_return_val_if_fail (item != NULL, FALSE);
|
||||
|
||||
return GTK_FILTER_GET_CLASS (self)->match (self, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_get_strictness:
|
||||
* @self: a #GtkFilter
|
||||
*
|
||||
* Gets the known strictness of @filters. If the strictness is not known,
|
||||
* %GTK_FILTER_MATCH_SOME is returned.
|
||||
*
|
||||
* This value may change after emission of the GtkFilter:changed signal.
|
||||
*
|
||||
* This function is meant purely for optimization purposes, filters can
|
||||
* choose to omit implementing it, but #GtkFilterListModel uses it.
|
||||
*
|
||||
* Returns: the strictness of @self
|
||||
**/
|
||||
GtkFilterMatch
|
||||
gtk_filter_get_strictness (GtkFilter *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER (self), GTK_FILTER_MATCH_SOME);
|
||||
|
||||
return GTK_FILTER_GET_CLASS (self)->get_strictness (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_changed:
|
||||
* @self: a #GtkFilter
|
||||
* @change: How the filter changed
|
||||
*
|
||||
* Emits the #GtkFilter:changed signal to notify all users of the filter that
|
||||
* the filter changed. Users of the filter should then check items again via
|
||||
* gtk_filter_match().
|
||||
*
|
||||
* Depending on the @change parameter, not all items need to be changed, but
|
||||
* only some. Refer to the #GtkFilterChange documentation for details.
|
||||
*
|
||||
* This function is intended for implementors of #GtkFilter subclasses and
|
||||
* should not be called from other functions.
|
||||
*/
|
||||
void
|
||||
gtk_filter_changed (GtkFilter *self,
|
||||
GtkFilterChange change)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_FILTER (self));
|
||||
|
||||
g_signal_emit (self, signals[CHANGED], 0, change);
|
||||
}
|
||||
|
121
gtk/gtkfilter.h
Normal file
121
gtk/gtkfilter.h
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_FILTER_H__
|
||||
#define __GTK_FILTER_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GtkFilterMatch:
|
||||
* @GTK_FILTER_MATCH_SOME: The filter matches some items,
|
||||
* gtk_filter_match() may return %TRUE or %FALSE
|
||||
* @GTK_FILTER_MATCH_NONE: The filter does not match any item,
|
||||
* gtk_filter_match() will always return %FALSE.
|
||||
* @GTK_FILTER_MATCH_ALL: The filter matches all items,
|
||||
* gtk_filter_match() will alays return %TRUE.
|
||||
*
|
||||
* Describes the known strictness of a filter.
|
||||
*
|
||||
* Note that for filters where the strictness is not known,
|
||||
* %@GTK_FILTER_MATCH_SOME is always an acceptable value,
|
||||
* even if a filter does match all or no items.
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_FILTER_MATCH_SOME = 0,
|
||||
GTK_FILTER_MATCH_NONE,
|
||||
GTK_FILTER_MATCH_ALL
|
||||
} GtkFilterMatch;
|
||||
|
||||
/**
|
||||
* GtkFilterChange:
|
||||
* @GTK_FILTER_CHANGE_DIFFERENT: The filter change cannot be
|
||||
* described with any of the other enumeration values.
|
||||
* @GTK_FILTER_CHANGE_LESS_STRICT: The filter is less strict than
|
||||
* it was before: All items that it used to return %TRUE for
|
||||
* still return %TRUE, others now may, too.
|
||||
* @GTK_FILTER_CHANGE_MORE_STRICT: The filter is more strict than
|
||||
* it was before: All items that it used to return %FALSE for
|
||||
* still return %FALSE, others now may, too.
|
||||
*
|
||||
* Describes changes in a filter in more detail and allows objects
|
||||
* using the filter to optimize refiltering items.
|
||||
*
|
||||
* If you are writing an implementation and are not sure which
|
||||
* value to pass, @GTK_FILTER_CHANGE_DIFFERENT is always a correct
|
||||
* choice.
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_FILTER_CHANGE_DIFFERENT = 0,
|
||||
GTK_FILTER_CHANGE_LESS_STRICT,
|
||||
GTK_FILTER_CHANGE_MORE_STRICT,
|
||||
} GtkFilterChange;
|
||||
|
||||
#define GTK_TYPE_FILTER (gtk_filter_get_type ())
|
||||
|
||||
/**
|
||||
* GtkFilter:
|
||||
*
|
||||
* The object describing a filter.
|
||||
*/
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_DERIVABLE_TYPE (GtkFilter, gtk_filter, GTK, FILTER, GObject)
|
||||
|
||||
struct _GtkFilterClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
gboolean (* match) (GtkFilter *self,
|
||||
gpointer item);
|
||||
|
||||
/* optional */
|
||||
GtkFilterMatch (* get_strictness) (GtkFilter *self);
|
||||
|
||||
/* Padding for future expansion */
|
||||
void (*_gtk_reserved1) (void);
|
||||
void (*_gtk_reserved2) (void);
|
||||
void (*_gtk_reserved3) (void);
|
||||
void (*_gtk_reserved4) (void);
|
||||
void (*_gtk_reserved5) (void);
|
||||
void (*_gtk_reserved6) (void);
|
||||
void (*_gtk_reserved7) (void);
|
||||
void (*_gtk_reserved8) (void);
|
||||
};
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_filter_match (GtkFilter *self,
|
||||
gpointer item);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilterMatch gtk_filter_get_strictness (GtkFilter *self);
|
||||
|
||||
/* for filter implementations */
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_changed (GtkFilter *self,
|
||||
GtkFilterChange change);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_FILTER_H__ */
|
@ -29,17 +29,17 @@
|
||||
* SECTION:gtkfilterlistmodel
|
||||
* @title: GtkFilterListModel
|
||||
* @short_description: A list model that filters its items
|
||||
* @see_also: #GListModel
|
||||
* @see_also: #GListModel, #GtkFilter
|
||||
*
|
||||
* #GtkFilterListModel is a list model that filters a given other
|
||||
* listmodel.
|
||||
* It hides some elements from the other model according to
|
||||
* criteria given by a #GtkFilterListModelFilterFunc.
|
||||
* criteria given by a #GtkFilter.
|
||||
*/
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_HAS_FILTER,
|
||||
PROP_FILTER,
|
||||
PROP_ITEM_TYPE,
|
||||
PROP_MODEL,
|
||||
NUM_PROPERTIES
|
||||
@ -65,11 +65,10 @@ struct _GtkFilterListModel
|
||||
|
||||
GType item_type;
|
||||
GListModel *model;
|
||||
GtkFilterListModelFilterFunc filter_func;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
GtkFilter *filter;
|
||||
GtkFilterMatch strictness;
|
||||
|
||||
GtkRbTree *items; /* NULL if filter_func == NULL */
|
||||
GtkRbTree *items; /* NULL if strictness != GTK_FILTER_MATCH_SOME */
|
||||
};
|
||||
|
||||
struct _GtkFilterListModelClass
|
||||
@ -79,6 +78,33 @@ struct _GtkFilterListModelClass
|
||||
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_augment (GtkRbTree *filter,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
FilterNode *node = _node;
|
||||
FilterAugment *aug = _aug;
|
||||
|
||||
aug->n_items = 1;
|
||||
aug->n_visible = node->visible ? 1 : 0;
|
||||
|
||||
if (left)
|
||||
{
|
||||
FilterAugment *left_aug = gtk_rb_tree_get_augment (filter, left);
|
||||
aug->n_items += left_aug->n_items;
|
||||
aug->n_visible += left_aug->n_visible;
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
FilterAugment *right_aug = gtk_rb_tree_get_augment (filter, right);
|
||||
aug->n_items += right_aug->n_items;
|
||||
aug->n_visible += right_aug->n_visible;
|
||||
}
|
||||
}
|
||||
|
||||
static FilterNode *
|
||||
gtk_filter_list_model_get_nth_filtered (GtkRbTree *tree,
|
||||
guint position,
|
||||
@ -180,11 +206,20 @@ gtk_filter_list_model_get_n_items (GListModel *list)
|
||||
FilterAugment *aug;
|
||||
FilterNode *node;
|
||||
|
||||
if (self->model == NULL)
|
||||
return 0;
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
return 0;
|
||||
|
||||
if (!self->items)
|
||||
return g_list_model_get_n_items (self->model);
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
return g_list_model_get_n_items (self->model);
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
break;
|
||||
}
|
||||
|
||||
node = gtk_rb_tree_get_root (self->items);
|
||||
if (node == NULL)
|
||||
@ -201,13 +236,22 @@ gtk_filter_list_model_get_item (GListModel *list,
|
||||
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
|
||||
guint unfiltered;
|
||||
|
||||
if (self->model == NULL)
|
||||
return NULL;
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
return NULL;
|
||||
|
||||
if (self->items)
|
||||
gtk_filter_list_model_get_nth_filtered (self->items, position, &unfiltered);
|
||||
else
|
||||
unfiltered = position;
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
unfiltered = position;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
gtk_filter_list_model_get_nth_filtered (self->items, position, &unfiltered);
|
||||
break;
|
||||
}
|
||||
|
||||
return g_list_model_get_item (self->model, unfiltered);
|
||||
}
|
||||
@ -230,8 +274,11 @@ gtk_filter_list_model_run_filter (GtkFilterListModel *self,
|
||||
gpointer item;
|
||||
gboolean visible;
|
||||
|
||||
/* all other cases should have beeen optimized away */
|
||||
g_assert (self->strictness == GTK_FILTER_MATCH_SOME);
|
||||
|
||||
item = g_list_model_get_item (self->model, position);
|
||||
visible = self->filter_func (item, self->user_data);
|
||||
visible = gtk_filter_match (self->filter, item);
|
||||
g_object_unref (item);
|
||||
|
||||
return visible;
|
||||
@ -269,10 +316,20 @@ gtk_filter_list_model_items_changed_cb (GListModel *model,
|
||||
FilterNode *node;
|
||||
guint i, filter_position, filter_removed, filter_added;
|
||||
|
||||
if (self->items == NULL)
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
return;
|
||||
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
||||
return;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
break;
|
||||
}
|
||||
|
||||
node = gtk_filter_list_model_get_nth (self->items, position, &filter_position);
|
||||
@ -303,6 +360,10 @@ gtk_filter_list_model_set_property (GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_FILTER:
|
||||
gtk_filter_list_model_set_filter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_ITEM_TYPE:
|
||||
self->item_type = g_value_get_gtype (value);
|
||||
break;
|
||||
@ -327,8 +388,8 @@ gtk_filter_list_model_get_property (GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_HAS_FILTER:
|
||||
g_value_set_boolean (value, self->items != NULL);
|
||||
case PROP_FILTER:
|
||||
g_value_set_object (value, self->filter);
|
||||
break;
|
||||
|
||||
case PROP_ITEM_TYPE:
|
||||
@ -357,17 +418,223 @@ gtk_filter_list_model_clear_model (GtkFilterListModel *self)
|
||||
gtk_rb_tree_remove_all (self->items);
|
||||
}
|
||||
|
||||
/*<private>
|
||||
* gtk_filter_list_model_find_filtered:
|
||||
* @self: a #GtkFilterListModel
|
||||
* @start: (out) (caller-allocates): number of unfiltered items
|
||||
* at start of list
|
||||
* @end: (out) (caller-allocates): number of unfiltered items
|
||||
* at end of list
|
||||
* @n_items: (out) (caller-allocates): number of unfiltered items in
|
||||
* list
|
||||
*
|
||||
* Checks if elements in self->items are filtered out and returns
|
||||
* the range that they occupy.
|
||||
* This function is intended to be used for GListModel::items-changed
|
||||
* emissions, so it is called in an intermediate state for @self.
|
||||
*
|
||||
* Returns: %TRUE if elements are filtered out, %FALSE if none are
|
||||
**/
|
||||
static gboolean
|
||||
gtk_filter_list_model_find_filtered (GtkFilterListModel *self,
|
||||
guint *start,
|
||||
guint *end,
|
||||
guint *n_items)
|
||||
{
|
||||
FilterNode *root, *node, *tmp;
|
||||
FilterAugment *aug;
|
||||
|
||||
if (self->items == NULL || self->model == NULL)
|
||||
return FALSE;
|
||||
|
||||
root = gtk_rb_tree_get_root (self->items);
|
||||
if (root == NULL)
|
||||
return FALSE; /* empty parent model */
|
||||
|
||||
aug = gtk_rb_tree_get_augment (self->items, root);
|
||||
if (aug->n_items == aug->n_visible)
|
||||
return FALSE; /* all items visible */
|
||||
|
||||
/* find first filtered */
|
||||
*start = 0;
|
||||
*end = 0;
|
||||
*n_items = aug->n_visible;
|
||||
|
||||
node = root;
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_left (node);
|
||||
if (tmp)
|
||||
{
|
||||
aug = gtk_rb_tree_get_augment (self->items, tmp);
|
||||
if (aug->n_visible < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
*start += aug->n_items;
|
||||
}
|
||||
|
||||
if (!node->visible)
|
||||
break;
|
||||
|
||||
(*start)++;
|
||||
|
||||
node = gtk_rb_tree_node_get_right (node);
|
||||
}
|
||||
|
||||
/* find last filtered by doing everything the opposite way */
|
||||
node = root;
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_right (node);
|
||||
if (tmp)
|
||||
{
|
||||
aug = gtk_rb_tree_get_augment (self->items, tmp);
|
||||
if (aug->n_visible < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
*end += aug->n_items;
|
||||
}
|
||||
|
||||
if (!node->visible)
|
||||
break;
|
||||
|
||||
(*end)++;
|
||||
|
||||
node = gtk_rb_tree_node_get_left (node);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_refilter (GtkFilterListModel *self);
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_update_strictness_and_refilter (GtkFilterListModel *self)
|
||||
{
|
||||
GtkFilterMatch new_strictness;
|
||||
|
||||
if (self->model == NULL)
|
||||
new_strictness = GTK_FILTER_MATCH_NONE;
|
||||
else if (self->filter == NULL)
|
||||
new_strictness = GTK_FILTER_MATCH_ALL;
|
||||
else
|
||||
new_strictness = gtk_filter_get_strictness (self->filter);
|
||||
|
||||
/* don't set self->strictness yet so get_n_items() and friends return old values */
|
||||
|
||||
switch (new_strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
{
|
||||
guint n_before = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
self->strictness = new_strictness;
|
||||
if (n_before > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_before, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
self->strictness = new_strictness;
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, g_list_model_get_n_items (self->model));
|
||||
break;
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
self->strictness = new_strictness;
|
||||
break;
|
||||
default:
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
{
|
||||
guint start, end, n_before, n_after;
|
||||
self->strictness = new_strictness;
|
||||
if (gtk_filter_list_model_find_filtered (self, &start, &end, &n_before))
|
||||
{
|
||||
n_after = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), start, n_before - end - start, n_after - end - start);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
{
|
||||
guint n_after;
|
||||
self->strictness = new_strictness;
|
||||
self->items = gtk_rb_tree_new (FilterNode,
|
||||
FilterAugment,
|
||||
gtk_filter_list_model_augment,
|
||||
NULL, NULL);
|
||||
n_after = gtk_filter_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (self->model));
|
||||
if (n_after > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, n_after);
|
||||
}
|
||||
break;
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
{
|
||||
guint start, end, n_before, n_after;
|
||||
self->strictness = new_strictness;
|
||||
self->items = gtk_rb_tree_new (FilterNode,
|
||||
FilterAugment,
|
||||
gtk_filter_list_model_augment,
|
||||
NULL, NULL);
|
||||
n_before = g_list_model_get_n_items (self->model);
|
||||
gtk_filter_list_model_add_items (self, NULL, 0, n_before);
|
||||
if (gtk_filter_list_model_find_filtered (self, &start, &end, &n_after))
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), start, n_before - end - start, n_after - end - start);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
gtk_filter_list_model_refilter (self);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_filter_changed_cb (GtkFilter *filter,
|
||||
GtkFilterChange change,
|
||||
GtkFilterListModel *self)
|
||||
{
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_clear_filter (GtkFilterListModel *self)
|
||||
{
|
||||
if (self->filter == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->filter, gtk_filter_list_model_filter_changed_cb, self);
|
||||
g_clear_object (&self->filter);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_dispose (GObject *object)
|
||||
{
|
||||
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (object);
|
||||
|
||||
gtk_filter_list_model_clear_model (self);
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
self->filter_func = NULL;
|
||||
self->user_data = NULL;
|
||||
self->user_destroy = NULL;
|
||||
gtk_filter_list_model_clear_filter (self);
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_filter_list_model_parent_class)->dispose (object);
|
||||
@ -383,16 +650,16 @@ gtk_filter_list_model_class_init (GtkFilterListModelClass *class)
|
||||
gobject_class->dispose = gtk_filter_list_model_dispose;
|
||||
|
||||
/**
|
||||
* GtkFilterListModel:has-filter:
|
||||
* GtkFilterListModel:filter:
|
||||
*
|
||||
* If a filter is set for this model
|
||||
* The filter for this model
|
||||
*/
|
||||
properties[PROP_HAS_FILTER] =
|
||||
g_param_spec_boolean ("has-filter",
|
||||
P_("has filter"),
|
||||
P_("If a filter is set for this model"),
|
||||
FALSE,
|
||||
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
properties[PROP_FILTER] =
|
||||
g_param_spec_object ("filter",
|
||||
P_("Filter"),
|
||||
P_("The filter set for this model"),
|
||||
GTK_TYPE_FILTER,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkFilterListModel:item-type:
|
||||
@ -424,53 +691,22 @@ gtk_filter_list_model_class_init (GtkFilterListModelClass *class)
|
||||
static void
|
||||
gtk_filter_list_model_init (GtkFilterListModel *self)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_augment (GtkRbTree *filter,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
FilterNode *node = _node;
|
||||
FilterAugment *aug = _aug;
|
||||
|
||||
aug->n_items = 1;
|
||||
aug->n_visible = node->visible ? 1 : 0;
|
||||
|
||||
if (left)
|
||||
{
|
||||
FilterAugment *left_aug = gtk_rb_tree_get_augment (filter, left);
|
||||
aug->n_items += left_aug->n_items;
|
||||
aug->n_visible += left_aug->n_visible;
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
FilterAugment *right_aug = gtk_rb_tree_get_augment (filter, right);
|
||||
aug->n_items += right_aug->n_items;
|
||||
aug->n_visible += right_aug->n_visible;
|
||||
}
|
||||
self->strictness = GTK_FILTER_MATCH_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_new:
|
||||
* @model: the model to sort
|
||||
* @filter_func: (allow-none): filter function or %NULL to not filter items
|
||||
* @user_data: (closure): user data passed to @filter_func
|
||||
* @user_destroy: destroy notifier for @user_data
|
||||
* @filter: (allow-none): filter or %NULL to not filter items
|
||||
*
|
||||
* Creates a new #GtkFilterListModel that will filter @model using the given
|
||||
* @filter_func.
|
||||
* @filter.
|
||||
*
|
||||
* Returns: a new #GtkFilterListModel
|
||||
**/
|
||||
GtkFilterListModel *
|
||||
gtk_filter_list_model_new (GListModel *model,
|
||||
GtkFilterListModelFilterFunc filter_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
gtk_filter_list_model_new (GListModel *model,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
GtkFilterListModel *result;
|
||||
|
||||
@ -479,11 +715,9 @@ gtk_filter_list_model_new (GListModel *model,
|
||||
result = g_object_new (GTK_TYPE_FILTER_LIST_MODEL,
|
||||
"item-type", g_list_model_get_item_type (model),
|
||||
"model", model,
|
||||
"filter", filter,
|
||||
NULL);
|
||||
|
||||
if (filter_func)
|
||||
gtk_filter_list_model_set_filter_func (result, filter_func, user_data, user_destroy);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -492,7 +726,7 @@ gtk_filter_list_model_new (GListModel *model,
|
||||
* @item_type: the type of the items that will be returned
|
||||
*
|
||||
* Creates a new empty filter list model set up to return items of type @item_type.
|
||||
* It is up to the application to set a proper filter function and model to ensure
|
||||
* It is up to the application to set a proper filter and model to ensure
|
||||
* the item type is matched.
|
||||
*
|
||||
* Returns: a new #GtkFilterListModel
|
||||
@ -508,66 +742,53 @@ gtk_filter_list_model_new_for_type (GType item_type)
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_set_filter_func:
|
||||
* gtk_filter_list_model_set_filter:
|
||||
* @self: a #GtkFilterListModel
|
||||
* @filter_func: (allow-none): filter function or %NULL to not filter items
|
||||
* @user_data: (closure): user data passed to @filter_func
|
||||
* @user_destroy: destroy notifier for @user_data
|
||||
* @filter: (allow-none) (transfer none): filter to use or %NULL to not filter items
|
||||
*
|
||||
* Sets the function used to filter items. The function will be called for every
|
||||
* item and if it returns %TRUE the item is considered visible.
|
||||
* Sets the filter used to filter items.
|
||||
**/
|
||||
void
|
||||
gtk_filter_list_model_set_filter_func (GtkFilterListModel *self,
|
||||
GtkFilterListModelFilterFunc filter_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
gtk_filter_list_model_set_filter (GtkFilterListModel *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
gboolean was_filtered, will_be_filtered;
|
||||
|
||||
g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
|
||||
g_return_if_fail (filter_func != NULL || (user_data == NULL && !user_destroy));
|
||||
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
|
||||
|
||||
was_filtered = self->filter_func != NULL;
|
||||
will_be_filtered = filter_func != NULL;
|
||||
|
||||
if (!was_filtered && !will_be_filtered)
|
||||
if (self->filter == filter)
|
||||
return;
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
gtk_filter_list_model_clear_filter (self);
|
||||
|
||||
self->filter_func = filter_func;
|
||||
self->user_data = user_data;
|
||||
self->user_destroy = user_destroy;
|
||||
|
||||
if (!will_be_filtered)
|
||||
if (filter)
|
||||
{
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
self->filter = g_object_ref (filter);
|
||||
g_signal_connect (filter, "changed", G_CALLBACK (gtk_filter_list_model_filter_changed_cb), self);
|
||||
gtk_filter_list_model_filter_changed_cb (filter, GTK_FILTER_CHANGE_DIFFERENT, self);
|
||||
}
|
||||
else if (!was_filtered)
|
||||
else
|
||||
{
|
||||
guint i, n_items;
|
||||
|
||||
self->items = gtk_rb_tree_new (FilterNode,
|
||||
FilterAugment,
|
||||
gtk_filter_list_model_augment,
|
||||
NULL, NULL);
|
||||
if (self->model)
|
||||
{
|
||||
n_items = g_list_model_get_n_items (self->model);
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
FilterNode *node = gtk_rb_tree_insert_before (self->items, NULL);
|
||||
node->visible = TRUE;
|
||||
}
|
||||
}
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
}
|
||||
|
||||
gtk_filter_list_model_refilter (self);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILTER]);
|
||||
}
|
||||
|
||||
if (was_filtered != will_be_filtered)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HAS_FILTER]);
|
||||
/**
|
||||
* gtk_filter_list_model_get_filter:
|
||||
* @self: a #GtkFilterListModel
|
||||
*
|
||||
* Gets the #GtkFilter currently set on @self.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The filter currently in use
|
||||
* or %NULL if the list isn't filtered
|
||||
**/
|
||||
GtkFilter *
|
||||
gtk_filter_list_model_get_filter (GtkFilterListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), FALSE);
|
||||
|
||||
return self->filter;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -579,8 +800,8 @@ gtk_filter_list_model_set_filter_func (GtkFilterListModel *self,
|
||||
*
|
||||
* Note that GTK makes no effort to ensure that @model conforms to
|
||||
* the item type of @self. It assumes that the caller knows what they
|
||||
* are doing and have set up an appropriate filter function to ensure
|
||||
* that item types match.
|
||||
* are doing and have set up an appropriate filter to ensure that item
|
||||
* types match.
|
||||
**/
|
||||
void
|
||||
gtk_filter_list_model_set_model (GtkFilterListModel *self,
|
||||
@ -603,14 +824,23 @@ gtk_filter_list_model_set_model (GtkFilterListModel *self,
|
||||
{
|
||||
self->model = g_object_ref (model);
|
||||
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_filter_list_model_items_changed_cb), self);
|
||||
if (self->items)
|
||||
if (removed == 0)
|
||||
{
|
||||
self->strictness = GTK_FILTER_MATCH_NONE;
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
added = 0;
|
||||
}
|
||||
else if (self->items)
|
||||
added = gtk_filter_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (model));
|
||||
else
|
||||
added = g_list_model_get_n_items (model);
|
||||
}
|
||||
else
|
||||
added = 0;
|
||||
|
||||
{
|
||||
self->strictness = GTK_FILTER_MATCH_NONE;
|
||||
added = 0;
|
||||
}
|
||||
|
||||
if (removed > 0 || added > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
|
||||
|
||||
@ -633,32 +863,7 @@ gtk_filter_list_model_get_model (GtkFilterListModel *self)
|
||||
return self->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_has_filter:
|
||||
* @self: a #GtkFilterListModel
|
||||
*
|
||||
* Checks if a filter function is currently set on @self
|
||||
*
|
||||
* Returns: %TRUE if a filter function is set
|
||||
**/
|
||||
gboolean
|
||||
gtk_filter_list_model_has_filter (GtkFilterListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), FALSE);
|
||||
|
||||
return self->filter_func != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_refilter:
|
||||
* @self: a #GtkFilterListModel
|
||||
*
|
||||
* Causes @self to refilter all items in the model.
|
||||
*
|
||||
* Calling this function is necessary when data used by the filter
|
||||
* function has changed.
|
||||
**/
|
||||
void
|
||||
static void
|
||||
gtk_filter_list_model_refilter (GtkFilterListModel *self)
|
||||
{
|
||||
FilterNode *node;
|
||||
|
@ -26,7 +26,7 @@
|
||||
#endif
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include <gtk/gtkfilter.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
@ -36,42 +36,22 @@ G_BEGIN_DECLS
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkFilterListModel, gtk_filter_list_model, GTK, FILTER_LIST_MODEL, GObject)
|
||||
|
||||
/**
|
||||
* GtkFilterListModelFilterFunc:
|
||||
* @item: (type GObject): The item that may be filtered
|
||||
* @user_data: user data
|
||||
*
|
||||
* User function that is called to determine if the @item of the original model should be visible.
|
||||
* If it should be visible, this function must return %TRUE. If the model should filter out the
|
||||
* @item, %FALSE must be returned.
|
||||
*
|
||||
* Returns: %TRUE to keep the item around
|
||||
*/
|
||||
typedef gboolean (* GtkFilterListModelFilterFunc) (gpointer item, gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilterListModel * gtk_filter_list_model_new (GListModel *model,
|
||||
GtkFilterListModelFilterFunc filter_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilterListModel * gtk_filter_list_model_new_for_type (GType item_type);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_list_model_set_filter_func (GtkFilterListModel *self,
|
||||
GtkFilterListModelFilterFunc filter_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
void gtk_filter_list_model_set_filter (GtkFilterListModel *self,
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_filter_list_model_get_filter (GtkFilterListModel *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_list_model_set_model (GtkFilterListModel *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_filter_list_model_get_model (GtkFilterListModel *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_filter_list_model_has_filter (GtkFilterListModel *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_list_model_refilter (GtkFilterListModel *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
124
gtk/gtkfunctionslistitemfactory.c
Normal file
124
gtk/gtkfunctionslistitemfactory.c
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkfunctionslistitemfactory.h"
|
||||
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
struct _GtkFunctionsListItemFactory
|
||||
{
|
||||
GtkListItemFactory parent_instance;
|
||||
|
||||
GtkListItemSetupFunc setup_func;
|
||||
GtkListItemBindFunc bind_func;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
};
|
||||
|
||||
struct _GtkFunctionsListItemFactoryClass
|
||||
{
|
||||
GtkListItemFactoryClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkFunctionsListItemFactory, gtk_functions_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_setup (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkFunctionsListItemFactory *self = GTK_FUNCTIONS_LIST_ITEM_FACTORY (factory);
|
||||
|
||||
if (self->setup_func)
|
||||
self->setup_func (list_item, self->user_data);
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_functions_list_item_factory_parent_class)->setup (factory, widget, list_item);
|
||||
|
||||
if (gtk_list_item_get_item (list_item) != NULL && self->bind_func)
|
||||
self->bind_func (list_item, self->user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_update (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
GtkFunctionsListItemFactory *self = GTK_FUNCTIONS_LIST_ITEM_FACTORY (factory);
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_functions_list_item_factory_parent_class)->update (factory, widget, list_item, position, item, selected);
|
||||
|
||||
if (item != NULL && self->bind_func)
|
||||
self->bind_func (list_item, self->user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_finalize (GObject *object)
|
||||
{
|
||||
GtkFunctionsListItemFactory *self = GTK_FUNCTIONS_LIST_ITEM_FACTORY (object);
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
G_OBJECT_CLASS (gtk_functions_list_item_factory_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_class_init (GtkFunctionsListItemFactoryClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
|
||||
|
||||
object_class->finalize = gtk_functions_list_item_factory_finalize;
|
||||
|
||||
factory_class->setup = gtk_functions_list_item_factory_setup;
|
||||
factory_class->update = gtk_functions_list_item_factory_update;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_init (GtkFunctionsListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkListItemFactory *
|
||||
gtk_functions_list_item_factory_new (GtkListItemSetupFunc setup_func,
|
||||
GtkListItemBindFunc bind_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
GtkFunctionsListItemFactory *self;
|
||||
|
||||
g_return_val_if_fail (setup_func || bind_func, NULL);
|
||||
g_return_val_if_fail (user_data != NULL || user_destroy == NULL, NULL);
|
||||
|
||||
self = g_object_new (GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, NULL);
|
||||
|
||||
self->setup_func = setup_func;
|
||||
self->bind_func = bind_func;
|
||||
self->user_data = user_data;
|
||||
self->user_destroy = user_destroy;
|
||||
|
||||
return GTK_LIST_ITEM_FACTORY (self);
|
||||
}
|
||||
|
82
gtk/gtkfunctionslistitemfactory.h
Normal file
82
gtk/gtkfunctionslistitemfactory.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_FUNCTIONS_LIST_ITEM_FACTORY_H__
|
||||
#define __GTK_FUNCTIONS_LIST_ITEM_FACTORY_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistitemfactory.h>
|
||||
#include <gtk/gtklistitem.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY (gtk_functions_list_item_factory_get_type ())
|
||||
#define GTK_FUNCTIONS_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, GtkFunctionsListItemFactory))
|
||||
#define GTK_FUNCTIONS_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, GtkFunctionsListItemFactoryClass))
|
||||
#define GTK_IS_FUNCTIONS_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY))
|
||||
#define GTK_IS_FUNCTIONS_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY))
|
||||
#define GTK_FUNCTIONS_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, GtkFunctionsListItemFactoryClass))
|
||||
|
||||
typedef struct _GtkFunctionsListItemFactory GtkFunctionsListItemFactory;
|
||||
typedef struct _GtkFunctionsListItemFactoryClass GtkFunctionsListItemFactoryClass;
|
||||
|
||||
/**
|
||||
* GtkListItemSetupFunc:
|
||||
* @item: the #GtkListItem to set up
|
||||
* @user_data: (closure): user data
|
||||
*
|
||||
* Called whenever a new list item needs to be setup for managing a row in
|
||||
* the list.
|
||||
*
|
||||
* At this point, the list item is not bound yet, so gtk_list_item_get_item()
|
||||
* will return %NULL.
|
||||
* The list item will later be bound to an item via the #GtkListItemBindFunc.
|
||||
*/
|
||||
typedef void (* GtkListItemSetupFunc) (GtkListItem *item, gpointer user_data);
|
||||
|
||||
/**
|
||||
* GtkListItemBindFunc:
|
||||
* @item: the #GtkListItem to bind
|
||||
* @user_data: (closure): user data
|
||||
*
|
||||
* Binds a#GtkListItem previously set up via a #GtkListItemSetupFunc to
|
||||
* an @item.
|
||||
*
|
||||
* Rebinding a @item to different @items is supported as well as
|
||||
* unbinding it by setting @item to %NULL.
|
||||
*/
|
||||
typedef void (* GtkListItemBindFunc) (GtkListItem *item,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_functions_list_item_factory_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory * gtk_functions_list_item_factory_new (GtkListItemSetupFunc setup_func,
|
||||
GtkListItemBindFunc bind_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_FUNCTIONS_LIST_ITEM_FACTORY_H__ */
|
1372
gtk/gtkgridview.c
Normal file
1372
gtk/gtkgridview.c
Normal file
File diff suppressed because it is too large
Load Diff
86
gtk/gtkgridview.h
Normal file
86
gtk/gtkgridview.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_GRID_VIEW_H__
|
||||
#define __GTK_GRID_VIEW_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistbase.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_GRID_VIEW (gtk_grid_view_get_type ())
|
||||
#define GTK_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_GRID_VIEW, GtkGridView))
|
||||
#define GTK_GRID_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_GRID_VIEW, GtkGridViewClass))
|
||||
#define GTK_IS_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_GRID_VIEW))
|
||||
#define GTK_IS_GRID_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_GRID_VIEW))
|
||||
#define GTK_GRID_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_GRID_VIEW, GtkGridViewClass))
|
||||
|
||||
/**
|
||||
* GtkGridView:
|
||||
*
|
||||
* GtkGridView is a list widget implementation that arranges its items in
|
||||
* a grid.
|
||||
*/
|
||||
typedef struct _GtkGridView GtkGridView;
|
||||
typedef struct _GtkGridViewClass GtkGridViewClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_grid_view_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_grid_view_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_grid_view_new_with_factory (GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_grid_view_get_model (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_model (GtkGridView *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_factory (GtkGridView *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory *
|
||||
gtk_grid_view_get_factory (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_grid_view_get_min_columns (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_min_columns (GtkGridView *self,
|
||||
guint min_columns);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_grid_view_get_max_columns (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_max_columns (GtkGridView *self,
|
||||
guint max_columns);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_single_click_activate (GtkGridView *self,
|
||||
gboolean single_click_activate);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_grid_view_get_single_click_activate (GtkGridView *self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_GRID_VIEW_H__ */
|
@ -163,7 +163,6 @@ static void gtk_icon_view_motion (GtkEventControl
|
||||
double y,
|
||||
gpointer user_data);
|
||||
static void gtk_icon_view_leave (GtkEventController *controller,
|
||||
GdkCrossingMode mode,
|
||||
gpointer user_data);
|
||||
static void gtk_icon_view_button_press (GtkGestureClick *gesture,
|
||||
int n_press,
|
||||
@ -1878,9 +1877,8 @@ gtk_icon_view_motion (GtkEventController *controller,
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_icon_view_leave(GtkEventController *controller,
|
||||
GdkCrossingMode mode,
|
||||
gpointer user_data)
|
||||
gtk_icon_view_leave (GtkEventController *controller,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkIconView *icon_view;
|
||||
GtkIconViewPrivate *priv;
|
||||
|
@ -439,7 +439,6 @@ static void gtk_label_motion (GtkEventControllerMotion *controller,
|
||||
double y,
|
||||
gpointer data);
|
||||
static void gtk_label_leave (GtkEventControllerMotion *controller,
|
||||
GdkCrossingMode mode,
|
||||
gpointer data);
|
||||
|
||||
static gboolean gtk_label_grab_focus (GtkWidget *widget);
|
||||
@ -4287,7 +4286,6 @@ gtk_label_motion (GtkEventControllerMotion *controller,
|
||||
|
||||
static void
|
||||
gtk_label_leave (GtkEventControllerMotion *controller,
|
||||
GdkCrossingMode mode,
|
||||
gpointer data)
|
||||
{
|
||||
GtkLabel *self = GTK_LABEL (data);
|
||||
|
1540
gtk/gtklistbase.c
Normal file
1540
gtk/gtklistbase.c
Normal file
File diff suppressed because it is too large
Load Diff
51
gtk/gtklistbase.h
Normal file
51
gtk/gtklistbase.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_BASE_H__
|
||||
#define __GTK_LIST_BASE_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_BASE (gtk_list_base_get_type ())
|
||||
#define GTK_LIST_BASE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_BASE, GtkListBase))
|
||||
#define GTK_LIST_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_BASE, GtkListBaseClass))
|
||||
#define GTK_IS_LIST_BASE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_BASE))
|
||||
#define GTK_IS_LIST_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_BASE))
|
||||
#define GTK_LIST_BASE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_BASE, GtkListBaseClass))
|
||||
|
||||
/**
|
||||
* GtkListBase:
|
||||
*
|
||||
* GtkListBase is the abstract base class for GTK's list widgets.
|
||||
*/
|
||||
typedef struct _GtkListBase GtkListBase;
|
||||
typedef struct _GtkListBaseClass GtkListBaseClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_list_base_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_BASE_H__ */
|
103
gtk/gtklistbaseprivate.h
Normal file
103
gtk/gtklistbaseprivate.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_BASE_PRIVATE_H__
|
||||
#define __GTK_LIST_BASE_PRIVATE_H__
|
||||
|
||||
#include "gtklistbase.h"
|
||||
|
||||
#include "gtklistitemmanagerprivate.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
struct _GtkListBase
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
};
|
||||
|
||||
struct _GtkListBaseClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
|
||||
const char * list_item_name;
|
||||
gsize list_item_size;
|
||||
gsize list_item_augment_size;
|
||||
GtkRbTreeAugmentFunc list_item_augment_func;
|
||||
|
||||
void (* adjustment_value_changed) (GtkListBase *self,
|
||||
GtkOrientation orientation);
|
||||
gboolean (* get_allocation_along) (GtkListBase *self,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size);
|
||||
gboolean (* get_allocation_across) (GtkListBase *self,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size);
|
||||
gboolean (* get_position_from_allocation) (GtkListBase *self,
|
||||
int across,
|
||||
int along,
|
||||
guint *pos,
|
||||
cairo_rectangle_int_t *area);
|
||||
guint (* move_focus_along) (GtkListBase *self,
|
||||
guint pos,
|
||||
int steps);
|
||||
guint (* move_focus_across) (GtkListBase *self,
|
||||
guint pos,
|
||||
int steps);
|
||||
};
|
||||
|
||||
GtkOrientation gtk_list_base_get_orientation (GtkListBase *self);
|
||||
#define gtk_list_base_get_opposite_orientation(self) OPPOSITE_ORIENTATION(gtk_list_base_get_orientation(self))
|
||||
guint gtk_list_base_get_focus_position (GtkListBase *self);
|
||||
GtkListItemManager * gtk_list_base_get_manager (GtkListBase *self);
|
||||
GtkScrollablePolicy gtk_list_base_get_scroll_policy (GtkListBase *self,
|
||||
GtkOrientation orientation);
|
||||
guint gtk_list_base_get_n_items (GtkListBase *self);
|
||||
GListModel * gtk_list_base_get_model (GtkListBase *self);
|
||||
gboolean gtk_list_base_set_model (GtkListBase *self,
|
||||
GListModel *model);
|
||||
void gtk_list_base_update_adjustments (GtkListBase *self,
|
||||
int total_across,
|
||||
int total_along,
|
||||
int page_across,
|
||||
int page_along,
|
||||
int *across,
|
||||
int *along);
|
||||
|
||||
guint gtk_list_base_get_anchor (GtkListBase *self);
|
||||
void gtk_list_base_set_anchor (GtkListBase *self,
|
||||
guint anchor_pos,
|
||||
double anchor_align_across,
|
||||
GtkPackType anchor_side_across,
|
||||
double anchor_align_along,
|
||||
GtkPackType anchor_side_along);
|
||||
void gtk_list_base_set_anchor_max_widgets (GtkListBase *self,
|
||||
guint n_center,
|
||||
guint n_above_below);
|
||||
void gtk_list_base_select_item (GtkListBase *self,
|
||||
guint pos,
|
||||
gboolean modify,
|
||||
gboolean extend);
|
||||
gboolean gtk_list_base_grab_focus_on_item (GtkListBase *self,
|
||||
guint pos,
|
||||
gboolean select,
|
||||
gboolean modify,
|
||||
gboolean extend);
|
||||
|
||||
#endif /* __GTK_LIST_BASE_PRIVATE_H__ */
|
465
gtk/gtklistitem.c
Normal file
465
gtk/gtklistitem.c
Normal file
@ -0,0 +1,465 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtklistitem
|
||||
* @title: GtkListItem
|
||||
* @short_description: Object used to represent items of a ListModel
|
||||
* @see_also: #GtkListView, #GListModel
|
||||
*
|
||||
* #GtkListItem is the object that list-handling containers such
|
||||
* as #GtkListView use to represent items in a #GListModel. They are
|
||||
* managed by the container and cannot be created by application code.
|
||||
*
|
||||
* #GtkListItems need to be populated by application code. This is done by
|
||||
* calling gtk_list_item_set_child().
|
||||
*
|
||||
* #GtkListItems exist in 2 stages:
|
||||
*
|
||||
* 1. The unbound stage where the listitem is not currently connected to
|
||||
* an item in the list. In that case, the #GtkListItem:item property is
|
||||
* set to %NULL.
|
||||
*
|
||||
* 2. The bound stage where the listitem references an item from the list.
|
||||
* The #GtkListItem:item property is not %NULL.
|
||||
*/
|
||||
|
||||
struct _GtkListItemClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_ACTIVATABLE,
|
||||
PROP_CHILD,
|
||||
PROP_ITEM,
|
||||
PROP_POSITION,
|
||||
PROP_SELECTABLE,
|
||||
PROP_SELECTED,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkListItem, gtk_list_item, G_TYPE_OBJECT)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_list_item_dispose (GObject *object)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (object);
|
||||
|
||||
g_assert (self->owner == NULL); /* would hold a reference */
|
||||
g_clear_object (&self->child);
|
||||
|
||||
G_OBJECT_CLASS (gtk_list_item_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_ACTIVATABLE:
|
||||
g_value_set_boolean (value, self->activatable);
|
||||
break;
|
||||
|
||||
case PROP_CHILD:
|
||||
g_value_set_object (value, self->child);
|
||||
break;
|
||||
|
||||
case PROP_ITEM:
|
||||
if (self->owner)
|
||||
g_value_set_object (value, gtk_list_item_widget_get_item (self->owner));
|
||||
break;
|
||||
|
||||
case PROP_POSITION:
|
||||
if (self->owner)
|
||||
g_value_set_uint (value, gtk_list_item_widget_get_position (self->owner));
|
||||
else
|
||||
g_value_set_uint (value, GTK_INVALID_LIST_POSITION);
|
||||
break;
|
||||
|
||||
case PROP_SELECTABLE:
|
||||
g_value_set_boolean (value, self->selectable);
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
if (self->owner)
|
||||
g_value_set_boolean (value, gtk_list_item_widget_get_selected (self->owner));
|
||||
else
|
||||
g_value_set_boolean (value, FALSE);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_ACTIVATABLE:
|
||||
gtk_list_item_set_activatable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_CHILD:
|
||||
gtk_list_item_set_child (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SELECTABLE:
|
||||
gtk_list_item_set_selectable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_class_init (GtkListItemClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gtk_list_item_dispose;
|
||||
gobject_class->get_property = gtk_list_item_get_property;
|
||||
gobject_class->set_property = gtk_list_item_set_property;
|
||||
|
||||
/**
|
||||
* GtkListItem:activatable:
|
||||
*
|
||||
* If the item can be activated by the user
|
||||
*/
|
||||
properties[PROP_ACTIVATABLE] =
|
||||
g_param_spec_boolean ("activatable",
|
||||
P_("Activatable"),
|
||||
P_("If the item can be activated by the user"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:child:
|
||||
*
|
||||
* Widget used for display
|
||||
*/
|
||||
properties[PROP_CHILD] =
|
||||
g_param_spec_object ("child",
|
||||
P_("Child"),
|
||||
P_("Widget used for display"),
|
||||
GTK_TYPE_WIDGET,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:item:
|
||||
*
|
||||
* Displayed item
|
||||
*/
|
||||
properties[PROP_ITEM] =
|
||||
g_param_spec_object ("item",
|
||||
P_("Item"),
|
||||
P_("Displayed item"),
|
||||
G_TYPE_OBJECT,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:position:
|
||||
*
|
||||
* Position of the item
|
||||
*/
|
||||
properties[PROP_POSITION] =
|
||||
g_param_spec_uint ("position",
|
||||
P_("Position"),
|
||||
P_("Position of the item"),
|
||||
0, G_MAXUINT, GTK_INVALID_LIST_POSITION,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:selectable:
|
||||
*
|
||||
* If the item can be selected by the user
|
||||
*/
|
||||
properties[PROP_SELECTABLE] =
|
||||
g_param_spec_boolean ("selectable",
|
||||
P_("Selectable"),
|
||||
P_("If the item can be selected by the user"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:selected:
|
||||
*
|
||||
* If the item is currently selected
|
||||
*/
|
||||
properties[PROP_SELECTED] =
|
||||
g_param_spec_boolean ("selected",
|
||||
P_("Selected"),
|
||||
P_("If the item is currently selected"),
|
||||
FALSE,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_init (GtkListItem *self)
|
||||
{
|
||||
self->selectable = TRUE;
|
||||
self->activatable = TRUE;
|
||||
}
|
||||
|
||||
GtkListItem *
|
||||
gtk_list_item_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_LIST_ITEM,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_item:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Gets the item that is currently displayed in model that @self is
|
||||
* currently bound to or %NULL if @self is unbound.
|
||||
*
|
||||
* Returns: (nullable) (transfer none) (type GObject): The item displayed
|
||||
**/
|
||||
gpointer
|
||||
gtk_list_item_get_item (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), NULL);
|
||||
|
||||
if (self->owner == NULL)
|
||||
return NULL;
|
||||
|
||||
return gtk_list_item_widget_get_item (self->owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_child:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Gets the child previously set via gtk_list_item_set_child() or
|
||||
* %NULL if none was set.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The child
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_list_item_get_child (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), NULL);
|
||||
|
||||
return self->child;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_set_child:
|
||||
* @self: a #GtkListItem
|
||||
* @child: (nullable): The list item's child or %NULL to unset
|
||||
*
|
||||
* Sets the child to be used for this listitem.
|
||||
*
|
||||
* This function is typically called by applications when
|
||||
* setting up a listitem so that the widget can be reused when
|
||||
* binding it multiple times.
|
||||
**/
|
||||
void
|
||||
gtk_list_item_set_child (GtkListItem *self,
|
||||
GtkWidget *child)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
g_return_if_fail (child == NULL || GTK_IS_WIDGET (child));
|
||||
|
||||
if (self->child == child)
|
||||
return;
|
||||
|
||||
if (self->child && self->owner)
|
||||
gtk_list_item_widget_remove_child (self->owner, self->child);
|
||||
|
||||
g_clear_object (&self->child);
|
||||
|
||||
if (child)
|
||||
{
|
||||
g_object_ref_sink (child);
|
||||
self->child = child;
|
||||
|
||||
if (self->owner)
|
||||
gtk_list_item_widget_add_child (self->owner, child);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_position:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Gets the position in the model that @self currently displays.
|
||||
* If @self is unbound, 0 is returned.
|
||||
*
|
||||
* Returns: The position of this item
|
||||
**/
|
||||
guint
|
||||
gtk_list_item_get_position (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), GTK_INVALID_LIST_POSITION);
|
||||
|
||||
if (self->owner == NULL)
|
||||
return GTK_INVALID_LIST_POSITION;
|
||||
|
||||
return gtk_list_item_widget_get_position (self->owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_selected:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Checks if the item is displayed as selected. The selected state is
|
||||
* maintained by the container and its list model and cannot be set
|
||||
* otherwise.
|
||||
*
|
||||
* Returns: %TRUE if the item is selected.
|
||||
**/
|
||||
gboolean
|
||||
gtk_list_item_get_selected (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
|
||||
|
||||
if (self->owner == NULL)
|
||||
return FALSE;
|
||||
|
||||
return gtk_list_item_widget_get_selected (self->owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_selectable:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Checks if a list item has been set to be selectable via
|
||||
* gtk_list_item_set_selectable().
|
||||
*
|
||||
* Do not confuse this function with gtk_list_item_get_selected().
|
||||
*
|
||||
* Returns: %TRUE if the item is selectable
|
||||
**/
|
||||
gboolean
|
||||
gtk_list_item_get_selectable (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
|
||||
|
||||
return self->selectable;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_set_selectable:
|
||||
* @self: a #GtkListItem
|
||||
* @selectable: if the item should be selectable
|
||||
*
|
||||
* Sets @self to be selectable. If an item is selectable, clicking
|
||||
* on the item or using the keyboard will try to select or unselect
|
||||
* the item. If this succeeds is up to the model to determine, as
|
||||
* it is managing the selected state.
|
||||
*
|
||||
* Note that this means that making an item non-selectable has no
|
||||
* influence on the selected state at all. A non-selectable item
|
||||
* may still be selected.
|
||||
*
|
||||
* By default, list items are selectable. When rebinding them to
|
||||
* a new item, they will also be reset to be selectable by GTK.
|
||||
**/
|
||||
void
|
||||
gtk_list_item_set_selectable (GtkListItem *self,
|
||||
gboolean selectable)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
|
||||
if (self->selectable == selectable)
|
||||
return;
|
||||
|
||||
self->selectable = selectable;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTABLE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_activatable:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Checks if a list item has been set to be activatable via
|
||||
* gtk_list_item_set_activatable().
|
||||
*
|
||||
* Returns: %TRUE if the item is activatable
|
||||
**/
|
||||
gboolean
|
||||
gtk_list_item_get_activatable (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
|
||||
|
||||
return self->activatable;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_set_activatable:
|
||||
* @self: a #GtkListItem
|
||||
* @activatable: if the item should be activatable
|
||||
*
|
||||
* Sets @self to be activatable.
|
||||
*
|
||||
* If an item is activatable, double-clicking on the item, using
|
||||
* the <Return> key or calling gtk_widget_activate() will activate
|
||||
* the item. Activating instructs the containing view to handle
|
||||
* activation. #GtkListView for example will be emitting the
|
||||
* GtkListView::activate signal.
|
||||
*
|
||||
* By default, list items are activatable
|
||||
**/
|
||||
void
|
||||
gtk_list_item_set_activatable (GtkListItem *self,
|
||||
gboolean activatable)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
|
||||
if (self->activatable == activatable)
|
||||
return;
|
||||
|
||||
self->activatable = activatable;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVATABLE]);
|
||||
}
|
70
gtk/gtklistitem.h
Normal file
70
gtk/gtklistitem.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_H__
|
||||
#define __GTK_LIST_ITEM_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM (gtk_list_item_get_type ())
|
||||
#define GTK_LIST_ITEM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM, GtkListItem))
|
||||
#define GTK_LIST_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM, GtkListItemClass))
|
||||
#define GTK_IS_LIST_ITEM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM))
|
||||
#define GTK_IS_LIST_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM))
|
||||
#define GTK_LIST_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM, GtkListItemClass))
|
||||
|
||||
typedef struct _GtkListItem GtkListItem;
|
||||
typedef struct _GtkListItemClass GtkListItemClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_list_item_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gpointer gtk_list_item_get_item (GtkListItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_list_item_get_position (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_selected (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_selectable (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_item_set_selectable (GtkListItem *self,
|
||||
gboolean selectable);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_activatable (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_item_set_activatable (GtkListItem *self,
|
||||
gboolean activatable);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_item_set_child (GtkListItem *self,
|
||||
GtkWidget *child);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_list_item_get_child (GtkListItem *self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_H__ */
|
166
gtk/gtklistitemfactory.c
Normal file
166
gtk/gtklistitemfactory.c
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtklistitemfactory
|
||||
* @Title: GtkListItemFactory
|
||||
* @Short_description: Mapping list items to widgets
|
||||
*
|
||||
* #GtkListItemFactory is one of the core concepts of handling list widgets.
|
||||
* It is the object tasked with creating widgets for items taken from a
|
||||
* #GListModel when the views need them and updating them as the items
|
||||
* displayed by the view change.
|
||||
*
|
||||
* A view is usually only able to display anything after both a factory
|
||||
* and a model have been set on the view. So it is important that you do
|
||||
* not skip this step when setting up your first view.
|
||||
*
|
||||
* Because views do not display the whole list at once but only a few
|
||||
* items, they only need to maintain a few widgets at a time. They will
|
||||
* instruct the #GtkListItemFactory to create these widgets and bind them
|
||||
* to the items that are currently displayed.
|
||||
* As the list model changes or the user scrolls to the list, the items will
|
||||
* change and the view will instruct the factory to bind the widgets to those
|
||||
* new items.
|
||||
*
|
||||
* The actual widgets used for displaying those widgets is provided by you.
|
||||
*
|
||||
* When the factory needs widgets created, it will create a #GtkListItem and
|
||||
* hand it to your code to set up a widget for. This list item will provide
|
||||
* various properties with information about what item to display and provide
|
||||
* you with some opportunities to configure its behavior. See the #GtkListItem
|
||||
* documentation for further details.
|
||||
*
|
||||
* Various implementations of #GtkListItemFactory exist to allow you different
|
||||
* ways to provide those widgets. The most common implementations are
|
||||
* #GtkBuilderListItemFactory which takes a #GtkBuilder .ui file and then creates
|
||||
* and manages widgets everything automatically from the information in that file
|
||||
* and #GtkSignalListItemFactory which allows you to connect to signals with your
|
||||
* own code and retain full control over how the widgets are setup and managed.
|
||||
*
|
||||
* A #GtkListItemFactory is supposed to be final - that means its behavior should
|
||||
* not change and the first widget created from it should behave the same way as
|
||||
* the last widget created from it.
|
||||
* If you intend to do changes to the behavior, it is recommended that you create
|
||||
* a new #GtkListItemFactory which will allow the views to recreate its widgets.
|
||||
*
|
||||
* Once you have chosen your factory and created it, you need to set it on the
|
||||
* view widget you want to use it with, such as via gtk_list_view_set_factory().
|
||||
* Reusing factories across different views is allowed, but very uncommon.
|
||||
*/
|
||||
|
||||
G_DEFINE_TYPE (GtkListItemFactory, gtk_list_item_factory, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_setup (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
gtk_list_item_widget_default_setup (widget, list_item);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_teardown (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
gtk_list_item_widget_default_teardown (widget, list_item);
|
||||
|
||||
gtk_list_item_set_child (list_item, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_update (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
gtk_list_item_widget_default_update (widget, list_item, position, item, selected);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_class_init (GtkListItemFactoryClass *klass)
|
||||
{
|
||||
klass->setup = gtk_list_item_factory_default_setup;
|
||||
klass->teardown = gtk_list_item_factory_default_teardown;
|
||||
klass->update = gtk_list_item_factory_default_update;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_init (GtkListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_setup (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget)
|
||||
{
|
||||
GtkListItem *list_item;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
|
||||
list_item = gtk_list_item_new ();
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->setup (self, widget, list_item);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_teardown (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget)
|
||||
{
|
||||
GtkListItem *list_item;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
|
||||
list_item = gtk_list_item_widget_get_list_item (widget);
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->teardown (self, widget, list_item);
|
||||
|
||||
g_object_unref (list_item);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_update (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
GtkListItem *list_item;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_WIDGET (widget));
|
||||
|
||||
list_item = gtk_list_item_widget_get_list_item (widget);
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (list_item));
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->update (self, widget, list_item, position, item, selected);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (list_item));
|
||||
}
|
||||
|
48
gtk/gtklistitemfactory.h
Normal file
48
gtk/gtklistitemfactory.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_FACTORY_H__
|
||||
#define __GTK_LIST_ITEM_FACTORY_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
typedef struct _GtkListItemFactoryClass GtkListItemFactoryClass;
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM_FACTORY (gtk_list_item_factory_get_type ())
|
||||
#define GTK_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactory))
|
||||
#define GTK_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactoryClass))
|
||||
#define GTK_IS_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_FACTORY))
|
||||
#define GTK_IS_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_FACTORY))
|
||||
#define GTK_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactoryClass))
|
||||
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_list_item_factory_get_type (void) G_GNUC_CONST;
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_FACTORY_H__ */
|
71
gtk/gtklistitemfactoryprivate.h
Normal file
71
gtk/gtklistitemfactoryprivate.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_FACTORY_PRIVATE_H__
|
||||
#define __GTK_LIST_ITEM_FACTORY_PRIVATE_H__
|
||||
|
||||
#include <gtk/gtklistitem.h>
|
||||
#include "gtk/gtklistitemwidgetprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct _GtkListItemFactory
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
struct _GtkListItemFactoryClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* setup @list_item so it can be bound */
|
||||
void (* setup) (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item);
|
||||
/* undo the effects of GtkListItemFactoryClass::setup() */
|
||||
void (* teardown) (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item);
|
||||
|
||||
/* Update properties on @list_item to the given @item, which is in @position and @selected state.
|
||||
* One or more of those properties might be unchanged. */
|
||||
void (* update) (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
};
|
||||
|
||||
void gtk_list_item_factory_setup (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget);
|
||||
void gtk_list_item_factory_teardown (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget);
|
||||
|
||||
void gtk_list_item_factory_update (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_FACTORY_PRIVATE_H__ */
|
1180
gtk/gtklistitemmanager.c
Normal file
1180
gtk/gtklistitemmanager.c
Normal file
File diff suppressed because it is too large
Load Diff
110
gtk/gtklistitemmanagerprivate.h
Normal file
110
gtk/gtklistitemmanagerprivate.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_MANAGER_H__
|
||||
#define __GTK_LIST_ITEM_MANAGER_H__
|
||||
|
||||
#include "gtk/gtktypes.h"
|
||||
|
||||
#include "gtk/gtklistitemfactory.h"
|
||||
#include "gtk/gtkrbtreeprivate.h"
|
||||
#include "gtk/gtkselectionmodel.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM_MANAGER (gtk_list_item_manager_get_type ())
|
||||
#define GTK_LIST_ITEM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManager))
|
||||
#define GTK_LIST_ITEM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManagerClass))
|
||||
#define GTK_IS_LIST_ITEM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_MANAGER))
|
||||
#define GTK_IS_LIST_ITEM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_MANAGER))
|
||||
#define GTK_LIST_ITEM_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManagerClass))
|
||||
|
||||
typedef struct _GtkListItemManager GtkListItemManager;
|
||||
typedef struct _GtkListItemManagerClass GtkListItemManagerClass;
|
||||
typedef struct _GtkListItemManagerItem GtkListItemManagerItem; /* sorry */
|
||||
typedef struct _GtkListItemManagerItemAugment GtkListItemManagerItemAugment;
|
||||
typedef struct _GtkListItemTracker GtkListItemTracker;
|
||||
|
||||
struct _GtkListItemManagerItem
|
||||
{
|
||||
GtkWidget *widget;
|
||||
guint n_items;
|
||||
};
|
||||
|
||||
struct _GtkListItemManagerItemAugment
|
||||
{
|
||||
guint n_items;
|
||||
};
|
||||
|
||||
|
||||
GType gtk_list_item_manager_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkListItemManager * gtk_list_item_manager_new_for_size (GtkWidget *widget,
|
||||
const char *item_css_name,
|
||||
gsize element_size,
|
||||
gsize augment_size,
|
||||
GtkRbTreeAugmentFunc augment_func);
|
||||
#define gtk_list_item_manager_new(widget, item_css_name, type, augment_type, augment_func) \
|
||||
gtk_list_item_manager_new_for_size (widget, item_css_name, sizeof (type), sizeof (augment_type), (augment_func))
|
||||
|
||||
void gtk_list_item_manager_augment_node (GtkRbTree *tree,
|
||||
gpointer node_augment,
|
||||
gpointer node,
|
||||
gpointer left,
|
||||
gpointer right);
|
||||
gpointer gtk_list_item_manager_get_root (GtkListItemManager *self);
|
||||
gpointer gtk_list_item_manager_get_first (GtkListItemManager *self);
|
||||
gpointer gtk_list_item_manager_get_nth (GtkListItemManager *self,
|
||||
guint position,
|
||||
guint *offset);
|
||||
guint gtk_list_item_manager_get_item_position (GtkListItemManager *self,
|
||||
gpointer item);
|
||||
gpointer gtk_list_item_manager_get_item_augment (GtkListItemManager *self,
|
||||
gpointer item);
|
||||
|
||||
void gtk_list_item_manager_set_factory (GtkListItemManager *self,
|
||||
GtkListItemFactory *factory);
|
||||
GtkListItemFactory * gtk_list_item_manager_get_factory (GtkListItemManager *self);
|
||||
void gtk_list_item_manager_set_model (GtkListItemManager *self,
|
||||
GtkSelectionModel *model);
|
||||
GtkSelectionModel * gtk_list_item_manager_get_model (GtkListItemManager *self);
|
||||
|
||||
guint gtk_list_item_manager_get_size (GtkListItemManager *self);
|
||||
void gtk_list_item_manager_set_single_click_activate
|
||||
(GtkListItemManager *self,
|
||||
gboolean single_click_activate);
|
||||
gboolean gtk_list_item_manager_get_single_click_activate
|
||||
(GtkListItemManager *self);
|
||||
|
||||
GtkListItemTracker * gtk_list_item_tracker_new (GtkListItemManager *self);
|
||||
void gtk_list_item_tracker_free (GtkListItemManager *self,
|
||||
GtkListItemTracker *tracker);
|
||||
void gtk_list_item_tracker_set_position (GtkListItemManager *self,
|
||||
GtkListItemTracker *tracker,
|
||||
guint position,
|
||||
guint n_before,
|
||||
guint n_after);
|
||||
guint gtk_list_item_tracker_get_position (GtkListItemManager *self,
|
||||
GtkListItemTracker *tracker);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_MANAGER_H__ */
|
46
gtk/gtklistitemprivate.h
Normal file
46
gtk/gtklistitemprivate.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_PRIVATE_H__
|
||||
#define __GTK_LIST_ITEM_PRIVATE_H__
|
||||
|
||||
#include "gtklistitem.h"
|
||||
|
||||
#include "gtklistitemwidgetprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct _GtkListItem
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkListItemWidget *owner; /* has a reference */
|
||||
|
||||
GtkWidget *child;
|
||||
|
||||
guint activatable : 1;
|
||||
guint selectable : 1;
|
||||
};
|
||||
|
||||
GtkListItem * gtk_list_item_new (void);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_PRIVATE_H__ */
|
628
gtk/gtklistitemwidget.c
Normal file
628
gtk/gtklistitemwidget.c
Normal file
@ -0,0 +1,628 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtklistitemwidgetprivate.h"
|
||||
|
||||
#include "gtkbinlayout.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkeventcontrollerfocus.h"
|
||||
#include "gtkeventcontrollermotion.h"
|
||||
#include "gtkgestureclick.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
#include "gtkwidget.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
typedef struct _GtkListItemWidgetPrivate GtkListItemWidgetPrivate;
|
||||
struct _GtkListItemWidgetPrivate
|
||||
{
|
||||
GtkListItemFactory *factory;
|
||||
GtkListItem *list_item;
|
||||
|
||||
GObject *item;
|
||||
guint position;
|
||||
gboolean selected;
|
||||
gboolean single_click_activate;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_FACTORY,
|
||||
PROP_SINGLE_CLICK_ACTIVATE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ACTIVATE_SIGNAL,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GtkListItemWidget, gtk_list_item_widget, GTK_TYPE_WIDGET)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_activate_signal (GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
if (priv->list_item && !priv->list_item->activatable)
|
||||
return;
|
||||
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.activate-item",
|
||||
"u",
|
||||
priv->position);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_item_widget_focus (GtkWidget *widget,
|
||||
GtkDirectionType direction)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
/* The idea of this function is the following:
|
||||
* 1. If any child can take focus, do not ever attempt
|
||||
* to take focus.
|
||||
* 2. Otherwise, if this item is selectable or activatable,
|
||||
* allow focusing this widget.
|
||||
*
|
||||
* This makes sure every item in a list is focusable for
|
||||
* activation and selection handling, but no useless widgets
|
||||
* get focused and moving focus is as fast as possible.
|
||||
*/
|
||||
if (priv->list_item && priv->list_item->child)
|
||||
{
|
||||
if (gtk_widget_get_focus_child (widget))
|
||||
return FALSE;
|
||||
if (gtk_widget_child_focus (priv->list_item->child, direction))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (gtk_widget_is_focus (widget))
|
||||
return FALSE;
|
||||
|
||||
if (!gtk_widget_get_can_focus (widget) ||
|
||||
!priv->list_item->selectable)
|
||||
return FALSE;
|
||||
|
||||
return gtk_widget_grab_focus (widget);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_item_widget_grab_focus (GtkWidget *widget)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
if (priv->list_item && priv->list_item->child && gtk_widget_grab_focus (priv->list_item->child))
|
||||
return TRUE;
|
||||
|
||||
return GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->grab_focus (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_root (GtkWidget *widget)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->root (widget);
|
||||
|
||||
if (priv->factory)
|
||||
gtk_list_item_factory_setup (priv->factory, self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_unroot (GtkWidget *widget)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->unroot (widget);
|
||||
|
||||
if (priv->list_item)
|
||||
gtk_list_item_factory_teardown (priv->factory, self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
gtk_list_item_widget_set_factory (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SINGLE_CLICK_ACTIVATE:
|
||||
gtk_list_item_widget_set_single_click_activate (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_dispose (GObject *object)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (object);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
g_assert (priv->list_item == NULL);
|
||||
|
||||
g_clear_object (&priv->item);
|
||||
g_clear_object (&priv->factory);
|
||||
|
||||
G_OBJECT_CLASS (gtk_list_item_widget_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_select_action (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
gboolean modify, extend;
|
||||
|
||||
if (priv->list_item && !priv->list_item->selectable)
|
||||
return;
|
||||
|
||||
g_variant_get (parameter, "(bb)", &modify, &extend);
|
||||
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.select-item",
|
||||
"(ubb)",
|
||||
priv->position, modify, extend);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_class_init (GtkListItemWidgetClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
klass->activate_signal = gtk_list_item_widget_activate_signal;
|
||||
|
||||
widget_class->focus = gtk_list_item_widget_focus;
|
||||
widget_class->grab_focus = gtk_list_item_widget_grab_focus;
|
||||
widget_class->root = gtk_list_item_widget_root;
|
||||
widget_class->unroot = gtk_list_item_widget_unroot;
|
||||
|
||||
gobject_class->set_property = gtk_list_item_widget_set_property;
|
||||
gobject_class->dispose = gtk_list_item_widget_dispose;
|
||||
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
"Factory",
|
||||
"Factory managing this list item",
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_SINGLE_CLICK_ACTIVATE] =
|
||||
g_param_spec_boolean ("single-click-activate",
|
||||
"Single click activate",
|
||||
"Activate on single click",
|
||||
FALSE,
|
||||
G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
signals[ACTIVATE_SIGNAL] =
|
||||
g_signal_new (I_("activate-keybinding"),
|
||||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET (GtkListItemWidgetClass, activate_signal),
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
widget_class->activate_signal = signals[ACTIVATE_SIGNAL];
|
||||
|
||||
/**
|
||||
* GtkListItem|listitem.select:
|
||||
* @modify: %TRUE to toggle the existing selection, %FALSE to select
|
||||
* @extend: %TRUE to extend the selection
|
||||
*
|
||||
* Changes selection if the item is selectable.
|
||||
* If the item is not selectable, nothing happens.
|
||||
*
|
||||
* This function will emit the list.select-item action and the resulting
|
||||
* behavior, in particular the interpretation of @modify and @extend
|
||||
* depends on the view containing this listitem. See for example
|
||||
* GtkListView|list.select-item or GtkGridView|list.select-item.
|
||||
*/
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"listitem.select",
|
||||
"(bb)",
|
||||
gtk_list_item_widget_select_action);
|
||||
|
||||
gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, 0,
|
||||
"activate-keybinding", 0);
|
||||
gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, 0,
|
||||
"activate-keybinding", 0);
|
||||
gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter, 0,
|
||||
"activate-keybinding", 0);
|
||||
|
||||
/* note that some of these may get overwritten by child widgets,
|
||||
* such as GtkTreeExpander */
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, 0,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, 0,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
|
||||
/* This gets overwritten by gtk_list_item_widget_new() but better safe than sorry */
|
||||
gtk_widget_class_set_css_name (widget_class, I_("row"));
|
||||
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_click_gesture_pressed (GtkGestureClick *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
|
||||
if (priv->list_item && !priv->list_item->selectable && !priv->list_item->activatable)
|
||||
{
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!priv->list_item || priv->list_item->selectable)
|
||||
{
|
||||
GdkModifierType state;
|
||||
GdkEvent *event;
|
||||
gboolean extend, modify;
|
||||
|
||||
event = gtk_gesture_get_last_event (GTK_GESTURE (gesture),
|
||||
gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)));
|
||||
state = gdk_event_get_modifier_state (event);
|
||||
extend = (state & GDK_SHIFT_MASK) != 0;
|
||||
modify = (state & GDK_CONTROL_MASK) != 0;
|
||||
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.select-item",
|
||||
"(ubb)",
|
||||
priv->position, modify, extend);
|
||||
}
|
||||
|
||||
if (!priv->list_item || priv->list_item->activatable)
|
||||
{
|
||||
if (n_press == 2 || priv->single_click_activate)
|
||||
{
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.activate-item",
|
||||
"u",
|
||||
priv->position);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE);
|
||||
|
||||
if (gtk_widget_get_focus_on_click (widget))
|
||||
gtk_widget_grab_focus (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_enter_cb (GtkEventControllerFocus *controller,
|
||||
GtkListItemWidget *self)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
gtk_widget_activate_action (widget,
|
||||
"list.scroll-to-item",
|
||||
"u",
|
||||
priv->position);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_hover_cb (GtkEventControllerMotion *controller,
|
||||
double x,
|
||||
double y,
|
||||
GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
if (!priv->single_click_activate)
|
||||
return;
|
||||
|
||||
if (!priv->list_item || priv->list_item->selectable)
|
||||
{
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.select-item",
|
||||
"(ubb)",
|
||||
priv->position, FALSE, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_click_gesture_released (GtkGestureClick *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkListItemWidget *self)
|
||||
{
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_click_gesture_canceled (GtkGestureClick *gesture,
|
||||
GdkEventSequence *sequence,
|
||||
GtkListItemWidget *self)
|
||||
{
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_init (GtkListItemWidget *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
GtkGesture *gesture;
|
||||
|
||||
gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
|
||||
|
||||
gesture = gtk_gesture_click_new ();
|
||||
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
|
||||
GTK_PHASE_BUBBLE);
|
||||
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture),
|
||||
FALSE);
|
||||
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
|
||||
GDK_BUTTON_PRIMARY);
|
||||
g_signal_connect (gesture, "pressed",
|
||||
G_CALLBACK (gtk_list_item_widget_click_gesture_pressed), self);
|
||||
g_signal_connect (gesture, "released",
|
||||
G_CALLBACK (gtk_list_item_widget_click_gesture_released), self);
|
||||
g_signal_connect (gesture, "cancel",
|
||||
G_CALLBACK (gtk_list_item_widget_click_gesture_canceled), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
|
||||
|
||||
controller = gtk_event_controller_focus_new ();
|
||||
g_signal_connect (controller, "enter", G_CALLBACK (gtk_list_item_widget_enter_cb), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
controller = gtk_event_controller_motion_new ();
|
||||
g_signal_connect (controller, "enter", G_CALLBACK (gtk_list_item_widget_hover_cb), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_list_item_widget_new (GtkListItemFactory *factory,
|
||||
const char *css_name)
|
||||
{
|
||||
g_return_val_if_fail (css_name != NULL, NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_LIST_ITEM_WIDGET,
|
||||
"css-name", css_name,
|
||||
"factory", factory,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_update (GtkListItemWidget *self,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
if (priv->list_item)
|
||||
gtk_list_item_factory_update (priv->factory, self, position, item, selected);
|
||||
else
|
||||
gtk_list_item_widget_default_update (self, NULL, position, item, selected);
|
||||
|
||||
if (selected)
|
||||
gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED, FALSE);
|
||||
else
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_default_setup (GtkListItemWidget *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
priv->list_item = list_item;
|
||||
list_item->owner = self;
|
||||
|
||||
if (list_item->child)
|
||||
gtk_list_item_widget_add_child (self, list_item->child);
|
||||
|
||||
if (priv->item)
|
||||
g_object_notify (G_OBJECT (list_item), "item");
|
||||
if (priv->position != GTK_INVALID_LIST_POSITION)
|
||||
g_object_notify (G_OBJECT (list_item), "position");
|
||||
if (priv->selected)
|
||||
g_object_notify (G_OBJECT (list_item), "selected");
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_default_teardown (GtkListItemWidget *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
g_assert (priv->list_item == list_item);
|
||||
|
||||
priv->list_item = NULL;
|
||||
list_item->owner = NULL;
|
||||
|
||||
if (list_item->child)
|
||||
gtk_list_item_widget_remove_child (self, list_item->child);
|
||||
|
||||
if (priv->item)
|
||||
g_object_notify (G_OBJECT (list_item), "item");
|
||||
if (priv->position != GTK_INVALID_LIST_POSITION)
|
||||
g_object_notify (G_OBJECT (list_item), "position");
|
||||
if (priv->selected)
|
||||
g_object_notify (G_OBJECT (list_item), "selected");
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_default_update (GtkListItemWidget *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
/* FIXME: It's kinda evil to notify external objects from here... */
|
||||
|
||||
if (g_set_object (&priv->item, item))
|
||||
{
|
||||
if (list_item)
|
||||
g_object_notify (G_OBJECT (list_item), "item");
|
||||
}
|
||||
|
||||
if (priv->position != position)
|
||||
{
|
||||
priv->position = position;
|
||||
if (list_item)
|
||||
g_object_notify (G_OBJECT (list_item), "position");
|
||||
}
|
||||
|
||||
if (priv->selected != selected)
|
||||
{
|
||||
priv->selected = selected;
|
||||
if (list_item)
|
||||
g_object_notify (G_OBJECT (list_item), "selected");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_set_factory (GtkListItemWidget *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
if (priv->factory == factory)
|
||||
return;
|
||||
|
||||
if (priv->factory)
|
||||
{
|
||||
if (priv->list_item)
|
||||
gtk_list_item_factory_teardown (factory, self);
|
||||
g_clear_object (&priv->factory);
|
||||
}
|
||||
|
||||
if (factory)
|
||||
{
|
||||
priv->factory = g_object_ref (factory);
|
||||
|
||||
if (gtk_widget_get_root (GTK_WIDGET (self)))
|
||||
gtk_list_item_factory_setup (factory, self);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_set_single_click_activate (GtkListItemWidget *self,
|
||||
gboolean single_click_activate)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
if (priv->single_click_activate == single_click_activate)
|
||||
return;
|
||||
|
||||
priv->single_click_activate = single_click_activate;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SINGLE_CLICK_ACTIVATE]);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_add_child (GtkListItemWidget *self,
|
||||
GtkWidget *child)
|
||||
{
|
||||
gtk_widget_set_parent (child, GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_remove_child (GtkListItemWidget *self,
|
||||
GtkWidget *child)
|
||||
{
|
||||
gtk_widget_unparent (child);
|
||||
}
|
||||
|
||||
GtkListItem *
|
||||
gtk_list_item_widget_get_list_item (GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
return priv->list_item;
|
||||
}
|
||||
|
||||
guint
|
||||
gtk_list_item_widget_get_position (GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
return priv->position;
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_list_item_widget_get_item (GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
return priv->item;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_list_item_widget_get_selected (GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
return priv->selected;
|
||||
}
|
||||
|
87
gtk/gtklistitemwidgetprivate.h
Normal file
87
gtk/gtklistitemwidgetprivate.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_WIDGET_PRIVATE_H__
|
||||
#define __GTK_LIST_ITEM_WIDGET_PRIVATE_H__
|
||||
|
||||
#include "gtklistitemfactory.h"
|
||||
#include "gtkwidget.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM_WIDGET (gtk_list_item_widget_get_type ())
|
||||
#define GTK_LIST_ITEM_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_WIDGET, GtkListItemWidget))
|
||||
#define GTK_LIST_ITEM_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_WIDGET, GtkListItemWidgetClass))
|
||||
#define GTK_IS_LIST_ITEM_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_WIDGET))
|
||||
#define GTK_IS_LIST_ITEM_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_WIDGET))
|
||||
#define GTK_LIST_ITEM_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_WIDGET, GtkListItemWidgetClass))
|
||||
|
||||
typedef struct _GtkListItemWidget GtkListItemWidget;
|
||||
typedef struct _GtkListItemWidgetClass GtkListItemWidgetClass;
|
||||
|
||||
struct _GtkListItemWidget
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
};
|
||||
|
||||
struct _GtkListItemWidgetClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
|
||||
void (* activate_signal) (GtkListItemWidget *self);
|
||||
};
|
||||
|
||||
GType gtk_list_item_widget_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkWidget * gtk_list_item_widget_new (GtkListItemFactory *factory,
|
||||
const char *css_name);
|
||||
|
||||
void gtk_list_item_widget_update (GtkListItemWidget *self,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
GtkListItem * gtk_list_item_widget_get_list_item (GtkListItemWidget *self);
|
||||
|
||||
void gtk_list_item_widget_default_setup (GtkListItemWidget *self,
|
||||
GtkListItem *list_item);
|
||||
void gtk_list_item_widget_default_teardown (GtkListItemWidget *self,
|
||||
GtkListItem *list_item);
|
||||
void gtk_list_item_widget_default_update (GtkListItemWidget *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
|
||||
void gtk_list_item_widget_set_factory (GtkListItemWidget *self,
|
||||
GtkListItemFactory *factory);
|
||||
void gtk_list_item_widget_set_single_click_activate
|
||||
(GtkListItemWidget *self,
|
||||
gboolean single_click_activate);
|
||||
void gtk_list_item_widget_add_child (GtkListItemWidget *self,
|
||||
GtkWidget *child);
|
||||
void gtk_list_item_widget_remove_child (GtkListItemWidget *self,
|
||||
GtkWidget *child);
|
||||
|
||||
guint gtk_list_item_widget_get_position (GtkListItemWidget *self);
|
||||
gpointer gtk_list_item_widget_get_item (GtkListItemWidget *self);
|
||||
gboolean gtk_list_item_widget_get_selected (GtkListItemWidget *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_WIDGET_PRIVATE_H__ */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user