Compare commits
59 Commits
css-variab
...
wip/baeder
Author | SHA1 | Date | |
---|---|---|---|
|
0496c8050c | ||
|
a92ed687e1 | ||
|
4e5a4f6ab4 | ||
|
b03496d404 | ||
|
d06ab5f367 | ||
|
718f66ada4 | ||
|
82736005d1 | ||
|
c84138c63f | ||
|
0a9ac877ba | ||
|
73625cb157 | ||
|
22dcdc073f | ||
|
c69499f98b | ||
|
13a6b3c64e | ||
|
0d2f70bd16 | ||
|
2158d97800 | ||
|
7ff18b7b5b | ||
|
ed19c3eeee | ||
|
c633d01e4f | ||
|
8b12e8acd4 | ||
|
1532dff5a8 | ||
|
7666d41a79 | ||
|
54bbccf0de | ||
|
5974aaa59b | ||
|
9cb49d23c8 | ||
|
2301a18987 | ||
|
7fbbdcb2af | ||
|
46165449ef | ||
|
19be08854f | ||
|
f2e20904f1 | ||
|
95e462e181 | ||
|
2a54786be8 | ||
|
9c55ac513c | ||
|
1f1f5e2aed | ||
|
18a03fa043 | ||
|
a0e4be18a0 | ||
|
30a471bc31 | ||
|
7346d69217 | ||
|
0a85576377 | ||
|
ca6aa9e549 | ||
|
97db41c8a0 | ||
|
41815d3d26 | ||
|
c60b4fd031 | ||
|
d227a713ed | ||
|
112c5aa1ff | ||
|
88f43cdff8 | ||
|
e90758a43f | ||
|
b2a6938805 | ||
|
bc61ad4bc4 | ||
|
f967055224 | ||
|
c387f76e64 | ||
|
c22a6d355f | ||
|
a6a93ba340 | ||
|
f32c010904 | ||
|
e72eb6c302 | ||
|
4d19b7d612 | ||
|
42b04251d5 | ||
|
a53afb694d | ||
|
477fa48557 | ||
|
389eb0de56 |
248
demos/gtk-demo/award.c
Normal file
248
demos/gtk-demo/award.c
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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");
|
||||
gtk_builder_connect_signals (builder, NULL);
|
||||
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">
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label" bind-source="GtkListItem" bind-property="position"></property>
|
||||
<property name="margin">6</property>
|
||||
</object>
|
||||
</child>
|
||||
</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>
|
79
demos/gtk-demo/awardview.c
Normal file
79
demos/gtk-demo/awardview.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/* 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;
|
||||
|
||||
#if 0
|
||||
static void
|
||||
create_listitem (GtkListItem *item, gpointer user_data)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
g_object_set (label, "margin", 6, NULL); /* omg, why do we need to do this?! */
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0);
|
||||
gtk_container_add (GTK_CONTAINER (item), label);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_listitem (GtkListItem *item, gpointer user_data)
|
||||
{
|
||||
GtkAward *award = gtk_list_item_get_item (item);
|
||||
|
||||
gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item))),
|
||||
gtk_award_get_title (award));
|
||||
}
|
||||
#endif
|
||||
|
||||
GtkWidget *
|
||||
do_awardview (GtkWidget *do_widget)
|
||||
{
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *sw, *listview;
|
||||
GListModel *list;
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
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_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_container_add (GTK_CONTAINER (window), sw);
|
||||
|
||||
#if 0
|
||||
listview = gtk_list_view_new ();
|
||||
gtk_list_view_set_functions (GTK_LIST_VIEW (listview),
|
||||
create_listitem,
|
||||
bind_listitem,
|
||||
NULL, NULL);
|
||||
#else
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_builder_list_item_factory_new_from_resource ("/awardview/awardlistitem.ui"));
|
||||
#endif
|
||||
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_container_add (GTK_CONTAINER (sw), listview);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
@@ -1,5 +1,8 @@
|
||||
<?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">appmenu.ui</file>
|
||||
@@ -9,6 +12,9 @@
|
||||
<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>
|
||||
@@ -111,6 +117,9 @@
|
||||
<file>gnome-fs-directory.png</file>
|
||||
<file>gnome-fs-regular.png</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>
|
||||
@@ -147,6 +156,7 @@
|
||||
</gresource>
|
||||
<gresource prefix="/sources">
|
||||
<file>application_demo.c</file>
|
||||
<file>awardview.c</file>
|
||||
<file>assistant.c</file>
|
||||
<file>builder.c</file>
|
||||
<file>changedisplay.c</file>
|
||||
@@ -189,6 +199,8 @@
|
||||
<file>infobar.c</file>
|
||||
<file>links.c</file>
|
||||
<file>listbox.c</file>
|
||||
<file>listview_settings.c</file>
|
||||
<file>listview_weather.c</file>
|
||||
<file>list_store.c</file>
|
||||
<file>markup.c</file>
|
||||
<file>menus.c</file>
|
||||
|
@@ -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
|
||||
{
|
||||
gchar *name;
|
||||
gchar *title;
|
||||
gchar *filename;
|
||||
char *name;
|
||||
char *title;
|
||||
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]:
|
||||
|
@@ -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);
|
||||
}
|
||||
|
147
demos/gtk-demo/listview_settings.c
Normal file
147
demos/gtk-demo/listview_settings.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/* Lists/Settings
|
||||
*
|
||||
* This demo shows a settings viwer for GSettings.
|
||||
*
|
||||
* It demonstrates how to implement support for trees with listview.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static int
|
||||
strvcmp (gconstpointer p1,
|
||||
gconstpointer p2)
|
||||
{
|
||||
const char * const *s1 = p1;
|
||||
const char * const *s2 = p2;
|
||||
|
||||
return strcmp (*s1, *s2);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return G_LIST_MODEL (result);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_widget (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *label, *expander;
|
||||
|
||||
expander = gtk_tree_expander_new ();
|
||||
gtk_container_add (GTK_CONTAINER (list_item), expander);
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
||||
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), label);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_widget (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GSettings *settings;
|
||||
GtkWidget *label, *expander;
|
||||
GSettingsSchema *schema;
|
||||
|
||||
expander = gtk_bin_get_child (GTK_BIN (list_item));
|
||||
gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (expander), gtk_list_item_get_item (list_item));
|
||||
label = gtk_tree_expander_get_child (GTK_TREE_EXPANDER (expander));
|
||||
settings = gtk_tree_expander_get_item (GTK_TREE_EXPANDER (expander));
|
||||
|
||||
g_object_get (settings, "settings-schema", &schema, NULL);
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (label), g_settings_schema_get_id (schema));
|
||||
|
||||
g_settings_schema_unref (schema);
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_settings (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkWidget *listview, *sw;;
|
||||
GListModel *model;
|
||||
GtkTreeListModel *treemodel;
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 600);
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Settings");
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK(gtk_widget_destroyed), &window);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_container_add (GTK_CONTAINER (window), sw);
|
||||
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_functions_list_item_factory_new (setup_widget,
|
||||
bind_widget,
|
||||
NULL, NULL));
|
||||
|
||||
model = create_settings_model (NULL, NULL);
|
||||
treemodel = gtk_tree_list_model_new (FALSE,
|
||||
model,
|
||||
TRUE,
|
||||
create_settings_model,
|
||||
NULL,
|
||||
NULL);
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (treemodel));
|
||||
g_object_unref (treemodel);
|
||||
g_object_unref (model);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (sw), listview);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
316
demos/gtk-demo/listview_weather.c
Normal file
316
demos/gtk-demo/listview_weather.c
Normal file
@@ -0,0 +1,316 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
static void
|
||||
gtk_weather_info_class_init (GtkWeatherInfoClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_weather_info_init (GtkWeatherInfo *self)
|
||||
{
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE (GtkWeatherInfo, gtk_weather_info, G_TYPE_OBJECT);
|
||||
|
||||
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_container_add (GTK_CONTAINER (list_item), box);
|
||||
|
||||
child = gtk_label_new (NULL);
|
||||
gtk_label_set_width_chars (GTK_LABEL (child), 5);
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
|
||||
child = gtk_image_new ();
|
||||
gtk_image_set_icon_size (GTK_IMAGE (child), GTK_ICON_SIZE_LARGE);
|
||||
gtk_container_add (GTK_CONTAINER (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_container_add (GTK_CONTAINER (box), child);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_widget (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *box, *child;
|
||||
GtkWeatherInfo *info;
|
||||
GDateTime *timestamp;
|
||||
char *s;
|
||||
|
||||
box = gtk_bin_get_child (GTK_BIN (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_TOPLEVEL);
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Weather");
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK(gtk_widget_destroyed), &window);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_container_add (GTK_CONTAINER (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_container_add (GTK_CONTAINER (sw), listview);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (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
@@ -8,6 +8,7 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "award.h"
|
||||
#include "demos.h"
|
||||
|
||||
static GtkWidget *info_view;
|
||||
@@ -15,10 +16,32 @@ static GtkWidget *source_view;
|
||||
|
||||
static gchar *current_file = NULL;
|
||||
|
||||
static GtkWidget *window;
|
||||
static GtkWidget *notebook;
|
||||
static GtkWidget *treeview;
|
||||
static GtkWidget *listview;
|
||||
static GtkSingleSelection *selection;
|
||||
static GtkWidget *headerbar;
|
||||
|
||||
typedef struct _GtkDemo GtkDemo;
|
||||
struct _GtkDemo
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
const char *name;
|
||||
const char *title;
|
||||
const char *filename;
|
||||
GDoDemoFunc func;
|
||||
GListModel *children_model;
|
||||
};
|
||||
|
||||
# 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 void gtk_demo_init (GtkDemo *self) {}
|
||||
static void gtk_demo_class_init (GtkDemoClass *klass) {}
|
||||
|
||||
enum {
|
||||
NAME_COLUMN,
|
||||
TITLE_COLUMN,
|
||||
@@ -111,69 +134,7 @@ 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
|
||||
@@ -181,14 +142,19 @@ activate_run (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data)
|
||||
{
|
||||
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);
|
||||
GtkWidget *result;
|
||||
|
||||
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);
|
||||
if (!demo->func)
|
||||
return;
|
||||
|
||||
result = demo->func(window);
|
||||
if (result != NULL && GTK_IS_WINDOW (result))
|
||||
{
|
||||
gtk_window_set_transient_for (GTK_WINDOW (result), GTK_WINDOW (window));
|
||||
gtk_window_set_modal (GTK_WINDOW (result), TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Stupid syntax highlighting.
|
||||
@@ -888,81 +854,74 @@ load_file (const gchar *demoname,
|
||||
}
|
||||
|
||||
static void
|
||||
selection_cb (GtkTreeSelection *selection,
|
||||
GtkTreeModel *model)
|
||||
|
||||
selection_cb (GtkSingleSelection *selection,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
char *name;
|
||||
char *filename;
|
||||
char *title;
|
||||
GtkTreeListRow *row = gtk_single_selection_get_selected_item (selection);
|
||||
GtkDemo *demo = gtk_tree_list_row_get_item (row);
|
||||
|
||||
if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
|
||||
return;
|
||||
if (demo->filename)
|
||||
load_file (demo->name, demo->filename);
|
||||
|
||||
gtk_tree_model_get (model, &iter,
|
||||
NAME_COLUMN, &name,
|
||||
TITLE_COLUMN, &title,
|
||||
FILENAME_COLUMN, &filename,
|
||||
-1);
|
||||
|
||||
if (filename)
|
||||
load_file (name, filename);
|
||||
|
||||
gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), title);
|
||||
|
||||
g_free (name);
|
||||
g_free (title);
|
||||
g_free (filename);
|
||||
gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), demo->title);
|
||||
}
|
||||
|
||||
static void
|
||||
populate_model (GtkTreeModel *model)
|
||||
|
||||
|
||||
static GListModel *
|
||||
create_demo_model (void)
|
||||
{
|
||||
Demo *d = gtk_demos;
|
||||
GListStore *store = g_list_store_new (GTK_TYPE_DEMO);
|
||||
DemoData *demo = gtk_demos;
|
||||
|
||||
/* this code only supports 1 level of children. If we
|
||||
* want more we probably have to use a recursing function.
|
||||
*/
|
||||
while (d->title)
|
||||
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 GListModel *
|
||||
get_child_model (gpointer item,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkDemo *demo = item;
|
||||
|
||||
if (demo->children_model)
|
||||
return g_object_ref (G_LIST_MODEL (demo->children_model));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -982,22 +941,6 @@ startup (GApplication *app)
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
static void
|
||||
row_activated_cb (GtkWidget *tree_view,
|
||||
GtkTreePath *path,
|
||||
GtkTreeViewColumn *column)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkWidget *window;
|
||||
GtkTreeModel *model;
|
||||
|
||||
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);
|
||||
|
||||
run_example_for_row (window, model, &iter);
|
||||
}
|
||||
|
||||
static void
|
||||
start_cb (GtkMenuItem *item, GtkWidget *scrollbar)
|
||||
{
|
||||
@@ -1024,19 +967,50 @@ scrollbar_popup (GtkWidget *scrollbar, GtkWidget *menu)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_demo_row_func (GtkListItem *list_item,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkWidget *label;
|
||||
GtkWidget *expander;
|
||||
|
||||
expander = gtk_tree_expander_new ();
|
||||
|
||||
label = gtk_label_new ("");
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
|
||||
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), label);
|
||||
gtk_container_add (GTK_CONTAINER (list_item), expander);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_demo_row_func (GtkListItem *list_item,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkWidget *label, *expander;
|
||||
GtkTreeListRow *row;
|
||||
GtkDemo *demo;
|
||||
|
||||
row = gtk_list_item_get_item (list_item);
|
||||
demo = gtk_tree_list_row_get_item (row);
|
||||
expander = gtk_bin_get_child (GTK_BIN (list_item));
|
||||
gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (expander), row);
|
||||
label = gtk_tree_expander_get_child (GTK_TREE_EXPANDER (expander));
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (label), demo->title);
|
||||
}
|
||||
|
||||
static void
|
||||
activate (GApplication *app)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
GtkWindow *window;
|
||||
GtkWidget *widget;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
GError *error = NULL;
|
||||
GtkWidget *sw;
|
||||
GtkWidget *scrollbar;
|
||||
GtkWidget *menu;
|
||||
GtkWidget *item;
|
||||
GListModel *listmodel;
|
||||
GtkTreeListModel *treemodel;
|
||||
|
||||
static GActionEntry win_entries[] = {
|
||||
{ "run", activate_run, NULL, NULL, NULL }
|
||||
@@ -1050,8 +1024,8 @@ activate (GApplication *app)
|
||||
exit (1);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1061,8 +1035,7 @@ activate (GApplication *app)
|
||||
info_view = (GtkWidget *)gtk_builder_get_object (builder, "info-textview");
|
||||
source_view = (GtkWidget *)gtk_builder_get_object (builder, "source-textview");
|
||||
headerbar = (GtkWidget *)gtk_builder_get_object (builder, "headerbar");
|
||||
treeview = (GtkWidget *)gtk_builder_get_object (builder, "treeview");
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
|
||||
listview = (GtkWidget *)gtk_builder_get_object (builder, "listview");
|
||||
|
||||
sw = (GtkWidget *)gtk_builder_get_object (builder, "source-scrolledwindow");
|
||||
scrollbar = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (sw));
|
||||
@@ -1081,17 +1054,25 @@ activate (GApplication *app)
|
||||
|
||||
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);
|
||||
|
||||
g_signal_connect (treeview, "row-activated", G_CALLBACK (row_activated_cb), model);
|
||||
gtk_list_view_set_factory (GTK_LIST_VIEW (listview),
|
||||
gtk_functions_list_item_factory_new (setup_demo_row_func,
|
||||
bind_demo_row_func,
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
@@ -1108,7 +1089,7 @@ auto_quit (gpointer data)
|
||||
static void
|
||||
list_demos (void)
|
||||
{
|
||||
Demo *d, *c;
|
||||
DemoData *d, *c;
|
||||
|
||||
d = gtk_demos;
|
||||
|
||||
@@ -1135,7 +1116,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;
|
||||
|
||||
|
@@ -66,32 +66,9 @@
|
||||
<property name="can-focus">1</property>
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="min-content-width">150</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkTreeView" id="treeview">
|
||||
<property name="can-focus">1</property>
|
||||
<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>
|
||||
</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>
|
||||
<object class="GtkListView" id="listview">
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@@ -3,6 +3,7 @@
|
||||
demos = files([
|
||||
'application_demo.c',
|
||||
'assistant.c',
|
||||
'awardview.c',
|
||||
'builder.c',
|
||||
'changedisplay.c',
|
||||
'clipboard.c',
|
||||
@@ -43,6 +44,8 @@ demos = files([
|
||||
'listbox.c',
|
||||
'flowbox.c',
|
||||
'list_store.c',
|
||||
'listview_settings.c',
|
||||
'listview_weather.c',
|
||||
'markup.c',
|
||||
'menus.c',
|
||||
'modelbutton.c',
|
||||
@@ -85,6 +88,7 @@ demos = files([
|
||||
gtkdemo_deps = [ libgtk_dep, ]
|
||||
|
||||
extra_demo_sources = files(['main.c',
|
||||
'award.c',
|
||||
'gtkfishbowl.c',
|
||||
'fontplane.c',
|
||||
'gtkgears.c',
|
||||
|
@@ -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 *button,
|
||||
GtkWidget *window)
|
||||
{
|
||||
award ("password-correct");
|
||||
gtk_widget_destroy (window);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
@@ -72,7 +86,7 @@ do_password_entry (GtkWidget *do_widget)
|
||||
|
||||
button = gtk_button_new_with_mnemonic ("_Done");
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action");
|
||||
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_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;
|
||||
}
|
||||
|
||||
@@ -401,6 +412,18 @@ add_choice (GtkWidget *choices,
|
||||
gtk_container_add (GTK_CONTAINER (choices), icon);
|
||||
}
|
||||
|
||||
static void
|
||||
widget_destroyed (GtkWidget *widget,
|
||||
GtkWidget **widget_pointer)
|
||||
{
|
||||
if (widget_pointer)
|
||||
*widget_pointer = NULL;
|
||||
|
||||
if (!solved)
|
||||
award ("puzzle-give-up");
|
||||
}
|
||||
|
||||
|
||||
GtkWidget *
|
||||
do_sliding_puzzle (GtkWidget *do_widget)
|
||||
{
|
||||
@@ -469,7 +492,7 @@ do_sliding_puzzle (GtkWidget *do_widget)
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), header);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
G_CALLBACK (widget_destroyed), &window);
|
||||
|
||||
frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, (float) gdk_paintable_get_intrinsic_aspect_ratio (puzzle), FALSE);
|
||||
gtk_container_add (GTK_CONTAINER (window), frame);
|
||||
|
@@ -53,6 +53,7 @@
|
||||
<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="Application">
|
||||
@@ -87,6 +88,9 @@
|
||||
<xi:include href="xml/gtkrevealer.xml" />
|
||||
<xi:include href="xml/gtklistbox.xml" />
|
||||
<xi:include href="xml/gtkflowbox.xml" />
|
||||
<xi:include href="xml/gtklistview.xml" />
|
||||
<xi:include href="xml/gtkgridview.xml" />
|
||||
<xi:include href="xml/gtktreeexpander.xml" />
|
||||
<xi:include href="xml/gtkstack.xml" />
|
||||
<xi:include href="xml/gtkstackswitcher.xml" />
|
||||
<xi:include href="xml/gtkstacksidebar.xml" />
|
||||
|
@@ -493,6 +493,71 @@ 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_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>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
|
||||
<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>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
|
||||
<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
|
||||
@@ -1322,6 +1387,32 @@ 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>gtkspinbutton</FILE>
|
||||
<SECTION>
|
||||
<FILE>gtkfilterlistmodel</FILE>
|
||||
<TITLE>GtkFilterListModel</TITLE>
|
||||
@@ -3464,6 +3555,26 @@ gtk_tree_list_model_get_type
|
||||
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>
|
||||
|
@@ -45,9 +45,6 @@ typedef struct _GdkWaylandSurfaceClass GdkWaylandSurfaceClass;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gdk_wayland_surface_get_type (void);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GdkSurface * gdk_wayland_surface_new_subsurface (GdkDisplay *display,
|
||||
const GdkRectangle *position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
struct wl_surface *gdk_wayland_surface_get_wl_surface (GdkSurface *surface);
|
||||
|
||||
|
@@ -55,6 +55,7 @@
|
||||
#include <gtk/gtkbox.h>
|
||||
#include <gtk/gtkbuildable.h>
|
||||
#include <gtk/gtkbuilder.h>
|
||||
#include <gtk/gtkbuilderlistitemfactory.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkcalendar.h>
|
||||
#include <gtk/gtkcellarea.h>
|
||||
@@ -90,6 +91,7 @@
|
||||
#include <gtk/gtkcustomlayout.h>
|
||||
#include <gtk/gtkdebug.h>
|
||||
#include <gtk/gtkdialog.h>
|
||||
#include <gtk/gtkdirectorylist.h>
|
||||
#include <gtk/gtkdnd.h>
|
||||
#include <gtk/gtkdragdest.h>
|
||||
#include <gtk/gtkdragsource.h>
|
||||
@@ -121,6 +123,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 +137,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>
|
||||
@@ -148,7 +152,10 @@
|
||||
#include <gtk/gtklevelbar.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>
|
||||
@@ -243,6 +250,7 @@
|
||||
#include <gtk/gtktooltip.h>
|
||||
#include <gtk/gtktestutils.h>
|
||||
#include <gtk/gtktreednd.h>
|
||||
#include <gtk/gtktreeexpander.h>
|
||||
#include <gtk/gtktreelistmodel.h>
|
||||
#include <gtk/gtktreemodel.h>
|
||||
#include <gtk/gtktreemodelfilter.h>
|
||||
|
137
gtk/gtkbuilderlistitemfactory.c
Normal file
137
gtk/gtkbuilderlistitemfactory.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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 "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
struct _GtkBuilderListItemFactory
|
||||
{
|
||||
GtkListItemFactory parent_instance;
|
||||
|
||||
GBytes *bytes;
|
||||
};
|
||||
|
||||
struct _GtkBuilderListItemFactoryClass
|
||||
{
|
||||
GtkListItemFactoryClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkBuilderListItemFactory, gtk_builder_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_setup (GtkListItemFactory *factory,
|
||||
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, list_item);
|
||||
|
||||
builder = gtk_builder_new ();
|
||||
|
||||
if (!gtk_builder_extend_with_template (builder, GTK_WIDGET (list_item), G_OBJECT_TYPE (list_item),
|
||||
(const gchar *)g_bytes_get_data (self->bytes, NULL),
|
||||
g_bytes_get_size (self->bytes),
|
||||
&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;
|
||||
}
|
||||
|
||||
gtk_builder_connect_signals (builder, list_item);
|
||||
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_finalize (GObject *object)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
|
||||
|
||||
g_bytes_unref (self->bytes);
|
||||
|
||||
G_OBJECT_CLASS (gtk_builder_list_item_factory_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_class_init (GtkBuilderListItemFactoryClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
|
||||
|
||||
object_class->finalize = gtk_builder_list_item_factory_finalize;
|
||||
|
||||
factory_class->setup = gtk_builder_list_item_factory_setup;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_init (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkListItemFactory *
|
||||
gtk_builder_list_item_factory_new_from_bytes (GBytes *bytes)
|
||||
{
|
||||
GtkBuilderListItemFactory *self;
|
||||
|
||||
g_return_val_if_fail (bytes != NULL, NULL);
|
||||
|
||||
self = g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, NULL);
|
||||
|
||||
self->bytes = g_bytes_ref (bytes);
|
||||
|
||||
return GTK_LIST_ITEM_FACTORY (self);
|
||||
}
|
||||
|
||||
GtkListItemFactory *
|
||||
gtk_builder_list_item_factory_new_from_resource (const char *resource_path)
|
||||
{
|
||||
GtkListItemFactory *result;
|
||||
GError *error = NULL;
|
||||
GBytes *bytes;
|
||||
|
||||
g_return_val_if_fail (resource_path != NULL, NULL);
|
||||
|
||||
bytes = g_resources_lookup_data (resource_path, 0, &error);
|
||||
if (!bytes)
|
||||
{
|
||||
g_critical ("Unable to load resource for list item template: %s", error->message);
|
||||
g_error_free (error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = gtk_builder_list_item_factory_new_from_bytes (bytes);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
52
gtk/gtkbuilderlistitemfactory.h
Normal file
52
gtk/gtkbuilderlistitemfactory.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 (GBytes *bytes);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory * gtk_builder_list_item_factory_new_from_resource (const char *resource_path);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_BUILDER_LIST_ITEM_FACTORY_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__ */
|
135
gtk/gtkfunctionslistitemfactory.c
Normal file
135
gtk/gtkfunctionslistitemfactory.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkFunctionsListItemFactory *self = GTK_FUNCTIONS_LIST_ITEM_FACTORY (factory);
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_functions_list_item_factory_parent_class)->setup (factory, list_item);
|
||||
|
||||
if (self->setup_func)
|
||||
self->setup_func (list_item, self->user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_bind (GtkListItemFactory *factory,
|
||||
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)->bind (factory, list_item, position, item, selected);
|
||||
|
||||
if (self->bind_func)
|
||||
self->bind_func (list_item, self->user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_rebind (GtkListItemFactory *factory,
|
||||
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)->bind (factory, list_item, position, item, selected);
|
||||
|
||||
if (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->bind = gtk_functions_list_item_factory_bind;
|
||||
factory_class->rebind = gtk_functions_list_item_factory_rebind;
|
||||
}
|
||||
|
||||
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__ */
|
1129
gtk/gtkgridview.c
Normal file
1129
gtk/gtkgridview.c
Normal file
File diff suppressed because it is too large
Load Diff
66
gtk/gtkgridview.h
Normal file
66
gtk/gtkgridview.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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/gtkwidget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_GRID_VIEW (gtk_grid_view_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkGridView, gtk_grid_view, GTK, GRID_VIEW, GtkWidget)
|
||||
|
||||
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);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_GRID_VIEW_H__ */
|
512
gtk/gtklistitem.c
Normal file
512
gtk/gtklistitem.c
Normal file
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
* 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 "gtkcssnodeprivate.h"
|
||||
#include "gtkeventcontrollerkey.h"
|
||||
#include "gtkgestureclick.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtklistitem
|
||||
* @title: GtkListItem
|
||||
* @short_description: Widget used to represent items of a ListModel
|
||||
* @see_also: #GtkListView, #GListModel
|
||||
*
|
||||
* #GtkListItem is the widget that GTK list-handling containers such
|
||||
* as #GtkListView create to represent items in a #GListModel.
|
||||
* They are managed by the container and cannot be created by application
|
||||
* code.
|
||||
*
|
||||
* #GtkListIems are container widgets that need to be populated by
|
||||
* application code. The container provides functions to do that.
|
||||
*
|
||||
* #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 _GtkListItem
|
||||
{
|
||||
GtkBin parent_instance;
|
||||
|
||||
GObject *item;
|
||||
guint position;
|
||||
|
||||
guint selectable : 1;
|
||||
guint selected : 1;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_ITEM,
|
||||
PROP_POSITION,
|
||||
PROP_SELECTABLE,
|
||||
PROP_SELECTED,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkListItem, gtk_list_item, GTK_TYPE_BIN)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static gboolean
|
||||
gtk_list_item_focus (GtkWidget *widget,
|
||||
GtkDirectionType direction)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (widget);
|
||||
GtkWidget *child;
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
child = gtk_bin_get_child (GTK_BIN (self));
|
||||
|
||||
if (child)
|
||||
{
|
||||
if (gtk_widget_get_focus_child (widget))
|
||||
return FALSE;
|
||||
if (gtk_widget_child_focus (child, direction))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_can_focus (widget) ||
|
||||
!self->selectable)
|
||||
return FALSE;
|
||||
|
||||
gtk_widget_grab_focus (widget);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_dispose (GObject *object)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (object);
|
||||
|
||||
g_assert (self->item == NULL);
|
||||
|
||||
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_ITEM:
|
||||
g_value_set_object (value, self->item);
|
||||
break;
|
||||
|
||||
case PROP_POSITION:
|
||||
g_value_set_uint (value, self->position);
|
||||
break;
|
||||
|
||||
case PROP_SELECTABLE:
|
||||
g_value_set_boolean (value, self->selectable);
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
g_value_set_boolean (value, self->selected);
|
||||
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_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)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
widget_class->focus = gtk_list_item_focus;
|
||||
|
||||
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: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 in the item
|
||||
*/
|
||||
properties[PROP_POSITION] =
|
||||
g_param_spec_uint ("position",
|
||||
P_("Position"),
|
||||
P_("Position of the item"),
|
||||
0, G_MAXUINT, 0,
|
||||
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);
|
||||
|
||||
/* This gets overwritten by gtk_list_item_new() but better safe than sorry */
|
||||
gtk_widget_class_set_css_name (widget_class, I_("row"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_click_gesture_pressed (GtkGestureClick *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkListItem *self)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
GdkModifierType state;
|
||||
GdkModifierType mask;
|
||||
gboolean extend = FALSE, modify = FALSE;
|
||||
|
||||
if (!self->selectable)
|
||||
{
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gtk_get_current_event_state (&state))
|
||||
{
|
||||
mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION);
|
||||
if ((state & mask) == mask)
|
||||
modify = TRUE;
|
||||
mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION);
|
||||
if ((state & mask) == mask)
|
||||
extend = TRUE;
|
||||
}
|
||||
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.select-item",
|
||||
"(ubb)",
|
||||
self->position, modify, extend);
|
||||
|
||||
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_focus_changed_cb (GtkEventControllerKey *controller,
|
||||
GParamSpec *psepc,
|
||||
GtkListItem *self)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
|
||||
if (gtk_event_controller_key_contains_focus (controller))
|
||||
{
|
||||
gtk_widget_activate_action (widget,
|
||||
"list.scroll-to-item",
|
||||
"u",
|
||||
self->position);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_click_gesture_released (GtkGestureClick *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkListItem *self)
|
||||
{
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_click_gesture_canceled (GtkGestureClick *gesture,
|
||||
GdkEventSequence *sequence,
|
||||
GtkListItem *self)
|
||||
{
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_init (GtkListItem *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
GtkGesture *gesture;
|
||||
|
||||
self->selectable = TRUE;
|
||||
gtk_widget_set_can_focus (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_click_gesture_pressed), self);
|
||||
g_signal_connect (gesture, "released",
|
||||
G_CALLBACK (gtk_list_item_click_gesture_released), self);
|
||||
g_signal_connect (gesture, "cancel",
|
||||
G_CALLBACK (gtk_list_item_click_gesture_canceled), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
|
||||
|
||||
controller = gtk_event_controller_key_new ();
|
||||
g_signal_connect (controller, "notify::contains-focus", G_CALLBACK (gtk_list_item_focus_changed_cb), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
}
|
||||
|
||||
GtkListItem *
|
||||
gtk_list_item_new (const char *css_name)
|
||||
{
|
||||
GtkListItem *result;
|
||||
|
||||
g_return_val_if_fail (css_name != NULL, NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_LIST_ITEM,
|
||||
"css-name", css_name,
|
||||
NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
return self->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), 0);
|
||||
|
||||
return self->position;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_set_item (GtkListItem *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
g_return_if_fail (item == NULL || G_IS_OBJECT (item));
|
||||
|
||||
if (self->item == item)
|
||||
return;
|
||||
|
||||
g_clear_object (&self->item);
|
||||
if (item)
|
||||
self->item = g_object_ref (item);
|
||||
|
||||
gtk_css_node_invalidate (gtk_widget_get_css_node (GTK_WIDGET (self)), GTK_CSS_CHANGE_ANIMATIONS);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_set_position (GtkListItem *self,
|
||||
guint position)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
|
||||
if (self->position == position)
|
||||
return;
|
||||
|
||||
self->position = position;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_POSITION]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
return self->selected;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_set_selected (GtkListItem *self,
|
||||
gboolean selected)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
|
||||
if (self->selected == selected)
|
||||
return;
|
||||
|
||||
self->selected = 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);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
}
|
51
gtk/gtklistitem.h
Normal file
51
gtk/gtklistitem.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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/gtkbin.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkListItem, gtk_list_item, GTK, LIST_ITEM, GtkBin)
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM (gtk_list_item_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gpointer gtk_list_item_get_item (GtkListItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_list_item_get_position (GtkListItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_selected (GtkListItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_selectable (GtkListItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_item_set_selectable (GtkListItem *self,
|
||||
gboolean selectable);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_H__ */
|
187
gtk/gtklistitemfactory.c
Normal file
187
gtk/gtklistitemfactory.c
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
G_DEFINE_TYPE (GtkListItemFactory, gtk_list_item_factory, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_setup (GtkListItemFactory *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_teardown (GtkListItemFactory *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *child;
|
||||
|
||||
child = gtk_bin_get_child (GTK_BIN (list_item));
|
||||
if (child)
|
||||
gtk_container_remove (GTK_CONTAINER (list_item), child);
|
||||
|
||||
gtk_list_item_set_selectable (list_item, TRUE);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_bind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
gtk_list_item_set_item (list_item, item);
|
||||
gtk_list_item_set_position (list_item, position);
|
||||
gtk_list_item_set_selected (list_item, selected);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_rebind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
gtk_list_item_set_item (list_item, item);
|
||||
gtk_list_item_set_position (list_item, position);
|
||||
gtk_list_item_set_selected (list_item, selected);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_update (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gboolean selected)
|
||||
{
|
||||
gtk_list_item_set_position (list_item, position);
|
||||
gtk_list_item_set_selected (list_item, selected);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_unbind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
gtk_list_item_set_item (list_item, NULL);
|
||||
gtk_list_item_set_position (list_item, 0);
|
||||
gtk_list_item_set_selected (list_item, FALSE);
|
||||
}
|
||||
|
||||
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->bind = gtk_list_item_factory_default_bind;
|
||||
klass->rebind = gtk_list_item_factory_default_rebind;
|
||||
klass->update = gtk_list_item_factory_default_update;
|
||||
klass->unbind = gtk_list_item_factory_default_unbind;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_init (GtkListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_setup (GtkListItemFactory *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->setup (self, list_item);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_teardown (GtkListItemFactory *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->teardown (self, list_item);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_bind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (list_item));
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->bind (self, list_item, position, item, selected);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (list_item));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_rebind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (list_item));
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->rebind (self, list_item, position, item, selected);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (list_item));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_update (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gboolean selected)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (list_item));
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->update (self, list_item, position, selected);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (list_item));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_unbind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (list_item));
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->unbind (self, list_item);
|
||||
|
||||
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__ */
|
93
gtk/gtklistitemfactoryprivate.h
Normal file
93
gtk/gtklistitemfactoryprivate.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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/gtklistitemfactory.h>
|
||||
#include <gtk/gtklistview.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,
|
||||
GtkListItem *list_item);
|
||||
/* undo the effects of GtkListItemFactoryClass::setup() */
|
||||
void (* teardown) (GtkListItemFactory *self,
|
||||
GtkListItem *list_item);
|
||||
|
||||
/* bind @list_item to the given @item, which is in @position and @selected state */
|
||||
void (* bind) (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
/* unbind the current item and bind a new one */
|
||||
void (* rebind) (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
/* like GtkListItemFactoryClass::rebind(), but the item didn't change */
|
||||
void (* update) (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gboolean selected);
|
||||
/* undo the effects of GtkListItemFactoryClass::bind() */
|
||||
void (* unbind) (GtkListItemFactory *self,
|
||||
GtkListItem *list_item);
|
||||
};
|
||||
|
||||
void gtk_list_item_factory_setup (GtkListItemFactory *self,
|
||||
GtkListItem *list_item);
|
||||
void gtk_list_item_factory_teardown (GtkListItemFactory *self,
|
||||
GtkListItem *list_item);
|
||||
|
||||
void gtk_list_item_factory_bind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
void gtk_list_item_factory_rebind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
void gtk_list_item_factory_update (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gboolean selected);
|
||||
void gtk_list_item_factory_unbind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_FACTORY_PRIVATE_H__ */
|
1136
gtk/gtklistitemmanager.c
Normal file
1136
gtk/gtklistitemmanager.c
Normal file
File diff suppressed because it is too large
Load Diff
105
gtk/gtklistitemmanagerprivate.h
Normal file
105
gtk/gtklistitemmanagerprivate.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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/gtklistitemfactoryprivate.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);
|
||||
|
||||
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__ */
|
40
gtk/gtklistitemprivate.h
Normal file
40
gtk/gtklistitemprivate.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 "gtklistitemmanagerprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GtkListItem * gtk_list_item_new (const char *css_name);
|
||||
|
||||
void gtk_list_item_set_item (GtkListItem *self,
|
||||
gpointer item);
|
||||
void gtk_list_item_set_position (GtkListItem *self,
|
||||
guint position);
|
||||
void gtk_list_item_set_selected (GtkListItem *self,
|
||||
gboolean selected);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_PRIVATE_H__ */
|
@@ -206,15 +206,12 @@ gtk_list_list_model_new_with_size (GType item_type,
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_list_model_item_added (GtkListListModel *self,
|
||||
gpointer item)
|
||||
static guint
|
||||
gtk_list_list_model_find (GtkListListModel *self,
|
||||
gpointer item)
|
||||
{
|
||||
gpointer x;
|
||||
guint position;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
g_return_if_fail (item != NULL);
|
||||
gpointer x;
|
||||
|
||||
position = 0;
|
||||
for (x = self->get_first (self->data);
|
||||
@@ -222,7 +219,17 @@ gtk_list_list_model_item_added (GtkListListModel *self,
|
||||
x = self->get_next (x, self->data))
|
||||
position++;
|
||||
|
||||
gtk_list_list_model_item_added_at (self, position);
|
||||
return position;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_list_model_item_added (GtkListListModel *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
g_return_if_fail (item != NULL);
|
||||
|
||||
gtk_list_list_model_item_added_at (self, gtk_list_list_model_find (self, item));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -241,26 +248,49 @@ void
|
||||
gtk_list_list_model_item_removed (GtkListListModel *self,
|
||||
gpointer previous)
|
||||
{
|
||||
gpointer x;
|
||||
guint position;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
|
||||
if (previous == NULL)
|
||||
position = 0;
|
||||
else
|
||||
position = 1 + gtk_list_list_model_find (self, previous);
|
||||
|
||||
gtk_list_list_model_item_removed_at (self, position);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_list_model_item_moved (GtkListListModel *self,
|
||||
gpointer item,
|
||||
gpointer previous_previous)
|
||||
{
|
||||
guint position, previous_position;
|
||||
guint min, max;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
g_return_if_fail (item != previous_previous);
|
||||
|
||||
position = gtk_list_list_model_find (self, item);
|
||||
|
||||
if (previous_previous == NULL)
|
||||
{
|
||||
position = 0;
|
||||
previous_position = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = 1;
|
||||
|
||||
for (x = self->get_first (self->data);
|
||||
x != previous;
|
||||
x = self->get_next (x, self->data))
|
||||
position++;
|
||||
previous_position = gtk_list_list_model_find (self, previous_previous);
|
||||
if (position > previous_position)
|
||||
previous_position++;
|
||||
}
|
||||
|
||||
gtk_list_list_model_item_removed_at (self, position);
|
||||
/* item didn't move */
|
||||
if (position == previous_position)
|
||||
return;
|
||||
|
||||
min = MIN (position, previous_position);
|
||||
max = MAX (position, previous_position) + 1;
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), min, max - min, max - min);
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -64,6 +64,9 @@ void gtk_list_list_model_item_removed (GtkListListMode
|
||||
gpointer previous);
|
||||
void gtk_list_list_model_item_removed_at (GtkListListModel *self,
|
||||
guint position);
|
||||
void gtk_list_list_model_item_moved (GtkListListModel *self,
|
||||
gpointer item,
|
||||
gpointer previous_previous);
|
||||
|
||||
void gtk_list_list_model_clear (GtkListListModel *self);
|
||||
|
||||
|
1219
gtk/gtklistview.c
Normal file
1219
gtk/gtklistview.c
Normal file
File diff suppressed because it is too large
Load Diff
62
gtk/gtklistview.h
Normal file
62
gtk/gtklistview.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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_VIEW_H__
|
||||
#define __GTK_LIST_VIEW_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include <gtk/gtklistitem.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_VIEW (gtk_list_view_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkListView, gtk_list_view, GTK, LIST_VIEW, GtkWidget)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_list_view_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_list_view_new_with_factory (GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_list_view_get_model (GtkListView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_view_set_model (GtkListView *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_view_set_factory (GtkListView *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory *
|
||||
gtk_list_view_get_factory (GtkListView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_view_set_show_separators (GtkListView *self,
|
||||
gboolean show_separators);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_view_get_show_separators (GtkListView *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_VIEW_H__ */
|
@@ -685,13 +685,6 @@ gtk_popover_unrealize (GtkWidget *widget)
|
||||
g_clear_object (&priv->surface);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_popover_move_focus (GtkWidget *widget,
|
||||
GtkDirectionType direction)
|
||||
{
|
||||
g_signal_emit_by_name (gtk_widget_get_root (widget), "move-focus", direction);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_popover_show (GtkWidget *widget)
|
||||
{
|
||||
@@ -1374,7 +1367,6 @@ gtk_popover_class_init (GtkPopoverClass *klass)
|
||||
widget_class->measure = gtk_popover_measure;
|
||||
widget_class->size_allocate = gtk_popover_size_allocate;
|
||||
widget_class->snapshot = gtk_popover_snapshot;
|
||||
widget_class->move_focus = gtk_popover_move_focus;
|
||||
|
||||
container_class->add = gtk_popover_add;
|
||||
container_class->remove = gtk_popover_remove;
|
||||
|
@@ -33,6 +33,18 @@ G_BEGIN_DECLS
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_INTERFACE (GtkSelectionModel, gtk_selection_model, GTK, SELECTION_MODEL, GListModel)
|
||||
|
||||
/**
|
||||
* GTK_INVALID_LIST_POSITION:
|
||||
*
|
||||
* The value used to refer to a guaranteed invalid position in a #GListModel. This
|
||||
* value may be returned from some functions, others may accept it as input.
|
||||
* Its interpretion may differ for different functions.
|
||||
*
|
||||
* Refer to each function's documentation for if this value is allowed and what it
|
||||
* does.
|
||||
*/
|
||||
#define GTK_INVALID_LIST_POSITION (G_MAXUINT)
|
||||
|
||||
/**
|
||||
* GtkSelectionModelInterface:
|
||||
* @is_selected: Return if the item at the given position is selected.
|
||||
|
@@ -26,18 +26,6 @@ G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_SINGLE_SELECTION (gtk_single_selection_get_type ())
|
||||
|
||||
/**
|
||||
* GTK_INVALID_LIST_POSITION:
|
||||
*
|
||||
* The value used to refer to a guaranteed invalid position in a #GListModel. This
|
||||
* value may be returned from some functions, others may accept it as input.
|
||||
* Its interpretion may differ for different functions.
|
||||
*
|
||||
* Refer to each function's documentation for if this value is allowed and what it
|
||||
* does.
|
||||
*/
|
||||
#define GTK_INVALID_LIST_POSITION (G_MAXUINT)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkSingleSelection, gtk_single_selection, GTK, SINGLE_SELECTION, GObject)
|
||||
|
||||
|
@@ -1632,6 +1632,7 @@ gtk_text_view_init (GtkTextView *text_view)
|
||||
priv->indent = 0;
|
||||
priv->tabs = NULL;
|
||||
priv->editable = TRUE;
|
||||
priv->cursor_alpha = 1.0;
|
||||
|
||||
priv->scroll_after_paste = FALSE;
|
||||
|
||||
|
682
gtk/gtktreeexpander.c
Normal file
682
gtk/gtktreeexpander.c
Normal file
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
* 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 "gtktreeexpander.h"
|
||||
|
||||
#include "gtkbindings.h"
|
||||
#include "gtkboxlayout.h"
|
||||
#include "gtkgestureclick.h"
|
||||
#include "gtkiconprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtktreelistmodel.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtktreeexpander
|
||||
* @title: GtkTreeExpander
|
||||
* @short_description: An indenting expander button for use in a tree list
|
||||
* @see_also: #GtkTreeListModel
|
||||
*
|
||||
* GtkTreeExpander is a widget that provides an expander for a list.
|
||||
*
|
||||
* It is typically placed as a bottommost child into a #GtkListView to allow
|
||||
* users to expand and collapse children in a list with a #GtkTreeListModel.
|
||||
* It will provide the common UI elements, gestures and keybindings for this
|
||||
* purpose.
|
||||
*
|
||||
* On top of this, the "listitem.expand", "listitem.collapse" and
|
||||
* "listitem.toggle-expand" actions are provided to allow adding custom UI
|
||||
* for managing expanded state.
|
||||
*
|
||||
* The #GtkTreeListModel must be set to not be passthrough. Then it will provide
|
||||
* #GtkTreeListRow items which can be set via gtk_tree_expander_set_list_row()
|
||||
* on the expander. The expander will then watch that row item automatically.
|
||||
* gtk_tree_expander_set_child() sets the widget that displays the actual row
|
||||
* contents.
|
||||
*
|
||||
* # CSS nodes
|
||||
*
|
||||
* |[<!-- language="plain" -->
|
||||
* treeexpander
|
||||
* ├── [indent]*
|
||||
* ├── [expander]
|
||||
* ╰── <child>
|
||||
* ]|
|
||||
*
|
||||
* GtkTreeExpander has zero or one CSS nodes with the name "expander" that should
|
||||
* display the expander icon. The node will be `:checked` when it is expanded.
|
||||
* If the node is not expandable, an "indent" node will be displayed instead.
|
||||
*
|
||||
* For every level of depth, another "indent" node is prepended.
|
||||
*/
|
||||
|
||||
struct _GtkTreeExpander
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GtkTreeListRow *list_row;
|
||||
GtkWidget *child;
|
||||
|
||||
GtkWidget *expander;
|
||||
guint notify_handler;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_CHILD,
|
||||
PROP_ITEM,
|
||||
PROP_LIST_ROW,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkTreeExpander, gtk_tree_expander, GTK_TYPE_WIDGET)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_tree_expander_click_gesture_pressed (GtkGestureClick *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
|
||||
|
||||
gtk_widget_activate_action (widget, "listitem.toggle-expand", NULL);
|
||||
|
||||
gtk_widget_set_state_flags (widget,
|
||||
GTK_STATE_FLAG_ACTIVE,
|
||||
FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_click_gesture_released (GtkGestureClick *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
gpointer unused)
|
||||
{
|
||||
gtk_widget_unset_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)),
|
||||
GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_click_gesture_canceled (GtkGestureClick *gesture,
|
||||
GdkEventSequence *sequence,
|
||||
gpointer unused)
|
||||
{
|
||||
gtk_widget_unset_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)),
|
||||
GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_update_for_list_row (GtkTreeExpander *self)
|
||||
{
|
||||
if (self->list_row == NULL)
|
||||
{
|
||||
GtkWidget *child;
|
||||
|
||||
for (child = gtk_widget_get_first_child (GTK_WIDGET (self));
|
||||
child != self->child;
|
||||
child = gtk_widget_get_first_child (GTK_WIDGET (self)))
|
||||
{
|
||||
gtk_widget_unparent (child);
|
||||
}
|
||||
self->expander = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkWidget *child;
|
||||
guint i, depth;
|
||||
|
||||
depth = gtk_tree_list_row_get_depth (self->list_row);
|
||||
if (gtk_tree_list_row_is_expandable (self->list_row))
|
||||
{
|
||||
if (self->expander == NULL)
|
||||
{
|
||||
GtkGesture *gesture;
|
||||
|
||||
self->expander = gtk_icon_new ("expander");
|
||||
|
||||
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_tree_expander_click_gesture_pressed), NULL);
|
||||
g_signal_connect (gesture, "released",
|
||||
G_CALLBACK (gtk_tree_expander_click_gesture_released), NULL);
|
||||
g_signal_connect (gesture, "cancel",
|
||||
G_CALLBACK (gtk_tree_expander_click_gesture_canceled), NULL);
|
||||
gtk_widget_add_controller (self->expander, GTK_EVENT_CONTROLLER (gesture));
|
||||
|
||||
gtk_widget_insert_before (self->expander,
|
||||
GTK_WIDGET (self),
|
||||
self->child);
|
||||
}
|
||||
if (gtk_tree_list_row_get_expanded (self->list_row))
|
||||
gtk_widget_set_state_flags (self->expander, GTK_STATE_FLAG_CHECKED, FALSE);
|
||||
else
|
||||
gtk_widget_unset_state_flags (self->expander, GTK_STATE_FLAG_CHECKED);
|
||||
child = gtk_widget_get_prev_sibling (self->expander);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_clear_pointer (&self->expander, gtk_widget_unparent);
|
||||
depth++;
|
||||
if (self->child)
|
||||
child = gtk_widget_get_prev_sibling (self->child);
|
||||
else
|
||||
child = gtk_widget_get_last_child (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
for (i = 0; i < depth; i++)
|
||||
{
|
||||
if (child)
|
||||
child = gtk_widget_get_prev_sibling (child);
|
||||
else
|
||||
gtk_widget_insert_after (gtk_icon_new ("indent"), GTK_WIDGET (self), NULL);
|
||||
}
|
||||
|
||||
while (child)
|
||||
{
|
||||
GtkWidget *prev = gtk_widget_get_prev_sibling (child);
|
||||
gtk_widget_unparent (child);
|
||||
child = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_list_row_notify_cb (GtkTreeListRow *list_row,
|
||||
GParamSpec *pspec,
|
||||
GtkTreeExpander *self)
|
||||
{
|
||||
if (pspec->name == g_intern_static_string ("expanded"))
|
||||
{
|
||||
if (self->expander)
|
||||
{
|
||||
if (gtk_tree_list_row_get_expanded (list_row))
|
||||
gtk_widget_set_state_flags (self->expander, GTK_STATE_FLAG_CHECKED, FALSE);
|
||||
else
|
||||
gtk_widget_unset_state_flags (self->expander, GTK_STATE_FLAG_CHECKED);
|
||||
}
|
||||
}
|
||||
else if (pspec->name == g_intern_static_string ("item"))
|
||||
{
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* can this happen other than when destroying the row? */
|
||||
gtk_tree_expander_update_for_list_row (self);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_tree_expander_focus (GtkWidget *widget,
|
||||
GtkDirectionType direction)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
|
||||
|
||||
/* 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 (self->child)
|
||||
{
|
||||
if (gtk_widget_get_focus_child (widget))
|
||||
return FALSE;
|
||||
if (gtk_widget_child_focus (self->child, direction))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_can_focus (widget))
|
||||
return FALSE;
|
||||
|
||||
gtk_widget_grab_focus (widget);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_clear_list_row (GtkTreeExpander *self)
|
||||
{
|
||||
if (self->list_row == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handler_disconnect (self->list_row, self->notify_handler);
|
||||
self->notify_handler = 0;
|
||||
g_clear_object (&self->list_row);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_dispose (GObject *object)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (object);
|
||||
|
||||
gtk_tree_expander_clear_list_row (self);
|
||||
gtk_tree_expander_update_for_list_row (self);
|
||||
|
||||
g_clear_pointer (&self->child, gtk_widget_unparent);
|
||||
|
||||
g_assert (self->expander == NULL);
|
||||
|
||||
G_OBJECT_CLASS (gtk_tree_expander_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_CHILD:
|
||||
g_value_set_object (value, self->child);
|
||||
break;
|
||||
|
||||
case PROP_ITEM:
|
||||
g_value_set_object (value, gtk_tree_expander_get_item (self));
|
||||
break;
|
||||
|
||||
case PROP_LIST_ROW:
|
||||
g_value_set_object (value, self->list_row);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_CHILD:
|
||||
gtk_tree_expander_set_child (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_LIST_ROW:
|
||||
gtk_tree_expander_set_list_row (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_expand (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
|
||||
|
||||
if (self->list_row == NULL)
|
||||
return;
|
||||
|
||||
gtk_tree_list_row_set_expanded (self->list_row, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_collapse (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
|
||||
|
||||
if (self->list_row == NULL)
|
||||
return;
|
||||
|
||||
gtk_tree_list_row_set_expanded (self->list_row, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_toggle_expand (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
|
||||
|
||||
if (self->list_row == NULL)
|
||||
return;
|
||||
|
||||
gtk_tree_list_row_set_expanded (self->list_row, !gtk_tree_list_row_get_expanded (self->list_row));
|
||||
}
|
||||
|
||||
static void
|
||||
expand_collapse_right (GtkWidget *widget,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
|
||||
|
||||
if (self->list_row == NULL)
|
||||
return;
|
||||
|
||||
gtk_tree_list_row_set_expanded (self->list_row, gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL);
|
||||
}
|
||||
|
||||
static void
|
||||
expand_collapse_left (GtkWidget *widget,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
|
||||
|
||||
if (self->list_row == NULL)
|
||||
return;
|
||||
|
||||
gtk_tree_list_row_set_expanded (self->list_row, gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_class_init (GtkTreeExpanderClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GtkBindingSet *binding_set;
|
||||
|
||||
widget_class->focus = gtk_tree_expander_focus;
|
||||
|
||||
gobject_class->dispose = gtk_tree_expander_dispose;
|
||||
gobject_class->get_property = gtk_tree_expander_get_property;
|
||||
gobject_class->set_property = gtk_tree_expander_set_property;
|
||||
|
||||
/**
|
||||
* GtkTreeExpander:child:
|
||||
*
|
||||
* The child widget with the actual contents
|
||||
*/
|
||||
properties[PROP_CHILD] =
|
||||
g_param_spec_object ("child",
|
||||
P_("Child"),
|
||||
P_("The child widget with the actual contents"),
|
||||
GTK_TYPE_WIDGET,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkTreeExpander:item:
|
||||
*
|
||||
* The item held by this expander's row
|
||||
*/
|
||||
properties[PROP_ITEM] =
|
||||
g_param_spec_object ("item",
|
||||
P_("Item"),
|
||||
P_("The item held by this expander's row"),
|
||||
G_TYPE_OBJECT,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkTreeExpander:list-row:
|
||||
*
|
||||
* The list row to track for expander state
|
||||
*/
|
||||
properties[PROP_LIST_ROW] =
|
||||
g_param_spec_object ("list-row",
|
||||
P_("List row"),
|
||||
P_("The list row to track for expander state"),
|
||||
GTK_TYPE_TREE_LIST_ROW,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
* GtkTreeExpander|listitem.expand:
|
||||
*
|
||||
* Expands the expander if it can be expanded.
|
||||
*/
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"listitem.expand",
|
||||
NULL,
|
||||
gtk_tree_expander_expand);
|
||||
|
||||
/**
|
||||
* GtkTreeExpander|listitem.collapse:
|
||||
*
|
||||
* Collapses the expander.
|
||||
*/
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"listitem.collapse",
|
||||
NULL,
|
||||
gtk_tree_expander_collapse);
|
||||
|
||||
/**
|
||||
* GtkTreeExpander|listitem.toggle-expand:
|
||||
*
|
||||
* Tries to expand the expander if it was collapsed or collapses it if
|
||||
* it was expanded.
|
||||
*/
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"listitem.toggle-expand",
|
||||
NULL,
|
||||
gtk_tree_expander_toggle_expand);
|
||||
|
||||
binding_set = gtk_binding_set_by_class (klass);
|
||||
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_plus, 0,
|
||||
"listitem.expand", NULL);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Add, 0,
|
||||
"listitem.expand", NULL);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_asterisk, 0,
|
||||
"listitem.expand", NULL);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Multiply, 0,
|
||||
"listitem.expand", NULL);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_minus, 0,
|
||||
"listitem.collapse", NULL);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Subtract, 0,
|
||||
"listitem.collapse", NULL);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_slash, 0,
|
||||
"listitem.collapse", NULL);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Divide, 0,
|
||||
"listitem.collapse", NULL);
|
||||
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_Right, GDK_SHIFT_MASK,
|
||||
expand_collapse_right, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Right, GDK_SHIFT_MASK,
|
||||
expand_collapse_right, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
expand_collapse_right, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
expand_collapse_right, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_Left, GDK_SHIFT_MASK,
|
||||
expand_collapse_left, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Left, GDK_SHIFT_MASK,
|
||||
expand_collapse_left, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
expand_collapse_left, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
expand_collapse_left, NULL, NULL);
|
||||
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
|
||||
"listitem.toggle-expand", NULL);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
|
||||
"listitem.toggle-expand", NULL);
|
||||
|
||||
#if 0
|
||||
/* These can't be implementes yet. */
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_BackSpace, 0, go_to_parent_row, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_BackSpace, GDK_CONTROL_MASK, go_to_parent_row, NULL, NULL);
|
||||
#endif
|
||||
|
||||
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
|
||||
gtk_widget_class_set_css_name (widget_class, I_("treeexpander"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_init (GtkTreeExpander *self)
|
||||
{
|
||||
gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_tree_expander_new:
|
||||
*
|
||||
* Creates a new #GtkTreeExpander
|
||||
*
|
||||
* Returns: a new #GtkTreeExpander
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_tree_expander_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_TREE_EXPANDER,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_tree_expander_get_child
|
||||
* @self: a #GtkTreeExpander
|
||||
*
|
||||
* Gets the child widget displayed by @self.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The child displayed by @self
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_tree_expander_get_child (GtkTreeExpander *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_TREE_EXPANDER (self), NULL);
|
||||
|
||||
return self->child;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_tree_expander_set_child:
|
||||
* @self: a #GtkTreeExpander widget
|
||||
* @child: (nullable): a #GtkWidget, or %NULL
|
||||
*
|
||||
* Sets the content widget to display.
|
||||
*/
|
||||
void
|
||||
gtk_tree_expander_set_child (GtkTreeExpander *self,
|
||||
GtkWidget *child)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_TREE_EXPANDER (self));
|
||||
g_return_if_fail (child == NULL || GTK_IS_WIDGET (child));
|
||||
|
||||
if (self->child == child)
|
||||
return;
|
||||
|
||||
g_clear_pointer (&self->child, gtk_widget_unparent);
|
||||
|
||||
if (child)
|
||||
{
|
||||
self->child = child;
|
||||
gtk_widget_set_parent (child, GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHILD]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_tree_expander_get_item
|
||||
* @self: a #GtkTreeExpander
|
||||
*
|
||||
* Forwards the item set on the #GtkTreeListRow that @self is managing.
|
||||
*
|
||||
* This call is essentially equivalent to calling
|
||||
* `gtk_tree_list_row_get_item (gtk_tree_expander_get_list_row (@self))`.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The item of the row
|
||||
**/
|
||||
gpointer
|
||||
gtk_tree_expander_get_item (GtkTreeExpander *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_TREE_EXPANDER (self), NULL);
|
||||
|
||||
if (self->list_row == NULL)
|
||||
return NULL;
|
||||
|
||||
return gtk_tree_list_row_get_item (self->list_row);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_tree_expander_get_list_row
|
||||
* @self: a #GtkTreeExpander
|
||||
*
|
||||
* Gets the list row managed by @self.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The list row displayed by @self
|
||||
**/
|
||||
GtkTreeListRow *
|
||||
gtk_tree_expander_get_list_row (GtkTreeExpander *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_TREE_EXPANDER (self), NULL);
|
||||
|
||||
return self->list_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_tree_expander_set_list_row:
|
||||
* @self: a #GtkTreeExpander widget
|
||||
* @list_row: (nullable): a #GtkTreeListRow, or %NULL
|
||||
*
|
||||
* Sets the tree list row that this expander should manage.
|
||||
*/
|
||||
void
|
||||
gtk_tree_expander_set_list_row (GtkTreeExpander *self,
|
||||
GtkTreeListRow *list_row)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_TREE_EXPANDER (self));
|
||||
g_return_if_fail (list_row == NULL || GTK_IS_TREE_LIST_ROW (list_row));
|
||||
|
||||
if (self->list_row == list_row)
|
||||
return;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
gtk_tree_expander_clear_list_row (self);
|
||||
|
||||
if (list_row)
|
||||
{
|
||||
self->list_row = g_object_ref (list_row);
|
||||
self->notify_handler = g_signal_connect (list_row,
|
||||
"notify",
|
||||
G_CALLBACK (gtk_tree_expander_list_row_notify_cb),
|
||||
self);
|
||||
}
|
||||
|
||||
gtk_tree_expander_update_for_list_row (self);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LIST_ROW]);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
56
gtk/gtktreeexpander.h
Normal file
56
gtk/gtktreeexpander.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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_TREE_EXPANDER_H__
|
||||
#define __GTK_TREE_EXPANDER_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktreelistmodel.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_TREE_EXPANDER (gtk_tree_expander_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkTreeExpander, gtk_tree_expander, GTK, TREE_EXPANDER, GtkWidget)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_tree_expander_new (void);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_tree_expander_get_child (GtkTreeExpander *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_tree_expander_set_child (GtkTreeExpander *self,
|
||||
GtkWidget *child);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gpointer gtk_tree_expander_get_item (GtkTreeExpander *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkTreeListRow * gtk_tree_expander_get_list_row (GtkTreeExpander *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_tree_expander_set_list_row (GtkTreeExpander *self,
|
||||
GtkTreeListRow *list_row);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_TREE_EXPANDER_H__ */
|
@@ -39,6 +39,7 @@ typedef struct _GtkClipboard GtkClipboard;
|
||||
typedef struct _GtkEventController GtkEventController;
|
||||
typedef struct _GtkGesture GtkGesture;
|
||||
typedef struct _GtkLayoutManager GtkLayoutManager;
|
||||
typedef struct _GtkListItemFactory GtkListItemFactory;
|
||||
typedef struct _GtkNative GtkNative;
|
||||
typedef struct _GtkRequisition GtkRequisition;
|
||||
typedef struct _GtkRoot GtkRoot;
|
||||
|
@@ -6524,7 +6524,7 @@ gtk_widget_reposition_after (GtkWidget *widget,
|
||||
if (parent->priv->children_observer)
|
||||
{
|
||||
if (prev_previous)
|
||||
g_warning ("oops");
|
||||
gtk_list_list_model_item_moved (parent->priv->children_observer, widget, prev_previous);
|
||||
else
|
||||
gtk_list_list_model_item_added (parent->priv->children_observer, widget);
|
||||
}
|
||||
|
@@ -46,6 +46,7 @@
|
||||
#include "gtksizegroup.h"
|
||||
#include "gtktextview.h"
|
||||
#include "gtktogglebutton.h"
|
||||
#include "gtktreeexpander.h"
|
||||
#include "gtktreelistmodel.h"
|
||||
#include "gtktreeview.h"
|
||||
#include "gtktreeselection.h"
|
||||
@@ -1019,7 +1020,6 @@ gtk_inspector_object_tree_create_list_widget (gpointer row_item,
|
||||
GtkInspectorObjectTree *wt = user_data;
|
||||
gpointer item;
|
||||
GtkWidget *row, *box, *column, *child;
|
||||
guint depth;
|
||||
|
||||
item = gtk_tree_list_row_get_item (row_item);
|
||||
|
||||
@@ -1036,37 +1036,14 @@ gtk_inspector_object_tree_create_list_widget (gpointer row_item,
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_container_add (GTK_CONTAINER (row), box);
|
||||
|
||||
column = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_size_group_add_widget (wt->priv->type_size_group, column);
|
||||
gtk_container_add (GTK_CONTAINER (box), column);
|
||||
|
||||
/* expander */
|
||||
depth = gtk_tree_list_row_get_depth (row_item);
|
||||
if (depth > 0)
|
||||
{
|
||||
child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_widget_set_size_request (child, 16 * depth, 0);
|
||||
gtk_container_add (GTK_CONTAINER (column), child);
|
||||
}
|
||||
if (gtk_tree_list_row_is_expandable (row_item))
|
||||
{
|
||||
GtkWidget *title, *arrow;
|
||||
child = gtk_tree_expander_new ();
|
||||
gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (child), row_item);
|
||||
gtk_size_group_add_widget (wt->priv->type_size_group, child);
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
|
||||
child = g_object_new (GTK_TYPE_BOX, "css-name", "expander", NULL);
|
||||
|
||||
title = g_object_new (GTK_TYPE_TOGGLE_BUTTON, "css-name", "title", NULL);
|
||||
gtk_button_set_relief (GTK_BUTTON (title), GTK_RELIEF_NONE);
|
||||
g_object_bind_property (row_item, "expanded", title, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
||||
gtk_container_add (GTK_CONTAINER (child), title);
|
||||
|
||||
arrow = gtk_icon_new ("arrow");
|
||||
gtk_container_add (GTK_CONTAINER (title), arrow);
|
||||
}
|
||||
else
|
||||
{
|
||||
child = gtk_image_new (); /* empty whatever */
|
||||
}
|
||||
gtk_container_add (GTK_CONTAINER (column), child);
|
||||
column = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (child), column);
|
||||
|
||||
/* 1st column: type name */
|
||||
child = gtk_label_new (G_OBJECT_TYPE_NAME (item));
|
||||
|
@@ -21,12 +21,16 @@
|
||||
|
||||
#include <gtk/gtkbox.h>
|
||||
#include <gtk/gtkfilechooserdialog.h>
|
||||
#include <gtk/gtkfunctionslistitemfactory.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtklistbox.h>
|
||||
#include <gtk/gtklistview.h>
|
||||
#include <gtk/gtkmessagedialog.h>
|
||||
#include <gtk/gtkpicture.h>
|
||||
#include <gtk/gtkpopover.h>
|
||||
#include <gtk/gtksingleselection.h>
|
||||
#include <gtk/gtktogglebutton.h>
|
||||
#include <gtk/gtktreeexpander.h>
|
||||
#include <gtk/gtktreelistmodel.h>
|
||||
#include <gtk/gtktreemodel.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
@@ -49,6 +53,8 @@ struct _GtkInspectorRecorderPrivate
|
||||
{
|
||||
GListModel *recordings;
|
||||
GtkTreeListModel *render_node_model;
|
||||
GListStore *render_node_root_model;
|
||||
GtkSingleSelection *render_node_selection;
|
||||
|
||||
GtkWidget *recordings_list;
|
||||
GtkWidget *render_node_view;
|
||||
@@ -293,64 +299,56 @@ node_name (GskRenderNode *node)
|
||||
}
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
create_widget_for_render_node (gpointer row_item,
|
||||
gpointer unused)
|
||||
static void
|
||||
setup_widget_for_render_node (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GdkPaintable *paintable;
|
||||
GskRenderNode *node;
|
||||
GtkWidget *row, *box, *child;
|
||||
char *name;
|
||||
guint depth;
|
||||
|
||||
paintable = gtk_tree_list_row_get_item (row_item);
|
||||
node = gtk_render_node_paintable_get_render_node (GTK_RENDER_NODE_PAINTABLE (paintable));
|
||||
row = gtk_list_box_row_new ();
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
|
||||
gtk_container_add (GTK_CONTAINER (row), box);
|
||||
GtkWidget *expander, *box, *child;
|
||||
|
||||
/* expander */
|
||||
depth = gtk_tree_list_row_get_depth (row_item);
|
||||
if (depth > 0)
|
||||
{
|
||||
child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_widget_set_size_request (child, 16 * depth, 0);
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
}
|
||||
if (gtk_tree_list_row_is_expandable (row_item))
|
||||
{
|
||||
GtkWidget *title, *arrow;
|
||||
expander = gtk_tree_expander_new ();
|
||||
gtk_container_add (GTK_CONTAINER (list_item), expander);
|
||||
|
||||
child = g_object_new (GTK_TYPE_BOX, "css-name", "expander", NULL);
|
||||
|
||||
title = g_object_new (GTK_TYPE_TOGGLE_BUTTON, "css-name", "title", NULL);
|
||||
gtk_button_set_relief (GTK_BUTTON (title), GTK_RELIEF_NONE);
|
||||
g_object_bind_property (row_item, "expanded", title, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
||||
gtk_container_add (GTK_CONTAINER (child), title);
|
||||
g_object_set_data_full (G_OBJECT (row), "make-sure-its-not-unreffed", g_object_ref (row_item), g_object_unref);
|
||||
|
||||
arrow = gtk_icon_new ("arrow");
|
||||
gtk_container_add (GTK_CONTAINER (title), arrow);
|
||||
}
|
||||
else
|
||||
{
|
||||
child = gtk_image_new (); /* empty whatever */
|
||||
}
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
|
||||
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), box);
|
||||
|
||||
/* icon */
|
||||
child = gtk_image_new_from_paintable (paintable);
|
||||
child = gtk_image_new ();
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
|
||||
/* name */
|
||||
name = node_name (node);
|
||||
child = gtk_label_new (name);
|
||||
g_free (name);
|
||||
child = gtk_label_new (NULL);
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
}
|
||||
|
||||
g_object_unref (paintable);
|
||||
static void
|
||||
bind_widget_for_render_node (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GdkPaintable *paintable;
|
||||
GskRenderNode *node;
|
||||
GtkTreeListRow *row_item;
|
||||
GtkWidget *expander, *box, *child;
|
||||
char *name;
|
||||
|
||||
return row;
|
||||
row_item = gtk_list_item_get_item (list_item);
|
||||
paintable = gtk_tree_list_row_get_item (row_item);
|
||||
node = gtk_render_node_paintable_get_render_node (GTK_RENDER_NODE_PAINTABLE (paintable));
|
||||
|
||||
/* expander */
|
||||
expander = gtk_bin_get_child (GTK_BIN (list_item));
|
||||
gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (expander), row_item);
|
||||
box = gtk_tree_expander_get_child (GTK_TREE_EXPANDER (expander));
|
||||
|
||||
/* icon */
|
||||
child = gtk_widget_get_first_child (box);
|
||||
gtk_image_set_from_paintable (GTK_IMAGE (child), paintable);
|
||||
|
||||
/* name */
|
||||
name = node_name (node);
|
||||
child = gtk_widget_get_last_child (box);
|
||||
gtk_label_set_label (GTK_LABEL (child), name);
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -366,11 +364,8 @@ recordings_list_row_selected (GtkListBox *box,
|
||||
else
|
||||
recording = NULL;
|
||||
|
||||
g_clear_object (&priv->render_node_model);
|
||||
|
||||
if (GTK_INSPECTOR_IS_RENDER_RECORDING (recording))
|
||||
{
|
||||
GListStore *root_model;
|
||||
graphene_rect_t bounds;
|
||||
GskRenderNode *node;
|
||||
GdkPaintable *paintable;
|
||||
@@ -380,14 +375,10 @@ recordings_list_row_selected (GtkListBox *box,
|
||||
paintable = gtk_render_node_paintable_new (node, &bounds);
|
||||
gtk_picture_set_paintable (GTK_PICTURE (priv->render_node_view), paintable);
|
||||
|
||||
root_model = g_list_store_new (GDK_TYPE_PAINTABLE);
|
||||
g_list_store_append (root_model, paintable);
|
||||
priv->render_node_model = gtk_tree_list_model_new (FALSE,
|
||||
G_LIST_MODEL (root_model),
|
||||
TRUE,
|
||||
create_list_model_for_render_node_paintable,
|
||||
NULL, NULL);
|
||||
g_object_unref (root_model);
|
||||
g_list_store_splice (priv->render_node_root_model,
|
||||
0, g_list_model_get_n_items (G_LIST_MODEL (priv->render_node_root_model)),
|
||||
(gpointer[1]) { paintable },
|
||||
1);
|
||||
g_object_unref (paintable);
|
||||
|
||||
g_print ("%u render nodes\n", g_list_model_get_n_items (G_LIST_MODEL (priv->render_node_model)));
|
||||
@@ -395,14 +386,9 @@ recordings_list_row_selected (GtkListBox *box,
|
||||
else
|
||||
{
|
||||
gtk_picture_set_paintable (GTK_PICTURE (priv->render_node_view), NULL);
|
||||
g_list_store_remove_all (priv->render_node_root_model);
|
||||
}
|
||||
|
||||
|
||||
gtk_list_box_bind_model (GTK_LIST_BOX (priv->render_node_list),
|
||||
G_LIST_MODEL (priv->render_node_model),
|
||||
create_widget_for_render_node,
|
||||
NULL, NULL);
|
||||
|
||||
if (recording)
|
||||
g_object_unref (recording);
|
||||
}
|
||||
@@ -919,20 +905,16 @@ get_selected_node (GtkInspectorRecorder *recorder)
|
||||
{
|
||||
GtkInspectorRecorderPrivate *priv = gtk_inspector_recorder_get_instance_private (recorder);
|
||||
GtkTreeListRow *row_item;
|
||||
GtkListBoxRow *row;
|
||||
GdkPaintable *paintable;
|
||||
GskRenderNode *node;
|
||||
|
||||
row = gtk_list_box_get_selected_row (GTK_LIST_BOX (priv->render_node_list));
|
||||
if (row == NULL)
|
||||
row_item = gtk_single_selection_get_selected_item (priv->render_node_selection);
|
||||
if (row_item == NULL)
|
||||
return NULL;
|
||||
|
||||
row_item = g_list_model_get_item (G_LIST_MODEL (priv->render_node_model),
|
||||
gtk_list_box_row_get_index (row));
|
||||
paintable = gtk_tree_list_row_get_item (row_item);
|
||||
node = gtk_render_node_paintable_get_render_node (GTK_RENDER_NODE_PAINTABLE (paintable));
|
||||
g_object_unref (paintable);
|
||||
g_object_unref (row_item);
|
||||
|
||||
return node;
|
||||
}
|
||||
@@ -947,14 +929,10 @@ render_node_list_selection_changed (GtkListBox *list,
|
||||
GdkPaintable *paintable;
|
||||
GtkTreeListRow *row_item;
|
||||
|
||||
if (row == NULL)
|
||||
{
|
||||
gtk_widget_set_sensitive (priv->render_node_save_button, FALSE);
|
||||
return;
|
||||
}
|
||||
row_item = gtk_single_selection_get_selected_item (priv->render_node_selection);
|
||||
if (row_item == NULL)
|
||||
return;
|
||||
|
||||
row_item = g_list_model_get_item (G_LIST_MODEL (priv->render_node_model),
|
||||
gtk_list_box_row_get_index (row));
|
||||
paintable = gtk_tree_list_row_get_item (row_item);
|
||||
|
||||
gtk_widget_set_sensitive (priv->render_node_save_button, TRUE);
|
||||
@@ -963,7 +941,6 @@ render_node_list_selection_changed (GtkListBox *list,
|
||||
populate_render_node_properties (GTK_LIST_STORE (priv->render_node_properties), node);
|
||||
|
||||
g_object_unref (paintable);
|
||||
g_object_unref (row_item);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1233,6 +1210,8 @@ gtk_inspector_recorder_dispose (GObject *object)
|
||||
GtkInspectorRecorderPrivate *priv = gtk_inspector_recorder_get_instance_private (recorder);
|
||||
|
||||
g_clear_object (&priv->render_node_model);
|
||||
g_clear_object (&priv->render_node_root_model);
|
||||
g_clear_object (&priv->render_node_selection);
|
||||
|
||||
G_OBJECT_CLASS (gtk_inspector_recorder_parent_class)->dispose (object);
|
||||
}
|
||||
@@ -1273,7 +1252,6 @@ gtk_inspector_recorder_class_init (GtkInspectorRecorderClass *klass)
|
||||
|
||||
gtk_widget_class_bind_template_callback (widget_class, recordings_clear_all);
|
||||
gtk_widget_class_bind_template_callback (widget_class, recordings_list_row_selected);
|
||||
gtk_widget_class_bind_template_callback (widget_class, render_node_list_selection_changed);
|
||||
gtk_widget_class_bind_template_callback (widget_class, render_node_save);
|
||||
gtk_widget_class_bind_template_callback (widget_class, node_property_activated);
|
||||
}
|
||||
@@ -1282,6 +1260,7 @@ static void
|
||||
gtk_inspector_recorder_init (GtkInspectorRecorder *recorder)
|
||||
{
|
||||
GtkInspectorRecorderPrivate *priv = gtk_inspector_recorder_get_instance_private (recorder);
|
||||
GtkListItemFactory *factory;
|
||||
|
||||
gtk_widget_init_template (GTK_WIDGET (recorder));
|
||||
|
||||
@@ -1291,6 +1270,23 @@ gtk_inspector_recorder_init (GtkInspectorRecorder *recorder)
|
||||
recorder,
|
||||
NULL);
|
||||
|
||||
priv->render_node_root_model = g_list_store_new (GDK_TYPE_PAINTABLE);
|
||||
priv->render_node_model = gtk_tree_list_model_new (FALSE,
|
||||
G_LIST_MODEL (priv->render_node_root_model),
|
||||
TRUE,
|
||||
create_list_model_for_render_node_paintable,
|
||||
NULL, NULL);
|
||||
priv->render_node_selection = gtk_single_selection_new (G_LIST_MODEL (priv->render_node_model));
|
||||
g_signal_connect (priv->render_node_selection, "notify::selected-item", G_CALLBACK (render_node_list_selection_changed), recorder);
|
||||
|
||||
factory = gtk_functions_list_item_factory_new (setup_widget_for_render_node,
|
||||
bind_widget_for_render_node,
|
||||
NULL, NULL);
|
||||
gtk_list_view_set_factory (GTK_LIST_VIEW (priv->render_node_list), factory);
|
||||
g_object_unref (factory);
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (priv->render_node_list),
|
||||
G_LIST_MODEL (priv->render_node_selection));
|
||||
|
||||
priv->render_node_properties = GTK_TREE_MODEL (gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, GDK_TYPE_TEXTURE));
|
||||
gtk_tree_view_set_model (GTK_TREE_VIEW (priv->node_property_tree), priv->render_node_properties);
|
||||
g_object_unref (priv->render_node_properties);
|
||||
|
@@ -73,15 +73,14 @@
|
||||
<object class="GtkFrame">
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="propagate-natural-width">1</property>
|
||||
<style>
|
||||
<class name="sidebar"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkListBox" id="render_node_list">
|
||||
<object class="GtkListView" id="render_node_list">
|
||||
<property name="vexpand">1</property>
|
||||
<signal name="row-selected" handler="render_node_list_selection_changed"/>
|
||||
<property name="hexpand">1</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@@ -177,6 +177,7 @@ gtk_public_sources = files([
|
||||
'gtkbuildable.c',
|
||||
'gtkbuilder.c',
|
||||
'gtkbuilderparser.c',
|
||||
'gtkbuilderlistitemfactory.c',
|
||||
'gtkbutton.c',
|
||||
'gtkcalendar.c',
|
||||
'gtkcellarea.c',
|
||||
@@ -212,6 +213,7 @@ gtk_public_sources = files([
|
||||
'gtkcontainer.c',
|
||||
'gtkcssprovider.c',
|
||||
'gtkdialog.c',
|
||||
'gtkdirectorylist.c',
|
||||
'gtkdnd.c',
|
||||
'gtkdragdest.c',
|
||||
'gtkdragsource.c',
|
||||
@@ -245,6 +247,7 @@ gtk_public_sources = files([
|
||||
'gtkfontchooserutils.c',
|
||||
'gtkfontchooserwidget.c',
|
||||
'gtkframe.c',
|
||||
'gtkfunctionslistitemfactory.c',
|
||||
'gtkgesture.c',
|
||||
'gtkgesturedrag.c',
|
||||
'gtkgesturelongpress.c',
|
||||
@@ -258,6 +261,7 @@ gtk_public_sources = files([
|
||||
'gtkglarea.c',
|
||||
'gtkgrid.c',
|
||||
'gtkgridlayout.c',
|
||||
'gtkgridview.c',
|
||||
'gtkheaderbar.c',
|
||||
'gtkicontheme.c',
|
||||
'gtkiconview.c',
|
||||
@@ -274,8 +278,12 @@ gtk_public_sources = files([
|
||||
'gtklevelbar.c',
|
||||
'gtklinkbutton.c',
|
||||
'gtklistbox.c',
|
||||
'gtklistitem.c',
|
||||
'gtklistitemfactory.c',
|
||||
'gtklistitemmanager.c',
|
||||
'gtklistlistmodel.c',
|
||||
'gtkliststore.c',
|
||||
'gtklistview.c',
|
||||
'gtklockbutton.c',
|
||||
'gtkmain.c',
|
||||
'gtkmaplistmodel.c',
|
||||
@@ -390,6 +398,7 @@ gtk_public_sources = files([
|
||||
'gtktooltip.c',
|
||||
'gtktooltipwindow.c',
|
||||
'gtktreednd.c',
|
||||
'gtktreeexpander.c',
|
||||
'gtktreelistmodel.c',
|
||||
'gtktreemenu.c',
|
||||
'gtktreemodel.c',
|
||||
@@ -442,6 +451,7 @@ gtk_public_headers = files([
|
||||
'gtkboxlayout.h',
|
||||
'gtkbuildable.h',
|
||||
'gtkbuilder.h',
|
||||
'gtkbuilderlistitemfactory.h',
|
||||
'gtkbutton.h',
|
||||
'gtkcalendar.h',
|
||||
'gtkcenterbox.h',
|
||||
@@ -478,6 +488,7 @@ gtk_public_headers = files([
|
||||
'gtkcustomlayout.h',
|
||||
'gtkdebug.h',
|
||||
'gtkdialog.h',
|
||||
'gtkdirectorylist.h',
|
||||
'gtkdnd.h',
|
||||
'gtkdragdest.h',
|
||||
'gtkdragsource.h',
|
||||
@@ -509,6 +520,7 @@ gtk_public_headers = files([
|
||||
'gtkfontchooserdialog.h',
|
||||
'gtkfontchooserwidget.h',
|
||||
'gtkframe.h',
|
||||
'gtkfunctionslistitemfactory.h',
|
||||
'gtkgesture.h',
|
||||
'gtkgesturedrag.h',
|
||||
'gtkgesturelongpress.h',
|
||||
@@ -522,6 +534,7 @@ gtk_public_headers = files([
|
||||
'gtkglarea.h',
|
||||
'gtkgrid.h',
|
||||
'gtkgridlayout.h',
|
||||
'gtkgridview.h',
|
||||
'gtkheaderbar.h',
|
||||
'gtkicontheme.h',
|
||||
'gtkiconview.h',
|
||||
@@ -537,7 +550,10 @@ gtk_public_headers = files([
|
||||
'gtklevelbar.h',
|
||||
'gtklinkbutton.h',
|
||||
'gtklistbox.h',
|
||||
'gtklistitem.h',
|
||||
'gtklistitemfactory.h',
|
||||
'gtkliststore.h',
|
||||
'gtklistview.h',
|
||||
'gtklockbutton.h',
|
||||
'gtkmain.h',
|
||||
'gtkmaplistmodel.h',
|
||||
@@ -632,6 +648,7 @@ gtk_public_headers = files([
|
||||
'gtktoolshell.h',
|
||||
'gtktooltip.h',
|
||||
'gtktreednd.h',
|
||||
'gtktreeexpander.h',
|
||||
'gtktreelistmodel.h',
|
||||
'gtktreemodel.h',
|
||||
'gtktreemodelfilter.h',
|
||||
|
@@ -3734,7 +3734,10 @@ list {
|
||||
row.expander { padding: 0px; }
|
||||
row.expander .row-header { padding: 2px; }
|
||||
|
||||
&.separators row:not(:first-child) {
|
||||
&.separators.horizontal row:not(:first-child) {
|
||||
border-left: 1px solid $borders_color;
|
||||
}
|
||||
&.separators:not(.horizontal) row:not(:first-child) {
|
||||
border-top: 1px solid $borders_color;
|
||||
}
|
||||
}
|
||||
|
@@ -1627,7 +1627,9 @@ list row.expander { padding: 0px; }
|
||||
|
||||
list row.expander .row-header { padding: 2px; }
|
||||
|
||||
list.separators row:not(:first-child) { border-top: 1px solid #1b1b1b; }
|
||||
list.separators.horizontal row:not(:first-child) { border-left: 1px solid #1b1b1b; }
|
||||
|
||||
list.separators:not(.horizontal) row:not(:first-child) { border-top: 1px solid #1b1b1b; }
|
||||
|
||||
row { transition: all 150ms cubic-bezier(0.25, 0.46, 0.45, 0.94); }
|
||||
|
||||
|
@@ -1643,7 +1643,9 @@ list row.expander { padding: 0px; }
|
||||
|
||||
list row.expander .row-header { padding: 2px; }
|
||||
|
||||
list.separators row:not(:first-child) { border-top: 1px solid #cdc7c2; }
|
||||
list.separators.horizontal row:not(:first-child) { border-left: 1px solid #cdc7c2; }
|
||||
|
||||
list.separators:not(.horizontal) row:not(:first-child) { border-top: 1px solid #cdc7c2; }
|
||||
|
||||
row { transition: all 150ms cubic-bezier(0.25, 0.46, 0.45, 0.94); }
|
||||
|
||||
|
@@ -59,6 +59,8 @@ gtk_tests = [
|
||||
['testlist2'],
|
||||
['testlist3'],
|
||||
['testlist4'],
|
||||
['testlistview'],
|
||||
['testlistview-animating'],
|
||||
['testlevelbar'],
|
||||
['testlockbutton'],
|
||||
['testmenubutton'],
|
||||
|
192
tests/testlistview-animating.c
Normal file
192
tests/testlistview-animating.c
Normal file
@@ -0,0 +1,192 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#ifdef SMALL
|
||||
#define AVERAGE 15
|
||||
#define VARIANCE 10
|
||||
#else
|
||||
#define AVERAGE 300
|
||||
#define VARIANCE 200
|
||||
#endif
|
||||
|
||||
static void
|
||||
update_label (GtkListItem *list_item,
|
||||
GParamSpec *pspec,
|
||||
GtkLabel *label)
|
||||
{
|
||||
gpointer item;
|
||||
char *s;
|
||||
|
||||
item = gtk_list_item_get_item (list_item);
|
||||
|
||||
if (item)
|
||||
s = g_strdup_printf ("%u: %s",
|
||||
gtk_list_item_get_position (list_item),
|
||||
(const char *) g_object_get_data (item, "message"));
|
||||
else
|
||||
s = NULL;
|
||||
|
||||
gtk_label_set_text (label, s);
|
||||
|
||||
g_free (s);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_list_item (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *label = gtk_label_new ("");
|
||||
|
||||
g_signal_connect (list_item, "notify", G_CALLBACK (update_label), label);
|
||||
gtk_container_add (GTK_CONTAINER (list_item), label);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
create_widget_for_listbox (gpointer item,
|
||||
gpointer unused)
|
||||
{
|
||||
const char *message = g_object_get_data (item, "message");
|
||||
GtkWidget *widget;
|
||||
|
||||
widget = gtk_label_new (message);
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
static gboolean reverse_sort;
|
||||
|
||||
static int
|
||||
compare (gconstpointer first,
|
||||
gconstpointer second,
|
||||
gpointer unused)
|
||||
{
|
||||
int diff = (GPOINTER_TO_UINT (g_object_get_data ((gpointer) first, "counter")) % 1000)
|
||||
- (GPOINTER_TO_UINT (g_object_get_data ((gpointer) second, "counter")) % 1000);
|
||||
|
||||
if (reverse_sort)
|
||||
return -diff;
|
||||
else
|
||||
return diff;
|
||||
}
|
||||
|
||||
static void
|
||||
add (GListStore *store)
|
||||
{
|
||||
static guint counter;
|
||||
GObject *o;
|
||||
char *message;
|
||||
guint pos;
|
||||
|
||||
counter++;
|
||||
o = g_object_new (G_TYPE_OBJECT, NULL);
|
||||
g_object_set_data (o, "counter", GUINT_TO_POINTER (counter));
|
||||
message = g_strdup_printf ("Item %u", counter);
|
||||
g_object_set_data_full (o, "message", message, g_free);
|
||||
|
||||
pos = g_random_int_range (0, g_list_model_get_n_items (G_LIST_MODEL (store)) + 1);
|
||||
g_list_store_insert (store, pos, o);
|
||||
g_object_unref (o);
|
||||
}
|
||||
|
||||
static void
|
||||
delete (GListStore *store)
|
||||
{
|
||||
guint pos;
|
||||
|
||||
pos = g_random_int_range (0, g_list_model_get_n_items (G_LIST_MODEL (store)));
|
||||
g_list_store_remove (store, pos);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_stuff (gpointer store)
|
||||
{
|
||||
if (g_random_int_range (AVERAGE - VARIANCE, AVERAGE + VARIANCE) < g_list_model_get_n_items (store))
|
||||
delete (store);
|
||||
else
|
||||
add (store);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
revert_sort (gpointer sort)
|
||||
{
|
||||
reverse_sort = !reverse_sort;
|
||||
|
||||
gtk_sort_list_model_resort (sort);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
GtkWidget *win, *hbox, *vbox, *sw, *listview, *listbox, *label;
|
||||
GListStore *store;
|
||||
GtkSortListModel *sort;
|
||||
guint i;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
store = g_list_store_new (G_TYPE_OBJECT);
|
||||
for (i = 0; i < AVERAGE; i++)
|
||||
add (store);
|
||||
sort = gtk_sort_list_model_new (G_LIST_MODEL (store),
|
||||
compare,
|
||||
NULL, NULL);
|
||||
|
||||
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 400, 600);
|
||||
g_signal_connect (win, "destroy", G_CALLBACK (gtk_main_quit), win);
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
||||
gtk_container_add (GTK_CONTAINER (win), hbox);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
|
||||
gtk_container_add (GTK_CONTAINER (hbox), vbox);
|
||||
|
||||
label = gtk_label_new ("GtkListView");
|
||||
gtk_container_add (GTK_CONTAINER (vbox), label);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_widget_set_hexpand (sw, TRUE);
|
||||
gtk_widget_set_vexpand (sw, TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), sw);
|
||||
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_functions_list_item_factory_new (setup_list_item,
|
||||
NULL,
|
||||
NULL, NULL));
|
||||
gtk_container_add (GTK_CONTAINER (sw), listview);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
|
||||
gtk_container_add (GTK_CONTAINER (hbox), vbox);
|
||||
|
||||
label = gtk_label_new ("GtkListBox");
|
||||
gtk_container_add (GTK_CONTAINER (vbox), label);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_widget_set_hexpand (sw, TRUE);
|
||||
gtk_widget_set_vexpand (sw, TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), sw);
|
||||
|
||||
listbox = gtk_list_box_new ();
|
||||
gtk_container_add (GTK_CONTAINER (sw), listbox);
|
||||
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (sort));
|
||||
gtk_list_box_bind_model (GTK_LIST_BOX (listbox),
|
||||
G_LIST_MODEL (sort),
|
||||
create_widget_for_listbox,
|
||||
NULL, NULL);
|
||||
|
||||
g_timeout_add (100, do_stuff, store);
|
||||
g_timeout_add_seconds (3, revert_sort, sort);
|
||||
|
||||
gtk_widget_show (win);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
g_object_unref (store);
|
||||
|
||||
return 0;
|
||||
}
|
679
tests/testlistview.c
Normal file
679
tests/testlistview.c
Normal file
@@ -0,0 +1,679 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define FILE_INFO_TYPE_SELECTION (file_info_selection_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FileInfoSelection, file_info_selection, FILE_INFO, SELECTION, GObject)
|
||||
|
||||
struct _FileInfoSelection
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GListModel *model;
|
||||
};
|
||||
|
||||
struct _FileInfoSelectionClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GType
|
||||
file_info_selection_get_item_type (GListModel *list)
|
||||
{
|
||||
FileInfoSelection *self = FILE_INFO_SELECTION (list);
|
||||
|
||||
return g_list_model_get_item_type (self->model);
|
||||
}
|
||||
|
||||
static guint
|
||||
file_info_selection_get_n_items (GListModel *list)
|
||||
{
|
||||
FileInfoSelection *self = FILE_INFO_SELECTION (list);
|
||||
|
||||
return g_list_model_get_n_items (self->model);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
file_info_selection_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
FileInfoSelection *self = FILE_INFO_SELECTION (list);
|
||||
|
||||
return g_list_model_get_item (self->model, position);
|
||||
}
|
||||
|
||||
static void
|
||||
file_info_selection_list_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = file_info_selection_get_item_type;
|
||||
iface->get_n_items = file_info_selection_get_n_items;
|
||||
iface->get_item = file_info_selection_get_item;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
file_info_selection_is_selected (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
FileInfoSelection *self = FILE_INFO_SELECTION (model);
|
||||
gpointer item;
|
||||
|
||||
item = g_list_model_get_item (self->model, position);
|
||||
if (item == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (GTK_IS_TREE_LIST_ROW (item))
|
||||
{
|
||||
GtkTreeListRow *row = item;
|
||||
item = gtk_tree_list_row_get_item (row);
|
||||
g_object_unref (row);
|
||||
}
|
||||
|
||||
return g_file_info_get_attribute_boolean (item, "filechooser::selected");
|
||||
}
|
||||
|
||||
static void
|
||||
file_info_selection_set_selected (FileInfoSelection *self,
|
||||
guint position,
|
||||
gboolean selected)
|
||||
{
|
||||
gpointer item;
|
||||
|
||||
item = g_list_model_get_item (self->model, position);
|
||||
if (item == NULL)
|
||||
return;
|
||||
|
||||
if (GTK_IS_TREE_LIST_ROW (item))
|
||||
{
|
||||
GtkTreeListRow *row = item;
|
||||
item = gtk_tree_list_row_get_item (row);
|
||||
g_object_unref (row);
|
||||
}
|
||||
|
||||
g_file_info_set_attribute_boolean (item, "filechooser::selected", selected);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
file_info_selection_select_item (GtkSelectionModel *model,
|
||||
guint position,
|
||||
gboolean exclusive)
|
||||
{
|
||||
FileInfoSelection *self = FILE_INFO_SELECTION (model);
|
||||
|
||||
if (exclusive)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (self->model); i++)
|
||||
file_info_selection_set_selected (self, i, i == position);
|
||||
|
||||
gtk_selection_model_selection_changed (model, 0, g_list_model_get_n_items (self->model));
|
||||
}
|
||||
else
|
||||
{
|
||||
file_info_selection_set_selected (self, position, TRUE);
|
||||
|
||||
gtk_selection_model_selection_changed (model, position, 1);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
file_info_selection_unselect_item (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
FileInfoSelection *self = FILE_INFO_SELECTION (model);
|
||||
|
||||
file_info_selection_set_selected (self, position, FALSE);
|
||||
|
||||
gtk_selection_model_selection_changed (model, position, 1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
file_info_selection_select_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
gboolean exclusive)
|
||||
{
|
||||
FileInfoSelection *self = FILE_INFO_SELECTION (model);
|
||||
guint i;
|
||||
|
||||
if (exclusive)
|
||||
for (i = 0; i < position; i++)
|
||||
file_info_selection_set_selected (self, i, FALSE);
|
||||
|
||||
for (i = position; i < position + n_items; i++)
|
||||
file_info_selection_set_selected (self, i, TRUE);
|
||||
|
||||
if (exclusive)
|
||||
for (i = position + n_items; i < g_list_model_get_n_items (self->model); i++)
|
||||
file_info_selection_set_selected (self, i, FALSE);
|
||||
|
||||
if (exclusive)
|
||||
gtk_selection_model_selection_changed (model, 0, g_list_model_get_n_items (self->model));
|
||||
else
|
||||
gtk_selection_model_selection_changed (model, position, n_items);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
file_info_selection_unselect_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items)
|
||||
{
|
||||
FileInfoSelection *self = FILE_INFO_SELECTION (model);
|
||||
guint i;
|
||||
|
||||
for (i = position; i < position + n_items; i++)
|
||||
file_info_selection_set_selected (self, i, FALSE);
|
||||
|
||||
gtk_selection_model_selection_changed (model, position, n_items);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
file_info_selection_selection_model_init (GtkSelectionModelInterface *iface)
|
||||
{
|
||||
iface->is_selected = file_info_selection_is_selected;
|
||||
iface->select_item = file_info_selection_select_item;
|
||||
iface->unselect_item = file_info_selection_unselect_item;
|
||||
iface->select_range = file_info_selection_select_range;
|
||||
iface->unselect_range = file_info_selection_unselect_range;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (FileInfoSelection, file_info_selection, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
|
||||
file_info_selection_list_model_init)
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL,
|
||||
file_info_selection_selection_model_init))
|
||||
|
||||
static void
|
||||
file_info_selection_items_changed_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
FileInfoSelection *self)
|
||||
{
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
||||
}
|
||||
|
||||
static void
|
||||
file_info_selection_clear_model (FileInfoSelection *self)
|
||||
{
|
||||
if (self->model == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->model,
|
||||
file_info_selection_items_changed_cb,
|
||||
self);
|
||||
g_clear_object (&self->model);
|
||||
}
|
||||
|
||||
static void
|
||||
file_info_selection_dispose (GObject *object)
|
||||
{
|
||||
FileInfoSelection *self = FILE_INFO_SELECTION (object);
|
||||
|
||||
file_info_selection_clear_model (self);
|
||||
|
||||
G_OBJECT_CLASS (file_info_selection_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
file_info_selection_class_init (FileInfoSelectionClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = file_info_selection_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
file_info_selection_init (FileInfoSelection *self)
|
||||
{
|
||||
}
|
||||
|
||||
static FileInfoSelection *
|
||||
file_info_selection_new (GListModel *model)
|
||||
{
|
||||
FileInfoSelection *result;
|
||||
|
||||
result = g_object_new (FILE_INFO_TYPE_SELECTION, NULL);
|
||||
|
||||
result->model = g_object_ref (model);
|
||||
g_signal_connect (result->model, "items-changed",
|
||||
G_CALLBACK (file_info_selection_items_changed_cb), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*** ---------------------- ***/
|
||||
|
||||
GSList *pending = NULL;
|
||||
guint active = 0;
|
||||
|
||||
static void
|
||||
loading_cb (GtkDirectoryList *dir,
|
||||
GParamSpec *pspec,
|
||||
gpointer unused)
|
||||
{
|
||||
if (gtk_directory_list_is_loading (dir))
|
||||
{
|
||||
active++;
|
||||
/* HACK: ensure loading finishes and the dir doesn't get destroyed */
|
||||
g_object_ref (dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
active--;
|
||||
g_object_unref (dir);
|
||||
|
||||
while (active < 20 && pending)
|
||||
{
|
||||
GtkDirectoryList *dir2 = pending->data;
|
||||
pending = g_slist_remove (pending, dir2);
|
||||
gtk_directory_list_set_file (dir2, g_object_get_data (G_OBJECT (dir2), "file"));
|
||||
g_object_unref (dir2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GtkDirectoryList *
|
||||
create_directory_list (GFile *file)
|
||||
{
|
||||
GtkDirectoryList *dir;
|
||||
|
||||
dir = gtk_directory_list_new (G_FILE_ATTRIBUTE_STANDARD_TYPE
|
||||
"," G_FILE_ATTRIBUTE_STANDARD_NAME
|
||||
"," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
|
||||
NULL);
|
||||
gtk_directory_list_set_io_priority (dir, G_PRIORITY_DEFAULT_IDLE);
|
||||
g_signal_connect (dir, "notify::loading", G_CALLBACK (loading_cb), NULL);
|
||||
g_assert (!gtk_directory_list_is_loading (dir));
|
||||
|
||||
if (active > 20)
|
||||
{
|
||||
g_object_set_data_full (G_OBJECT (dir), "file", g_object_ref (file), g_object_unref);
|
||||
pending = g_slist_prepend (pending, g_object_ref (dir));
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_directory_list_set_file (dir, file);
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
static int
|
||||
compare_files (gconstpointer first,
|
||||
gconstpointer second,
|
||||
gpointer unused)
|
||||
{
|
||||
GFile *first_file, *second_file;
|
||||
char *first_path, *second_path;
|
||||
int result;
|
||||
#if 0
|
||||
GFileType first_type, second_type;
|
||||
|
||||
/* This is a bit slow, because each g_file_query_file_type() does a stat() */
|
||||
first_type = g_file_query_file_type (G_FILE (first), G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL);
|
||||
second_type = g_file_query_file_type (G_FILE (second), G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL);
|
||||
|
||||
if (first_type == G_FILE_TYPE_DIRECTORY && second_type != G_FILE_TYPE_DIRECTORY)
|
||||
return -1;
|
||||
if (first_type != G_FILE_TYPE_DIRECTORY && second_type == G_FILE_TYPE_DIRECTORY)
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
first_file = G_FILE (g_file_info_get_attribute_object (G_FILE_INFO (first), "standard::file"));
|
||||
second_file = G_FILE (g_file_info_get_attribute_object (G_FILE_INFO (second), "standard::file"));
|
||||
first_path = g_file_get_path (first_file);
|
||||
second_path = g_file_get_path (second_file);
|
||||
|
||||
result = strcasecmp (first_path, second_path);
|
||||
|
||||
g_free (first_path);
|
||||
g_free (second_path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_list_model_for_directory (gpointer file)
|
||||
{
|
||||
GtkSortListModel *sort;
|
||||
GtkDirectoryList *dir;
|
||||
|
||||
if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) != G_FILE_TYPE_DIRECTORY)
|
||||
return NULL;
|
||||
|
||||
dir = create_directory_list(file);
|
||||
sort = gtk_sort_list_model_new (G_LIST_MODEL (dir),
|
||||
compare_files,
|
||||
NULL, NULL);
|
||||
g_object_unref (dir);
|
||||
return G_LIST_MODEL (sort);
|
||||
}
|
||||
|
||||
typedef struct _RowData RowData;
|
||||
struct _RowData
|
||||
{
|
||||
GtkWidget *expander;
|
||||
GtkWidget *icon;
|
||||
GtkWidget *name;
|
||||
GCancellable *cancellable;
|
||||
|
||||
GtkTreeListRow *current_item;
|
||||
};
|
||||
|
||||
static void row_data_notify_item (GtkListItem *item,
|
||||
GParamSpec *pspec,
|
||||
RowData *data);
|
||||
static void
|
||||
row_data_unbind (RowData *data)
|
||||
{
|
||||
if (data->current_item == NULL)
|
||||
return;
|
||||
|
||||
if (data->cancellable)
|
||||
{
|
||||
g_cancellable_cancel (data->cancellable);
|
||||
g_clear_object (&data->cancellable);
|
||||
}
|
||||
|
||||
g_clear_object (&data->current_item);
|
||||
}
|
||||
|
||||
static void
|
||||
row_data_update_info (RowData *data,
|
||||
GFileInfo *info)
|
||||
{
|
||||
GIcon *icon;
|
||||
const char *thumbnail_path;
|
||||
|
||||
thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
|
||||
if (thumbnail_path)
|
||||
{
|
||||
/* XXX: not async */
|
||||
GFile *thumbnail_file = g_file_new_for_path (thumbnail_path);
|
||||
icon = g_file_icon_new (thumbnail_file);
|
||||
g_object_unref (thumbnail_file);
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = g_file_info_get_icon (info);
|
||||
}
|
||||
|
||||
gtk_widget_set_visible (data->icon, icon != NULL);
|
||||
gtk_image_set_from_gicon (GTK_IMAGE (data->icon), icon);
|
||||
}
|
||||
|
||||
static void
|
||||
copy_attribute (GFileInfo *to,
|
||||
GFileInfo *from,
|
||||
const gchar *attribute)
|
||||
{
|
||||
GFileAttributeType type;
|
||||
gpointer value;
|
||||
|
||||
if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL))
|
||||
g_file_info_set_attribute (to, attribute, type, value);
|
||||
}
|
||||
|
||||
static void
|
||||
row_data_got_thumbnail_info_cb (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer _data)
|
||||
{
|
||||
RowData *data = _data; /* invalid if operation was cancelled */
|
||||
GFile *file = G_FILE (source);
|
||||
GFileInfo *queried, *info;
|
||||
|
||||
queried = g_file_query_info_finish (file, res, NULL);
|
||||
if (queried == NULL)
|
||||
return;
|
||||
|
||||
/* now we know row is valid */
|
||||
|
||||
info = gtk_tree_list_row_get_item (data->current_item);
|
||||
|
||||
copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
|
||||
copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
|
||||
copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
|
||||
|
||||
g_object_unref (queried);
|
||||
|
||||
row_data_update_info (data, info);
|
||||
|
||||
g_clear_object (&data->cancellable);
|
||||
}
|
||||
|
||||
static void
|
||||
row_data_bind (RowData *data,
|
||||
GtkTreeListRow *item)
|
||||
{
|
||||
GFileInfo *info;
|
||||
|
||||
row_data_unbind (data);
|
||||
|
||||
if (item == NULL)
|
||||
return;
|
||||
|
||||
data->current_item = g_object_ref (item);
|
||||
|
||||
gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (data->expander), item);
|
||||
|
||||
info = gtk_tree_list_row_get_item (item);
|
||||
|
||||
if (!g_file_info_has_attribute (info, "filechooser::queried"))
|
||||
{
|
||||
data->cancellable = g_cancellable_new ();
|
||||
g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE);
|
||||
g_file_query_info_async (G_FILE (g_file_info_get_attribute_object (info, "standard::file")),
|
||||
G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
|
||||
G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
|
||||
G_FILE_ATTRIBUTE_STANDARD_ICON,
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
G_PRIORITY_DEFAULT,
|
||||
data->cancellable,
|
||||
row_data_got_thumbnail_info_cb,
|
||||
data);
|
||||
}
|
||||
|
||||
row_data_update_info (data, info);
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (data->name), g_file_info_get_display_name (info));
|
||||
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
static void
|
||||
row_data_notify_item (GtkListItem *item,
|
||||
GParamSpec *pspec,
|
||||
RowData *data)
|
||||
{
|
||||
row_data_bind (data, gtk_list_item_get_item (item));
|
||||
}
|
||||
|
||||
static void
|
||||
row_data_free (gpointer _data)
|
||||
{
|
||||
RowData *data = _data;
|
||||
|
||||
row_data_unbind (data);
|
||||
|
||||
g_slice_free (RowData, data);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_widget (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *box, *child;
|
||||
RowData *data;
|
||||
|
||||
data = g_slice_new0 (RowData);
|
||||
g_signal_connect (list_item, "notify::item", G_CALLBACK (row_data_notify_item), data);
|
||||
g_object_set_data_full (G_OBJECT (list_item), "row-data", data, row_data_free);
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
||||
gtk_container_add (GTK_CONTAINER (list_item), box);
|
||||
|
||||
child = gtk_label_new (NULL);
|
||||
gtk_label_set_width_chars (GTK_LABEL (child), 5);
|
||||
gtk_label_set_xalign (GTK_LABEL (child), 1.0);
|
||||
g_object_bind_property (list_item, "position", child, "label", G_BINDING_SYNC_CREATE);
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
|
||||
data->expander = gtk_tree_expander_new ();
|
||||
gtk_container_add (GTK_CONTAINER (box), data->expander);
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
||||
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (data->expander), box);
|
||||
|
||||
data->icon = gtk_image_new ();
|
||||
gtk_container_add (GTK_CONTAINER (box), data->icon);
|
||||
|
||||
data->name = gtk_label_new (NULL);
|
||||
gtk_container_add (GTK_CONTAINER (box), data->name);
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_list_model_for_file_info (gpointer file_info,
|
||||
gpointer unused)
|
||||
{
|
||||
GFile *file = G_FILE (g_file_info_get_attribute_object (file_info, "standard::file"));
|
||||
|
||||
if (file == NULL)
|
||||
return NULL;
|
||||
|
||||
return create_list_model_for_directory (file);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
update_statusbar (GtkStatusbar *statusbar)
|
||||
{
|
||||
GListModel *model = g_object_get_data (G_OBJECT (statusbar), "model");
|
||||
GString *string = g_string_new (NULL);
|
||||
guint n;
|
||||
gboolean result = G_SOURCE_REMOVE;
|
||||
|
||||
gtk_statusbar_remove_all (statusbar, 0);
|
||||
|
||||
n = g_list_model_get_n_items (model);
|
||||
g_string_append_printf (string, "%u", n);
|
||||
if (GTK_IS_FILTER_LIST_MODEL (model))
|
||||
{
|
||||
guint n_unfiltered = g_list_model_get_n_items (gtk_filter_list_model_get_model (GTK_FILTER_LIST_MODEL (model)));
|
||||
if (n != n_unfiltered)
|
||||
g_string_append_printf (string, "/%u", n_unfiltered);
|
||||
}
|
||||
g_string_append (string, " items");
|
||||
|
||||
if (pending || active)
|
||||
{
|
||||
g_string_append_printf (string, " (%u directories remaining)", active + g_slist_length (pending));
|
||||
result = G_SOURCE_CONTINUE;
|
||||
}
|
||||
result = G_SOURCE_CONTINUE;
|
||||
|
||||
gtk_statusbar_push (statusbar, 0, string->str);
|
||||
g_free (string->str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
match_file (gpointer item, gpointer data)
|
||||
{
|
||||
GtkWidget *search_entry = data;
|
||||
GFileInfo *info = gtk_tree_list_row_get_item (item);
|
||||
GFile *file = G_FILE (g_file_info_get_attribute_object (info, "standard::file"));
|
||||
char *path;
|
||||
gboolean result;
|
||||
|
||||
path = g_file_get_path (file);
|
||||
|
||||
result = strstr (path, gtk_editable_get_text (GTK_EDITABLE (search_entry))) != NULL;
|
||||
|
||||
g_object_unref (info);
|
||||
g_free (path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *win, *vbox, *sw, *listview, *search_entry, *statusbar;
|
||||
GListModel *dirmodel;
|
||||
GtkTreeListModel *tree;
|
||||
GtkFilterListModel *filter;
|
||||
FileInfoSelection *selectionmodel;
|
||||
GFile *root;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 400, 600);
|
||||
g_signal_connect (win, "destroy", G_CALLBACK (gtk_main_quit), win);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_container_add (GTK_CONTAINER (win), vbox);
|
||||
|
||||
search_entry = gtk_search_entry_new ();
|
||||
gtk_container_add (GTK_CONTAINER (vbox), search_entry);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_widget_set_vexpand (sw, TRUE);
|
||||
gtk_search_entry_set_key_capture_widget (GTK_SEARCH_ENTRY (search_entry), sw);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), sw);
|
||||
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_functions_list_item_factory_new (setup_widget,
|
||||
NULL,
|
||||
NULL, NULL));
|
||||
gtk_container_add (GTK_CONTAINER (sw), listview);
|
||||
|
||||
if (argc > 1)
|
||||
root = g_file_new_for_commandline_arg (argv[1]);
|
||||
else
|
||||
root = g_file_new_for_path (g_get_current_dir ());
|
||||
dirmodel = create_list_model_for_directory (root);
|
||||
tree = gtk_tree_list_model_new (FALSE,
|
||||
dirmodel,
|
||||
TRUE,
|
||||
create_list_model_for_file_info,
|
||||
NULL, NULL);
|
||||
g_object_unref (dirmodel);
|
||||
g_object_unref (root);
|
||||
|
||||
filter = gtk_filter_list_model_new (G_LIST_MODEL (tree),
|
||||
match_file,
|
||||
search_entry,
|
||||
NULL);
|
||||
g_signal_connect_swapped (search_entry, "search-changed", G_CALLBACK (gtk_filter_list_model_refilter), filter);
|
||||
|
||||
selectionmodel = file_info_selection_new (G_LIST_MODEL (filter));
|
||||
g_object_unref (filter);
|
||||
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (selectionmodel));
|
||||
|
||||
statusbar = gtk_statusbar_new ();
|
||||
gtk_widget_add_tick_callback (statusbar, (GtkTickCallback) update_statusbar, NULL, NULL);
|
||||
g_object_set_data (G_OBJECT (statusbar), "model", filter);
|
||||
g_signal_connect_swapped (filter, "items-changed", G_CALLBACK (update_statusbar), statusbar);
|
||||
update_statusbar (GTK_STATUSBAR (statusbar));
|
||||
gtk_container_add (GTK_CONTAINER (vbox), statusbar);
|
||||
|
||||
g_object_unref (tree);
|
||||
g_object_unref (selectionmodel);
|
||||
|
||||
gtk_widget_show (win);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user