Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f425c3b7ab | |||
| 10911bfaf0 | |||
| b36a58fa97 | |||
| f978575e13 | |||
| cabbe06c5b | |||
| f2044539ee | |||
| 165d4e8307 | |||
| 2ba06352b1 | |||
| f41f288fe7 | |||
| deb39d804d | |||
| f7f2d9ce98 | |||
| 010808794f | |||
| 6ad943c400 | |||
| 305d0aaae1 | |||
| e22f480af9 | |||
| d479c4a6d4 | |||
| e7b523bc69 | |||
| 367a711c9d | |||
| cc5808f99f | |||
| 408140bb09 | |||
| 8b75d0c50a | |||
| 1a9b2d1110 | |||
| c885a44d73 | |||
| 1499d1e68e | |||
| b2b001988f | |||
| bad5b5e8ed | |||
| a9365373bb | |||
| 51f9750339 | |||
| 20efe0e501 | |||
| 1dcf35c84d | |||
| 4ea3d89ffe | |||
| 4d610f4b1f | |||
| 4fd5f96771 | |||
| 7bddadf75f | |||
| cfde1598b5 | |||
| c1d5d3036c | |||
| 101095075c | |||
| 16be945de2 | |||
| 3a27672f95 | |||
| ed6aa467bc | |||
| ddd0218d97 | |||
| 538df82ea0 | |||
| 778a11748b | |||
| 98941cd696 | |||
| 9d33d8b92b | |||
| 5b1b4290ca | |||
| baa564a3ba | |||
| 3048d330b0 | |||
| ef1eea6071 | |||
| 50f4ad6a38 | |||
| 399c275b1c | |||
| b7bdf99f54 | |||
| eae964a546 | |||
| b326f8aed3 | |||
| aade4dd9bf | |||
| 795f9f2cb5 | |||
| 7a864583cf | |||
| 523b600119 | |||
| 8d5cff3d91 | |||
| 795cb9e43b | |||
| b9d639e34a | |||
| 1d83e50f39 | |||
| 8c960139dc | |||
| c7092d07f4 | |||
| 5e273c957b | |||
| 46feacf6b8 | |||
| 0353bb27d9 | |||
| b5385dee22 | |||
| bafa91580b | |||
| add133eb1d | |||
| 46e183f33d | |||
| 3c8323b11b | |||
| f350416c72 | |||
| 7a58bfd7ad | |||
| ade9016987 | |||
| b469c16c4e | |||
| febc5f2030 | |||
| 4530253974 | |||
| 2fd13bd601 | |||
| 89003822c3 | |||
| 0595e73a8b | |||
| 154a9dfde6 | |||
| 8f34c13dfe | |||
| f9db1c38e7 | |||
| 0c1c0242ee | |||
| 9afdd0ca3e | |||
| 79700acecb | |||
| 849f67c198 | |||
| 6f57ced025 | |||
| 54b65ce465 | |||
| 47225f0ceb | |||
| 90bb2c4133 | |||
| d440dae1b7 | |||
| f0758a2af9 | |||
| d11e075c2f | |||
| 40ecd921b0 | |||
| 5aa45a2df9 | |||
| 801a3064bc |
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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,7 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/">
|
||||
<file>awards.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/ui">
|
||||
<file preprocess="xml-stripblanks">main.ui</file>
|
||||
<file preprocess="xml-stripblanks">main-listitem.ui</file>
|
||||
<file preprocess="xml-stripblanks">appmenu.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/application_demo">
|
||||
@@ -9,6 +13,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 +118,16 @@
|
||||
<file>gnome-fs-directory.png</file>
|
||||
<file>gnome-fs-regular.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview_filebrowser">
|
||||
<file>listview_filebrowser.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview_minesweeper">
|
||||
<file>listview_minesweeper.ui</file>
|
||||
<file>listview_minesweeper_cell.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview_weather">
|
||||
<file compressed="true">listview_weather.txt</file>
|
||||
</gresource>
|
||||
<gresource prefix="/shortcuts">
|
||||
<file>shortcuts.ui</file>
|
||||
<file>shortcuts-builder.ui</file>
|
||||
@@ -147,6 +164,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 +207,9 @@
|
||||
<file>infobar.c</file>
|
||||
<file>links.c</file>
|
||||
<file>listbox.c</file>
|
||||
<file>listview_filebrowser.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>
|
||||
|
||||
@@ -28,22 +28,7 @@
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label">fps</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" bind-source="bowl" bind-property="framerate-string"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label">Icons, </property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" bind-source="bowl" bind-property="count"/>
|
||||
<binding name="label">bowl.count + " Icons, " + bowl.framerate + "fps"</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/* Lists/File browser
|
||||
*
|
||||
* This demo shows off the different layouts that are quickly achievable
|
||||
* with GtkGridView by implementing a file browser with different views.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
/* Create a simple object that holds the data for the different views */
|
||||
typedef struct _FileBrowserView FileBrowserView;
|
||||
struct _FileBrowserView
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkListItemFactory *factory;
|
||||
char *icon_name;
|
||||
GtkOrientation orientation;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_FACTORY,
|
||||
PROP_ICON_NAME,
|
||||
PROP_ORIENTATION,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
#define FILE_BROWSER_TYPE_VIEW (file_browser_view_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (FileBrowserView, file_browser_view, FILE_BROWSER, VIEW, GObject);
|
||||
|
||||
G_DEFINE_TYPE (FileBrowserView, file_browser_view, G_TYPE_OBJECT);
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
file_browser_view_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FileBrowserView *self = FILE_BROWSER_VIEW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, self->factory);
|
||||
break;
|
||||
|
||||
case PROP_ICON_NAME:
|
||||
g_value_set_string (value, self->icon_name);
|
||||
break;
|
||||
|
||||
case PROP_ORIENTATION:
|
||||
g_value_set_enum (value, self->orientation);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
file_browser_view_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FileBrowserView *self = FILE_BROWSER_VIEW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_set_object (&self->factory, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_ICON_NAME:
|
||||
g_free (self->icon_name);
|
||||
self->icon_name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_ORIENTATION:
|
||||
self->orientation = g_value_get_enum (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
file_browser_view_finalize (GObject *object)
|
||||
{
|
||||
FileBrowserView *self = FILE_BROWSER_VIEW (object);
|
||||
|
||||
g_object_unref (self->factory);
|
||||
g_free (self->icon_name);
|
||||
|
||||
G_OBJECT_CLASS (file_browser_view_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
file_browser_view_class_init (FileBrowserViewClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = file_browser_view_get_property;
|
||||
gobject_class->set_property = file_browser_view_set_property;
|
||||
gobject_class->finalize = file_browser_view_finalize;
|
||||
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
"factory",
|
||||
"factory to use in the main view",
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE);
|
||||
properties[PROP_ICON_NAME] =
|
||||
g_param_spec_string ("icon-name",
|
||||
"icon name",
|
||||
"icon to display for selecting this view",
|
||||
NULL,
|
||||
G_PARAM_READWRITE);
|
||||
properties[PROP_ORIENTATION] =
|
||||
g_param_spec_enum ("orientation",
|
||||
"orientation",
|
||||
"orientation of the view",
|
||||
GTK_TYPE_ORIENTATION,
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void file_browser_view_init (FileBrowserView *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
up_clicked_cb (GtkButton *button,
|
||||
GtkDirectoryList *list)
|
||||
{
|
||||
GFile *file;
|
||||
|
||||
file = g_file_get_parent (gtk_directory_list_get_file (list));
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
gtk_directory_list_set_file (list, file);
|
||||
}
|
||||
|
||||
static void
|
||||
view_activated_cb (GtkGridView *view,
|
||||
guint pos,
|
||||
GtkDirectoryList *list)
|
||||
{
|
||||
GFileInfo *info;
|
||||
|
||||
info = g_list_model_get_item (gtk_grid_view_get_model (view), pos);
|
||||
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
|
||||
gtk_directory_list_set_file (list, G_FILE (g_file_info_get_attribute_object (info, "standard::file")));
|
||||
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_listview_filebrowser (GtkWidget *do_widget)
|
||||
{
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *view;
|
||||
GtkBuilder *builder;
|
||||
GtkDirectoryList *dirlist;
|
||||
GFile *file;
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/listview_filebrowser/listview_filebrowser.ui");
|
||||
gtk_builder_add_callback_symbols (builder,
|
||||
"up_clicked_cb", G_CALLBACK (up_clicked_cb),
|
||||
"view_activated_cb", G_CALLBACK (view_activated_cb),
|
||||
NULL);
|
||||
gtk_builder_connect_signals (builder, NULL);
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
|
||||
/* Create the model and fill it with the contents of the current directory */
|
||||
file = g_file_new_for_path (g_get_current_dir ());
|
||||
dirlist = GTK_DIRECTORY_LIST (gtk_builder_get_object (builder, "dirlist"));
|
||||
gtk_directory_list_set_file (dirlist, file);
|
||||
g_object_unref (file);
|
||||
|
||||
/* grab focus in the view */
|
||||
view = GTK_WIDGET (gtk_builder_get_object (builder, "view"));
|
||||
gtk_widget_grab_focus (view);
|
||||
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GListStore" id="viewlist">
|
||||
<property name="item-type">FileBrowserView</property>
|
||||
<child>
|
||||
<object class="FileBrowserView">
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<binding name="gicon">file-info(GtkListItem.item:GFileInfo, "standard::icon", object):GIcon</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">file-info(GtkListItem.item:GFileInfo, "standard::display-name", string)</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="icon-name">view-list-symbolic</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="FileBrowserView">
|
||||
<property name="icon-name">view-grid-symbolic</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-size">large</property>
|
||||
<binding name="gicon">file-info(GtkListItem.item:GFileInfo, "standard::icon", object):GIcon</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="wrap">1</property>
|
||||
<property name="wrap-mode">word-char</property>
|
||||
<property name="lines">2</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="width-chars">10</property>
|
||||
<property name="max-width-chars">30</property>
|
||||
<binding name="label">file-info(GtkListItem.item:GFileInfo, "standard::display-name", string)</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="orientation">vertical</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="FileBrowserView">
|
||||
<property name="icon-name">view-paged-symbolic</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-size">large</property>
|
||||
<binding name="gicon">file-info(GtkListItem.item:GFileInfo, "standard::icon", object):GIcon</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">file-info(GtkListItem.item:GFileInfo, "standard::display-name", string)</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">file-info(GtkListItem.item:GFileInfo, "standard::size", uint64) + " bytes"</binding>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">file-info(GtkListItem.item:GFileInfo, "standard::content-type", string)</binding>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkDirectoryList" id="dirlist">
|
||||
<property name="attributes">standard::name,standard::display-name,standard::icon,standard::size,standard::content-type</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="title" translatable="yes">File browser</property>
|
||||
<property name="default-width">600</property>
|
||||
<property name="default-height">400</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="">
|
||||
<property name="show-title-buttons">1</property>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="icon-name">go-up</property>
|
||||
<signal name="clicked" handler="up_clicked_cb" object="dirlist" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkListView">
|
||||
<property name="valign">center</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
<style>
|
||||
<class name="linked"/>
|
||||
</style>
|
||||
<property name="model">
|
||||
<object class="GtkSingleSelection" id="selected-view">
|
||||
<property name="model">viewlist</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<binding name="icon-name">GtkListItem.item:FileBrowserView.icon-name</binding>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="can-focus">1</property>
|
||||
<child>
|
||||
<object class="GtkGridView" id="view">
|
||||
<property name="model">dirlist</property>
|
||||
<property name="max-columns">15</property>
|
||||
<binding name="factory">selected-view.selected-item:FileBrowserView.factory</binding>
|
||||
<binding name="orientation">selected-view.selected-item:FileBrowserView.orientation</binding>
|
||||
<signal name="activate" handler="view_activated_cb" object="dirlist" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -0,0 +1,478 @@
|
||||
/* Lists/Minesweeper
|
||||
*
|
||||
* This demo shows how to develop a user interface for small game using a
|
||||
* gridview.
|
||||
*
|
||||
* It demonstrates how to use the activate signal and single-press behavior
|
||||
* to implement rather different interaction behavior to a typical list.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/*** The cell object ***/
|
||||
|
||||
/* Create an object that holds the data for a cell in the game */
|
||||
typedef struct _SweeperCell SweeperCell;
|
||||
struct _SweeperCell
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
gboolean is_mine;
|
||||
gboolean is_visible;
|
||||
guint neighbor_mines;
|
||||
};
|
||||
|
||||
enum {
|
||||
CELL_PROP_0,
|
||||
CELL_PROP_LABEL,
|
||||
|
||||
N_CELL_PROPS
|
||||
};
|
||||
|
||||
#define SWEEPER_TYPE_CELL (sweeper_cell_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (SweeperCell, sweeper_cell, SWEEPER, CELL, GObject);
|
||||
|
||||
G_DEFINE_TYPE (SweeperCell, sweeper_cell, G_TYPE_OBJECT);
|
||||
static GParamSpec *cell_properties[N_CELL_PROPS] = { NULL, };
|
||||
|
||||
static const char *
|
||||
sweeper_cell_get_label (SweeperCell *self)
|
||||
{
|
||||
static const char *minecount_labels[10] = { "", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
|
||||
|
||||
if (!self->is_visible)
|
||||
return "?";
|
||||
|
||||
if (self->is_mine)
|
||||
return "💣";
|
||||
|
||||
return minecount_labels[self->neighbor_mines];
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SweeperCell *self = SWEEPER_CELL (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case CELL_PROP_LABEL:
|
||||
g_value_set_string (value, sweeper_cell_get_label (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_class_init (SweeperCellClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = sweeper_cell_get_property;
|
||||
|
||||
cell_properties[CELL_PROP_LABEL] =
|
||||
g_param_spec_string ("label",
|
||||
"label",
|
||||
"label to display for this row",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_CELL_PROPS, cell_properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_init (SweeperCell *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_reveal (SweeperCell *self)
|
||||
{
|
||||
if (self->is_visible)
|
||||
return;
|
||||
|
||||
self->is_visible = TRUE;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), cell_properties[CELL_PROP_LABEL]);
|
||||
}
|
||||
|
||||
static SweeperCell *
|
||||
sweeper_cell_new ()
|
||||
{
|
||||
return g_object_new (SWEEPER_TYPE_CELL, NULL);
|
||||
}
|
||||
|
||||
/*** The board object ***/
|
||||
|
||||
/* Create an object that holds the data for the game */
|
||||
typedef struct _SweeperGame SweeperGame;
|
||||
struct _SweeperGame
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GPtrArray *cells;
|
||||
guint width;
|
||||
guint height;
|
||||
gboolean playing;
|
||||
gboolean win;
|
||||
};
|
||||
|
||||
enum {
|
||||
GAME_PROP_0,
|
||||
GAME_PROP_HEIGHT,
|
||||
GAME_PROP_PLAYING,
|
||||
GAME_PROP_WIDTH,
|
||||
GAME_PROP_WIN,
|
||||
|
||||
N_GAME_PROPS
|
||||
};
|
||||
|
||||
#define SWEEPER_TYPE_GAME (sweeper_game_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (SweeperGame, sweeper_game, SWEEPER, GAME, GObject);
|
||||
|
||||
static GType
|
||||
sweeper_game_list_model_get_item_type (GListModel *model)
|
||||
{
|
||||
return SWEEPER_TYPE_GAME;
|
||||
}
|
||||
|
||||
static guint
|
||||
sweeper_game_list_model_get_n_items (GListModel *model)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (model);
|
||||
|
||||
return self->width * self->height;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
sweeper_game_list_model_get_item (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (model);
|
||||
|
||||
return g_object_ref (g_ptr_array_index (self->cells, position));
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_list_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = sweeper_game_list_model_get_item_type;
|
||||
iface->get_n_items = sweeper_game_list_model_get_n_items;
|
||||
iface->get_item = sweeper_game_list_model_get_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (SweeperGame, sweeper_game, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, sweeper_game_list_model_init))
|
||||
|
||||
static GParamSpec *game_properties[N_GAME_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
sweeper_game_dispose (GObject *object)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (object);
|
||||
|
||||
g_clear_pointer (&self->cells, g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (sweeper_game_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case GAME_PROP_HEIGHT:
|
||||
g_value_set_uint (value, self->height);
|
||||
break;
|
||||
|
||||
case GAME_PROP_PLAYING:
|
||||
g_value_set_boolean (value, self->playing);
|
||||
break;
|
||||
|
||||
case GAME_PROP_WIDTH:
|
||||
g_value_set_uint (value, self->width);
|
||||
break;
|
||||
|
||||
case GAME_PROP_WIN:
|
||||
g_value_set_boolean (value, self->win);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_class_init (SweeperGameClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = sweeper_game_dispose;
|
||||
gobject_class->get_property = sweeper_game_get_property;
|
||||
|
||||
game_properties[GAME_PROP_HEIGHT] =
|
||||
g_param_spec_uint ("height",
|
||||
"height",
|
||||
"height of the game grid",
|
||||
1, G_MAXUINT, 8,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
game_properties[GAME_PROP_PLAYING] =
|
||||
g_param_spec_boolean ("playing",
|
||||
"playing",
|
||||
"if the game is still going on",
|
||||
FALSE,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
game_properties[GAME_PROP_WIDTH] =
|
||||
g_param_spec_uint ("width",
|
||||
"width",
|
||||
"width of the game grid",
|
||||
1, G_MAXUINT, 8,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
game_properties[GAME_PROP_WIN] =
|
||||
g_param_spec_boolean ("win",
|
||||
"win",
|
||||
"if the game was won",
|
||||
FALSE,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_GAME_PROPS, game_properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_reset_board (SweeperGame *self,
|
||||
guint width,
|
||||
guint height)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_ptr_array_set_size (self->cells, 0);
|
||||
|
||||
for (i = 0; i < width * height; i++)
|
||||
{
|
||||
g_ptr_array_add (self->cells, sweeper_cell_new ());
|
||||
}
|
||||
|
||||
if (self->width != width)
|
||||
{
|
||||
self->width = width;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIDTH]);
|
||||
}
|
||||
if (self->height != height)
|
||||
{
|
||||
self->height = height;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_HEIGHT]);
|
||||
}
|
||||
if (!self->playing)
|
||||
{
|
||||
self->playing = TRUE;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_PLAYING]);
|
||||
}
|
||||
if (self->win)
|
||||
{
|
||||
self->win = FALSE;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIN]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_place_mines (SweeperGame *self,
|
||||
guint n_mines)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < n_mines; i++)
|
||||
{
|
||||
SweeperCell *cell;
|
||||
|
||||
do {
|
||||
cell = g_ptr_array_index (self->cells, g_random_int_range (0, self->cells->len));
|
||||
} while (cell->is_mine);
|
||||
|
||||
cell->is_mine = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static SweeperCell *
|
||||
get_cell (SweeperGame *self,
|
||||
guint x,
|
||||
guint y)
|
||||
{
|
||||
return g_ptr_array_index (self->cells, y * self->width + x);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_count_neighbor_mines (SweeperGame *self,
|
||||
guint width,
|
||||
guint height)
|
||||
{
|
||||
guint x, y, x2, y2;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
SweeperCell *cell = get_cell (self, x, y);
|
||||
|
||||
for (y2 = MAX (1, y) - 1; y2 < MIN (height, y + 2); y2++)
|
||||
{
|
||||
for (x2 = MAX (1, x) - 1; x2 < MIN (width, x + 2); x2++)
|
||||
{
|
||||
SweeperCell *other = get_cell (self, x2, y2);
|
||||
|
||||
if (other->is_mine)
|
||||
cell->neighbor_mines++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_new_game (SweeperGame *self,
|
||||
guint width,
|
||||
guint height,
|
||||
guint n_mines)
|
||||
{
|
||||
guint n_items_before;
|
||||
|
||||
g_return_if_fail (n_mines <= width * height);
|
||||
|
||||
n_items_before = self->width * self->height;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
sweeper_game_reset_board (self, width, height);
|
||||
sweeper_game_place_mines (self, n_mines);
|
||||
sweeper_game_count_neighbor_mines (self, width, height);
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items_before, width * height);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_init (SweeperGame *self)
|
||||
{
|
||||
self->cells = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
sweeper_game_new_game (self, 8, 8, 10);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_end (SweeperGame *self,
|
||||
gboolean win)
|
||||
{
|
||||
if (self->playing)
|
||||
{
|
||||
self->playing = FALSE;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_PLAYING]);
|
||||
}
|
||||
if (self->win != win)
|
||||
{
|
||||
self->win = win;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIN]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_check_finished (SweeperGame *self)
|
||||
{
|
||||
guint i;
|
||||
|
||||
if (!self->playing)
|
||||
return;
|
||||
|
||||
for (i = 0; i < self->cells->len; i++)
|
||||
{
|
||||
SweeperCell *cell = g_ptr_array_index (self->cells, i);
|
||||
|
||||
/* There's still a non-revealed cell that isn't a mine */
|
||||
if (!cell->is_visible && !cell->is_mine)
|
||||
return;
|
||||
}
|
||||
|
||||
sweeper_game_end (self, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_reveal_cell (SweeperGame *self,
|
||||
guint position)
|
||||
{
|
||||
SweeperCell *cell;
|
||||
|
||||
if (!self->playing)
|
||||
return;
|
||||
|
||||
cell = g_ptr_array_index (self->cells, position);
|
||||
sweeper_cell_reveal (cell);
|
||||
|
||||
if (cell->is_mine)
|
||||
sweeper_game_end (self, FALSE);
|
||||
|
||||
sweeper_game_check_finished (self);
|
||||
}
|
||||
|
||||
static void
|
||||
cell_clicked_cb (GtkGridView *gridview,
|
||||
guint pos,
|
||||
SweeperGame *game)
|
||||
{
|
||||
sweeper_game_reveal_cell (game, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
new_game_cb (GtkButton *button,
|
||||
SweeperGame *game)
|
||||
{
|
||||
sweeper_game_new_game (game, 8, 8, 10);
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_minesweeper (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
|
||||
g_type_ensure (SWEEPER_TYPE_GAME);
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/listview_minesweeper/listview_minesweeper.ui");
|
||||
gtk_builder_add_callback_symbols (builder,
|
||||
"cell_clicked_cb", G_CALLBACK (cell_clicked_cb),
|
||||
"new_game_cb", G_CALLBACK (new_game_cb),
|
||||
NULL);
|
||||
gtk_builder_connect_signals (builder, NULL);
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="SweeperGame" id="game">
|
||||
</object>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="title" translatable="yes">Minesweeper</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="">
|
||||
<property name="show-title-buttons">1</property>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="label">New Game</property>
|
||||
<signal name="clicked" handler="new_game_cb" object="game" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="title">
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">trophy-gold</property>
|
||||
<binding name="visible">game.win</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGridView" id="view">
|
||||
<property name="model">
|
||||
<object class="GtkNoSelection">
|
||||
<property name="model">game</property>
|
||||
</object>
|
||||
</property>
|
||||
<binding name="max-columns">game.width</binding>
|
||||
<binding name="min-columns">game.width</binding>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="resource">/listview_minesweeper/listview_minesweeper_cell.ui</property>
|
||||
</object>
|
||||
</property>
|
||||
<signal name="activate" handler="cell_clicked_cb" object="game" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<binding name="label">GtkListItem.item:SweeperCell.label</binding>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<child>
|
||||
<object class="GtkTreeExpander" id="expander">
|
||||
<binding name="list-row">GtkListItem.item</binding>
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">expander.item:GtkDemo.title</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<binding name="visible">!expander.list-row.expandable</binding>
|
||||
<property name="halign">start</property>
|
||||
<property name="use-markup">1</property>
|
||||
<binding name="label">"<small>" + expander.item:GtkDemo.filename + "</small>"</binding>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
+194
-171
@@ -8,6 +8,7 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "award.h"
|
||||
#include "demos.h"
|
||||
|
||||
static GtkWidget *info_view;
|
||||
@@ -15,19 +16,99 @@ 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;
|
||||
|
||||
enum {
|
||||
NAME_COLUMN,
|
||||
TITLE_COLUMN,
|
||||
FILENAME_COLUMN,
|
||||
FUNC_COLUMN,
|
||||
STYLE_COLUMN,
|
||||
NUM_COLUMNS
|
||||
typedef struct _GtkDemo GtkDemo;
|
||||
struct _GtkDemo
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
const char *name;
|
||||
const char *title;
|
||||
const char *filename;
|
||||
GDoDemoFunc func;
|
||||
GListModel *children_model;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_FILENAME,
|
||||
PROP_NAME,
|
||||
PROP_TITLE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
# define GTK_TYPE_DEMO (gtk_demo_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkDemo, gtk_demo, GTK, DEMO, GObject);
|
||||
|
||||
G_DEFINE_TYPE (GtkDemo, gtk_demo, G_TYPE_OBJECT);
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_demo_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkDemo *self = GTK_DEMO (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FILENAME:
|
||||
g_value_set_string (value, self->filename);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, self->name);
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
g_value_set_string (value, self->title);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gtk_demo_class_init (GtkDemoClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gtk_demo_get_property;
|
||||
|
||||
properties[PROP_FILENAME] =
|
||||
g_param_spec_string ("filename",
|
||||
"filename",
|
||||
"filename",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
properties[PROP_NAME] =
|
||||
g_param_spec_string ("name",
|
||||
"name",
|
||||
"name",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
properties[PROP_TITLE] =
|
||||
g_param_spec_string ("title",
|
||||
"title",
|
||||
"title",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void gtk_demo_init (GtkDemo *self)
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _CallbackData CallbackData;
|
||||
struct _CallbackData
|
||||
{
|
||||
@@ -35,6 +116,27 @@ struct _CallbackData
|
||||
GtkTreePath *path;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
gtk_demo_run (GtkDemo *self,
|
||||
GtkWidget *window)
|
||||
{
|
||||
GtkWidget *result;
|
||||
|
||||
if (!self->func)
|
||||
return FALSE;
|
||||
|
||||
result = self->func (window);
|
||||
if (result == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (GTK_IS_WINDOW (result))
|
||||
{
|
||||
gtk_window_set_transient_for (GTK_WINDOW (result), GTK_WINDOW (window));
|
||||
gtk_window_set_modal (GTK_WINDOW (result), TRUE);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
activate_about (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
@@ -111,69 +213,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 +221,10 @@ 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);
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
|
||||
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
||||
run_example_for_row (window, model, &iter);
|
||||
gtk_demo_run (demo, window);
|
||||
}
|
||||
|
||||
/* Stupid syntax highlighting.
|
||||
@@ -888,81 +924,72 @@ 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 +1009,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,34 +1035,45 @@ scrollbar_popup (GtkWidget *scrollbar, GtkWidget *menu)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
listview_activate_cb (GtkCoverFlow *listview,
|
||||
guint position,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkTreeListRow *row = g_list_model_get_item (gtk_cover_flow_get_model (listview), position);
|
||||
GtkDemo *demo = gtk_tree_list_row_get_item (row);
|
||||
|
||||
gtk_demo_run (demo, window);
|
||||
}
|
||||
|
||||
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 }
|
||||
};
|
||||
|
||||
builder = gtk_builder_new ();
|
||||
gtk_builder_add_callback_symbol (builder, "listview_activate_cb", G_CALLBACK (listview_activate_cb));
|
||||
gtk_builder_add_from_resource (builder, "/ui/main.ui", &error);
|
||||
if (error != NULL)
|
||||
{
|
||||
g_critical ("%s", error->message);
|
||||
exit (1);
|
||||
}
|
||||
gtk_builder_connect_signals (builder, NULL);
|
||||
|
||||
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 +1083,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 +1102,19 @@ 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);
|
||||
selection = gtk_single_selection_new (G_LIST_MODEL (treemodel));
|
||||
g_signal_connect (selection, "notify::selected-item", G_CALLBACK (selection_cb), NULL);
|
||||
gtk_cover_flow_set_model (GTK_COVER_FLOW (listview),
|
||||
G_LIST_MODEL (selection));
|
||||
|
||||
g_signal_connect (treeview, "row-activated", G_CALLBACK (row_activated_cb), model);
|
||||
|
||||
widget = (GtkWidget *)gtk_builder_get_object (builder, "treeview-selection");
|
||||
g_signal_connect (widget, "changed", G_CALLBACK (selection_cb), model);
|
||||
|
||||
gtk_tree_model_get_iter_first (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)), &iter);
|
||||
gtk_tree_selection_select_iter (GTK_TREE_SELECTION (widget), &iter);
|
||||
|
||||
gtk_tree_view_collapse_all (GTK_TREE_VIEW (treeview));
|
||||
award ("demo-start");
|
||||
|
||||
gtk_widget_show (GTK_WIDGET (window));
|
||||
|
||||
@@ -1108,7 +1131,7 @@ auto_quit (gpointer data)
|
||||
static void
|
||||
list_demos (void)
|
||||
{
|
||||
Demo *d, *c;
|
||||
DemoData *d, *c;
|
||||
|
||||
d = gtk_demos;
|
||||
|
||||
@@ -1135,7 +1158,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;
|
||||
|
||||
|
||||
+7
-25
@@ -28,7 +28,6 @@
|
||||
<property name="default-width">800</property>
|
||||
<property name="default-height">600</property>
|
||||
<property name="title">GTK Demo</property>
|
||||
<signal name="destroy" handler="gtk_main_quit" swapped="no"/>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="headerbar">
|
||||
<property name="show-title-buttons">1</property>
|
||||
@@ -66,32 +65,15 @@
|
||||
<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 class="GtkCoverFlow" id="listview">
|
||||
<signal name="activate" handler="listview_activate_cb" swapped="no" />
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="resource">/ui/main-listitem.ui</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="style">4</attribute>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererText">
|
||||
<property name="text"> </property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
demos = files([
|
||||
'application_demo.c',
|
||||
'assistant.c',
|
||||
'awardview.c',
|
||||
'builder.c',
|
||||
'changedisplay.c',
|
||||
'clipboard.c',
|
||||
@@ -43,6 +44,10 @@ demos = files([
|
||||
'listbox.c',
|
||||
'flowbox.c',
|
||||
'list_store.c',
|
||||
'listview_filebrowser.c',
|
||||
'listview_minesweeper.c',
|
||||
'listview_settings.c',
|
||||
'listview_weather.c',
|
||||
'markup.c',
|
||||
'menus.c',
|
||||
'modelbutton.c',
|
||||
@@ -85,6 +90,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,114 @@ 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>gtkcolumnview</FILE>
|
||||
<TITLE>GtkColumnView</TITLE>
|
||||
GtkColumnView
|
||||
gtk_column_view_new
|
||||
gtk_column_view_get_columns
|
||||
gtk_column_view_get_model
|
||||
gtk_column_view_set_model
|
||||
gtk_column_view_get_show_separators
|
||||
gtk_column_view_set_show_separators
|
||||
<SUBSECTION Standard>
|
||||
GTK_COLUMN_VIEW
|
||||
GTK_COLUMN_VIEW_CLASS
|
||||
GTK_COLUMN_VIEW_GET_CLASS
|
||||
GTK_IS_COLUMN_VIEW
|
||||
GTK_IS_COLUMN_VIEW_CLASS
|
||||
GTK_TYPE_COLUMN_VIEW
|
||||
<SUBSECTION Private>
|
||||
gtk_column_view_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkcolumnviewcolumn</FILE>
|
||||
<TITLE>GtkColumnViewColumn</TITLE>
|
||||
GtkColumnViewColumn
|
||||
gtk_column_view_column_new
|
||||
gtk_column_view_column_new_with_factory
|
||||
gtk_column_view_column_get_column_view
|
||||
gtk_column_view_column_set_factory
|
||||
gtk_column_view_column_get_factory
|
||||
gtk_column_view_column_set_title
|
||||
gtk_column_view_column_get_title
|
||||
<SUBSECTION Standard>
|
||||
GTK_COLUMN_VIEW_COLUMN
|
||||
GTK_COLUMN_VIEW_COLUMN_CLASS
|
||||
GTK_COLUMN_VIEW_COLUMN_GET_CLASS
|
||||
GTK_IS_COLUMN_VIEW_COLUMN
|
||||
GTK_IS_COLUMN_VIEW_COLUMN_CLASS
|
||||
GTK_TYPE_COLUMN_VIEW_COLUMN
|
||||
<SUBSECTION Private>
|
||||
gtk_column_view_column_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkgridview</FILE>
|
||||
<TITLE>GtkGridView</TITLE>
|
||||
GtkGridView
|
||||
gtk_grid_view_new
|
||||
gtk_grid_view_set_model
|
||||
gtk_grid_view_get_model
|
||||
gtk_grid_view_set_max_columns
|
||||
gtk_grid_view_get_max_columns
|
||||
gtk_grid_view_set_min_columns
|
||||
gtk_grid_view_get_min_columns
|
||||
<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 +1430,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 +3598,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>
|
||||
@@ -4551,6 +4705,7 @@ gtk_widget_get_allocation
|
||||
gtk_widget_get_allocated_baseline
|
||||
gtk_widget_get_width
|
||||
gtk_widget_get_height
|
||||
gtk_widget_get_size
|
||||
gtk_widget_compute_bounds
|
||||
gtk_widget_compute_transform
|
||||
gtk_widget_compute_point
|
||||
|
||||
@@ -2055,20 +2055,9 @@ get_key_repeat (GdkWaylandSeat *seat,
|
||||
}
|
||||
else
|
||||
{
|
||||
GSettings *keyboard_settings = get_keyboard_settings (seat);
|
||||
|
||||
if (keyboard_settings)
|
||||
{
|
||||
repeat = g_settings_get_boolean (keyboard_settings, "repeat");
|
||||
*delay = g_settings_get_uint (keyboard_settings, "delay");
|
||||
*interval = g_settings_get_uint (keyboard_settings, "repeat-interval");
|
||||
}
|
||||
else
|
||||
{
|
||||
repeat = TRUE;
|
||||
*delay = 400;
|
||||
*interval = 80;
|
||||
}
|
||||
repeat = TRUE;
|
||||
*delay = 400;
|
||||
*interval = 80;
|
||||
}
|
||||
|
||||
return repeat;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureSingle, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureSwipe, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureZoom, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGrid, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGridView, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkHeaderBar, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkIMContext, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkIMContextSimple, g_object_unref)
|
||||
@@ -104,6 +105,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkInfoBar, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLevelBar, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLinkButton, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListStore, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListView, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLockButton, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkMenuBar, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkMenuButton, g_object_unref)
|
||||
|
||||
@@ -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>
|
||||
@@ -81,15 +82,19 @@
|
||||
#include <gtk/gtkcolorchooserdialog.h>
|
||||
#include <gtk/gtkcolorchooserwidget.h>
|
||||
#include <gtk/gtkcolorutils.h>
|
||||
#include <gtk/gtkcolumnview.h>
|
||||
#include <gtk/gtkcolumnviewcolumn.h>
|
||||
#include <gtk/gtkcombobox.h>
|
||||
#include <gtk/gtkcomboboxtext.h>
|
||||
#include <gtk/gtkconstraintlayout.h>
|
||||
#include <gtk/gtkconstraint.h>
|
||||
#include <gtk/gtkcontainer.h>
|
||||
#include <gtk/gtkcoverflow.h>
|
||||
#include <gtk/gtkcssprovider.h>
|
||||
#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 +126,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 +140,7 @@
|
||||
#include <gtk/gtkglarea.h>
|
||||
#include <gtk/gtkgrid.h>
|
||||
#include <gtk/gtkgridlayout.h>
|
||||
#include <gtk/gtkgridview.h>
|
||||
#include <gtk/gtkheaderbar.h>
|
||||
#include <gtk/gtkicontheme.h>
|
||||
#include <gtk/gtkiconview.h>
|
||||
@@ -146,9 +153,13 @@
|
||||
#include <gtk/gtklayoutmanager.h>
|
||||
#include <gtk/gtklayoutchild.h>
|
||||
#include <gtk/gtklevelbar.h>
|
||||
#include <gtk/gtklistbase.h>
|
||||
#include <gtk/gtklinkbutton.h>
|
||||
#include <gtk/gtklistbox.h>
|
||||
#include <gtk/gtklistitem.h>
|
||||
#include <gtk/gtklistitemfactory.h>
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtklistview.h>
|
||||
#include <gtk/gtklockbutton.h>
|
||||
#include <gtk/gtkmain.h>
|
||||
#include <gtk/gtkmaplistmodel.h>
|
||||
@@ -243,6 +254,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>
|
||||
|
||||
+44
-22
@@ -209,6 +209,7 @@
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkbuilderprivate.h"
|
||||
#include "gtkdebug.h"
|
||||
#include "gtkexpressionprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
@@ -992,19 +993,9 @@ gtk_builder_apply_delayed_properties (GtkBuilder *builder)
|
||||
g_slist_free (props);
|
||||
}
|
||||
|
||||
static inline void
|
||||
free_binding_info (gpointer data,
|
||||
gpointer user)
|
||||
{
|
||||
BindingInfo *info = data;
|
||||
|
||||
g_free (info->source);
|
||||
g_free (info->source_property);
|
||||
g_slice_free (BindingInfo, data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gtk_builder_create_bindings (GtkBuilder *builder)
|
||||
static inline gboolean
|
||||
gtk_builder_create_bindings (GtkBuilder *builder,
|
||||
GError **error)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
GSList *l;
|
||||
@@ -1014,24 +1005,51 @@ gtk_builder_create_bindings (GtkBuilder *builder)
|
||||
BindingInfo *info = l->data;
|
||||
GObject *source;
|
||||
|
||||
source = _gtk_builder_lookup_object (builder, info->source, info->line, info->col);
|
||||
if (source)
|
||||
g_object_bind_property (source, info->source_property,
|
||||
info->target, info->target_pspec->name,
|
||||
info->flags);
|
||||
if (info->tag_type == TAG_BINDING)
|
||||
{
|
||||
source = _gtk_builder_lookup_object (builder, info->source, info->line, info->col);
|
||||
if (source)
|
||||
g_object_bind_property (source, info->source_property,
|
||||
info->target, info->target_pspec->name,
|
||||
info->flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkExpression *expression, *assign;
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
free_binding_info (info, NULL);
|
||||
expression = gtk_expression_parse (builder, info->source, error);
|
||||
if (expression == NULL)
|
||||
{
|
||||
g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col);
|
||||
goto fail;
|
||||
}
|
||||
assign = gtk_expression_new_assign (info->target, info->target_pspec->name, expression);
|
||||
if (gtk_expression_evaluate (assign, &value))
|
||||
g_value_unset (&value);
|
||||
gtk_expression_unref (assign);
|
||||
}
|
||||
|
||||
_free_binding_info (info, NULL);
|
||||
}
|
||||
|
||||
fail:
|
||||
for (; l; l = l->next)
|
||||
{
|
||||
_free_binding_info (l->data, NULL);
|
||||
}
|
||||
|
||||
g_slist_free (priv->bindings);
|
||||
priv->bindings = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_builder_finish (GtkBuilder *builder)
|
||||
gboolean
|
||||
_gtk_builder_finish (GtkBuilder *builder,
|
||||
GError **error)
|
||||
{
|
||||
gtk_builder_apply_delayed_properties (builder);
|
||||
gtk_builder_create_bindings (builder);
|
||||
return gtk_builder_create_bindings (builder, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2095,6 +2113,10 @@ gtk_builder_value_from_string_type (GtkBuilder *builder,
|
||||
gchar **vector = g_strsplit (string, "\n", 0);
|
||||
g_value_take_boxed (value, vector);
|
||||
}
|
||||
else if (G_VALUE_HOLDS (value, G_TYPE_BYTES))
|
||||
{
|
||||
g_value_take_boxed (value, g_bytes_new (string, strlen (string)));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_set_error (error,
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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 "gtkintl.h"
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
struct _GtkBuilderListItemFactory
|
||||
{
|
||||
GtkListItemFactory parent_instance;
|
||||
|
||||
GBytes *bytes;
|
||||
char *resource;
|
||||
};
|
||||
|
||||
struct _GtkBuilderListItemFactoryClass
|
||||
{
|
||||
GtkListItemFactoryClass parent_class;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_BYTES,
|
||||
PROP_RESOURCE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkBuilderListItemFactory, gtk_builder_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_setup (GtkListItemFactory *factory,
|
||||
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_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_BYTES:
|
||||
g_value_set_boxed (value, self->bytes);
|
||||
break;
|
||||
|
||||
case PROP_RESOURCE:
|
||||
g_value_set_string (value, self->resource);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_builder_list_item_factory_set_bytes (GtkBuilderListItemFactory *self,
|
||||
GBytes *bytes)
|
||||
{
|
||||
if (bytes == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (self->bytes)
|
||||
{
|
||||
g_critical ("Data for GtkBuilderListItemFactory has already been set.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->bytes = g_bytes_ref (bytes);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_BYTES:
|
||||
gtk_builder_list_item_factory_set_bytes (self, g_value_get_boxed (value));
|
||||
break;
|
||||
|
||||
case PROP_RESOURCE:
|
||||
{
|
||||
GError *error = NULL;
|
||||
GBytes *bytes;
|
||||
const char *resource;
|
||||
|
||||
resource = g_value_get_string (value);
|
||||
if (resource == NULL)
|
||||
break;
|
||||
|
||||
bytes = g_resources_lookup_data (resource, 0, &error);
|
||||
if (bytes)
|
||||
{
|
||||
if (gtk_builder_list_item_factory_set_bytes (self, bytes))
|
||||
self->resource = g_strdup (resource);
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_critical ("Unable to load resource for list item template: %s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_finalize (GObject *object)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
|
||||
|
||||
g_bytes_unref (self->bytes);
|
||||
g_free (self->resource);
|
||||
|
||||
G_OBJECT_CLASS (gtk_builder_list_item_factory_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_class_init (GtkBuilderListItemFactoryClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = gtk_builder_list_item_factory_finalize;
|
||||
gobject_class->get_property = gtk_builder_list_item_factory_get_property;
|
||||
gobject_class->set_property = gtk_builder_list_item_factory_set_property;
|
||||
|
||||
factory_class->setup = gtk_builder_list_item_factory_setup;
|
||||
|
||||
/**
|
||||
* GtkBuilderListItemFactory:bytes:
|
||||
*
|
||||
* bytes containing the UI definition
|
||||
*/
|
||||
properties[PROP_BYTES] =
|
||||
g_param_spec_boxed ("bytes",
|
||||
P_("Bytes"),
|
||||
P_("bytes containing the UI definition"),
|
||||
G_TYPE_BYTES,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkBuilderListItemFactory:resource:
|
||||
*
|
||||
* resource containing the UI definition
|
||||
*/
|
||||
properties[PROP_RESOURCE] =
|
||||
g_param_spec_string ("resource",
|
||||
P_("Resource"),
|
||||
P_("resource containing the UI definition"),
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_init (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkListItemFactory *
|
||||
gtk_builder_list_item_factory_new_from_bytes (GBytes *bytes)
|
||||
{
|
||||
g_return_val_if_fail (bytes != NULL, NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
|
||||
"bytes", bytes,
|
||||
NULL);
|
||||
}
|
||||
|
||||
GtkListItemFactory *
|
||||
gtk_builder_list_item_factory_new_from_resource (const char *resource_path)
|
||||
{
|
||||
g_return_val_if_fail (resource_path != NULL, NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
|
||||
"resource", resource_path,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_list_item_factory_get_bytes:
|
||||
* @self: a #GtkBuilderListItemFactory
|
||||
*
|
||||
* Gets the data used as the #GtkBuilder UI template for constructing
|
||||
* listitems.
|
||||
*
|
||||
* Returns: (transfer none): The GtkBuilder data
|
||||
*
|
||||
**/
|
||||
GBytes *
|
||||
gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
|
||||
|
||||
return self->bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_list_item_factory_get_resource:
|
||||
* @self: a #GtkBuilderListItemFactory
|
||||
*
|
||||
* If the data references a resource, gets the path of that resource.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The path to the resource or %NULL
|
||||
* if none
|
||||
**/
|
||||
const char *
|
||||
gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
|
||||
|
||||
return self->resource;
|
||||
}
|
||||
|
||||
@@ -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_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);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GBytes * gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const char * gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self) G_GNUC_PURE;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_BUILDER_LIST_ITEM_FACTORY_H__ */
|
||||
+134
-2
@@ -928,7 +928,8 @@ parse_property (ParserData *data,
|
||||
{
|
||||
BindingInfo *binfo;
|
||||
|
||||
binfo = g_slice_new (BindingInfo);
|
||||
binfo = g_slice_new0 (BindingInfo);
|
||||
binfo->tag_type = TAG_BINDING;
|
||||
binfo->target = NULL;
|
||||
binfo->target_pspec = pspec;
|
||||
binfo->source = g_strdup (bind_source);
|
||||
@@ -960,6 +961,87 @@ parse_property (ParserData *data,
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_binding (ParserData *data,
|
||||
const gchar *element_name,
|
||||
const gchar **names,
|
||||
const gchar **values,
|
||||
GError **error)
|
||||
{
|
||||
BindingInfo *info;
|
||||
const gchar *name = NULL;
|
||||
const gchar *context = NULL;
|
||||
gboolean translatable = FALSE;
|
||||
ObjectInfo *object_info;
|
||||
GParamSpec *pspec = NULL;
|
||||
gint line, col;
|
||||
|
||||
object_info = state_peek_info (data, ObjectInfo);
|
||||
if (!object_info ||
|
||||
!(object_info->tag_type == TAG_OBJECT ||
|
||||
object_info->tag_type == TAG_TEMPLATE))
|
||||
{
|
||||
error_invalid_tag (data, element_name, NULL, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_markup_collect_attributes (element_name, names, values, error,
|
||||
G_MARKUP_COLLECT_STRING, "name", &name,
|
||||
G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable,
|
||||
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &context,
|
||||
G_MARKUP_COLLECT_INVALID))
|
||||
{
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
pspec = g_object_class_find_property (object_info->oclass, name);
|
||||
|
||||
if (!pspec)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_PROPERTY,
|
||||
"Invalid property: %s.%s",
|
||||
g_type_name (object_info->type), name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
else if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_PROPERTY,
|
||||
"%s.%s is a construct-only property",
|
||||
g_type_name (object_info->type), name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
else if (!(pspec->flags & G_PARAM_WRITABLE))
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_PROPERTY,
|
||||
"%s.%s is a non-writable property",
|
||||
g_type_name (object_info->type), name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_buildable_parse_context_get_position (&data->ctx, &line, &col);
|
||||
|
||||
info = g_slice_new0 (BindingInfo);
|
||||
info->tag_type = TAG_EXPRESSION;
|
||||
info->target = NULL;
|
||||
info->target_pspec = pspec;
|
||||
info->source = NULL;
|
||||
info->flags = 0;
|
||||
info->line = line;
|
||||
info->col = col;
|
||||
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
static void
|
||||
free_property_info (PropertyInfo *info)
|
||||
{
|
||||
@@ -1052,6 +1134,16 @@ _free_signal_info (SignalInfo *info,
|
||||
g_slice_free (SignalInfo, info);
|
||||
}
|
||||
|
||||
void
|
||||
_free_binding_info (BindingInfo *info,
|
||||
gpointer user)
|
||||
{
|
||||
g_free (info->source);
|
||||
g_free (info->source_property);
|
||||
g_slice_free (BindingInfo, info);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
free_requires_info (RequiresInfo *info,
|
||||
gpointer user_data)
|
||||
@@ -1292,6 +1384,8 @@ start_element (GtkBuildableParseContext *context,
|
||||
}
|
||||
else if (strcmp (element_name, "property") == 0)
|
||||
parse_property (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "binding") == 0)
|
||||
parse_binding (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "child") == 0)
|
||||
parse_child (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "signal") == 0)
|
||||
@@ -1377,6 +1471,23 @@ end_element (GtkBuildableParseContext *context,
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
else if (strcmp (element_name, "binding") == 0)
|
||||
{
|
||||
BindingInfo *binfo = state_pop_info (data, BindingInfo);
|
||||
CommonInfo *info = state_peek_info (data, CommonInfo);
|
||||
|
||||
g_assert (info != NULL);
|
||||
|
||||
/* Normal properties */
|
||||
if (info->tag_type == TAG_OBJECT ||
|
||||
info->tag_type == TAG_TEMPLATE)
|
||||
{
|
||||
ObjectInfo *object_info = (ObjectInfo*)info;
|
||||
object_info->bindings = g_slist_prepend (object_info->bindings, binfo);
|
||||
}
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
else if (strcmp (element_name, "object") == 0 ||
|
||||
strcmp (element_name, "template") == 0)
|
||||
{
|
||||
@@ -1517,6 +1628,22 @@ text (GtkBuildableParseContext *context,
|
||||
|
||||
g_string_append_len (prop_info->text, text, text_len);
|
||||
}
|
||||
else if (strcmp (gtk_buildable_parse_context_get_element (context), "binding") == 0)
|
||||
{
|
||||
BindingInfo *binfo = (BindingInfo *) info;
|
||||
|
||||
if (binfo->source == NULL)
|
||||
{
|
||||
binfo->source = g_strndup (text, text_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *s;
|
||||
s = g_strdup_printf ("%s%*s", binfo->source, (guint) text_len, text);
|
||||
g_free (binfo->source);
|
||||
binfo->source = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1531,6 +1658,10 @@ free_info (CommonInfo *info)
|
||||
case TAG_CHILD:
|
||||
free_child_info ((ChildInfo *)info);
|
||||
break;
|
||||
case TAG_BINDING:
|
||||
case TAG_EXPRESSION:
|
||||
_free_binding_info ((BindingInfo *)info, NULL);
|
||||
break;
|
||||
case TAG_PROPERTY:
|
||||
free_property_info ((PropertyInfo *)info);
|
||||
break;
|
||||
@@ -1594,7 +1725,8 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
|
||||
if (!gtk_buildable_parse_context_parse (&data.ctx, buffer, length, error))
|
||||
goto out;
|
||||
|
||||
_gtk_builder_finish (builder);
|
||||
if (!_gtk_builder_finish (builder, error))
|
||||
goto out;
|
||||
if (_gtk_builder_lookup_failed (builder, error))
|
||||
goto out;
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
|
||||
enum {
|
||||
TAG_PROPERTY,
|
||||
TAG_MENU,
|
||||
TAG_BINDING,
|
||||
TAG_EXPRESSION,
|
||||
TAG_REQUIRES,
|
||||
TAG_OBJECT,
|
||||
TAG_CHILD,
|
||||
@@ -84,6 +85,7 @@ typedef struct {
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint tag_type;
|
||||
GObject *target;
|
||||
GParamSpec *target_pspec;
|
||||
gchar *source;
|
||||
@@ -175,9 +177,12 @@ void _gtk_builder_add (GtkBuilder *builder,
|
||||
ChildInfo *child_info);
|
||||
void _gtk_builder_add_signals (GtkBuilder *builder,
|
||||
GSList *signals);
|
||||
void _gtk_builder_finish (GtkBuilder *builder);
|
||||
gboolean _gtk_builder_finish (GtkBuilder *builder,
|
||||
GError **error);
|
||||
void _free_signal_info (SignalInfo *info,
|
||||
gpointer user_data);
|
||||
void _free_binding_info (BindingInfo *info,
|
||||
gpointer user_data);
|
||||
|
||||
/* Internal API which might be made public at some point */
|
||||
gboolean _gtk_builder_boolean_from_string (const gchar *string,
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* 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 "gtkcolumnview.h"
|
||||
|
||||
#include "gtkcolumnviewcolumn.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistview.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcolumnview
|
||||
* @title: GtkColumnView
|
||||
* @short_description: A widget for displaying lists in multiple columns
|
||||
* @see_also: #GtkColumnViewColumn, #GtkTreeView
|
||||
*
|
||||
* GtkColumnView is a widget to present a view into a large dynamic list of items
|
||||
* using multiple columns.
|
||||
*/
|
||||
|
||||
struct _GtkColumnView
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GListStore *columns;
|
||||
|
||||
GtkListView *listview;
|
||||
};
|
||||
|
||||
struct _GtkColumnViewClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_COLUMNS,
|
||||
PROP_MODEL,
|
||||
PROP_SHOW_SEPARATORS,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTIVATE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnView, gtk_column_view, GTK_TYPE_WIDGET)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
gtk_column_view_activate_cb (GtkListView *listview,
|
||||
guint pos,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
g_signal_emit (self, signals[ACTIVATE], 0, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
g_list_store_remove_all (self->columns);
|
||||
|
||||
g_clear_pointer ((GtkWidget **) &self->listview, gtk_widget_unparent);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_finalize (GObject *object)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
g_object_unref (self->columns);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_COLUMNS:
|
||||
g_value_set_object (value, self->columns);
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, gtk_list_view_get_model (self->listview));
|
||||
break;
|
||||
|
||||
case PROP_SHOW_SEPARATORS:
|
||||
g_value_set_boolean (value, gtk_list_view_get_show_separators (self->listview));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_MODEL:
|
||||
gtk_column_view_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SHOW_SEPARATORS:
|
||||
gtk_column_view_set_show_separators (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_class_init (GtkColumnViewClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gtk_column_view_dispose;
|
||||
gobject_class->finalize = gtk_column_view_finalize;
|
||||
gobject_class->get_property = gtk_column_view_get_property;
|
||||
gobject_class->set_property = gtk_column_view_set_property;
|
||||
|
||||
/**
|
||||
* GtkColumnView:columns:
|
||||
*
|
||||
* The list of columns
|
||||
*/
|
||||
properties[PROP_COLUMNS] =
|
||||
g_param_spec_object ("columns",
|
||||
P_("Columns"),
|
||||
P_("List of columns"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnView:model:
|
||||
*
|
||||
* Model for the items displayed
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("Model for the items displayed"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnView:show-separators:
|
||||
*
|
||||
* Show separators between rows
|
||||
*/
|
||||
properties[PROP_SHOW_SEPARATORS] =
|
||||
g_param_spec_boolean ("show-separators",
|
||||
P_("Show separators"),
|
||||
P_("Show separators between rows"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
* GtkColumnView::activate:
|
||||
* @self: The #GtkColumnView
|
||||
* @position: position of item to activate
|
||||
*
|
||||
* The ::activate signal is emitted when a row has been activated by the user,
|
||||
* usually via activating the GtkListBase|list.activate-item action.
|
||||
*
|
||||
* This allows for a convenient way to handle activation in a columnview.
|
||||
* See gtk_list_item_set_activatable() for details on how to use this signal.
|
||||
*/
|
||||
signals[ACTIVATE] =
|
||||
g_signal_new (I_("activate"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_UINT);
|
||||
g_signal_set_va_marshaller (signals[ACTIVATE],
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
g_cclosure_marshal_VOID__UINTv);
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("columnview"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_init (GtkColumnView *self)
|
||||
{
|
||||
self->columns = g_list_store_new (GTK_TYPE_COLUMN_VIEW_COLUMN);
|
||||
|
||||
self->listview = GTK_LIST_VIEW (gtk_list_view_new ());
|
||||
g_signal_connect (self->listview, "activate", G_CALLBACK (gtk_column_view_activate_cb), self);
|
||||
gtk_widget_set_parent (GTK_WIDGET (self->listview), GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_new:
|
||||
*
|
||||
* Creates a new empty #GtkColumnView.
|
||||
*
|
||||
* You most likely want to call gtk_column_view_set_factory() to
|
||||
* set up a way to map its items to widgets and gtk_column_view_set_model()
|
||||
* to set a model to provide items next.
|
||||
*
|
||||
* Returns: a new #GtkColumnView
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_column_view_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_COLUMN_VIEW, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_model:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Gets the model that's currently used to read the items displayed.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model in use
|
||||
**/
|
||||
GListModel *
|
||||
gtk_column_view_get_model (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
|
||||
|
||||
return gtk_list_view_get_model (self->listview);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_set_model:
|
||||
* @self: a #GtkColumnView
|
||||
* @model: (allow-none) (transfer none): the model to use or %NULL for none
|
||||
*
|
||||
* Sets the #GListModel to use.
|
||||
*
|
||||
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
|
||||
* Otherwise, @self creates a #GtkSingleSelection for the selection.
|
||||
**/
|
||||
void
|
||||
gtk_column_view_set_model (GtkColumnView *self,
|
||||
GListModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (gtk_list_view_get_model (self->listview) == model)
|
||||
return;
|
||||
|
||||
gtk_list_view_set_model (self->listview, model);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_columns:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Gets the list of columns in this column view. This list is constant over
|
||||
* the lifetime of @self and can be used to monitor changes to the columns
|
||||
* of @self by connecting to the GListModel:items-changed signal.
|
||||
*
|
||||
* Returns: (transfer none): The list managing the columns
|
||||
**/
|
||||
GListModel *
|
||||
gtk_column_view_get_columns (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
|
||||
|
||||
return G_LIST_MODEL (self->columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_set_show_separators:
|
||||
* @self: a #GtkColumnView
|
||||
* @show_separators: %TRUE to show separators
|
||||
*
|
||||
* Sets whether the list should show separators
|
||||
* between rows.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_set_show_separators (GtkColumnView *self,
|
||||
gboolean show_separators)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
|
||||
if (gtk_list_view_get_show_separators (self->listview) == show_separators)
|
||||
return;
|
||||
|
||||
gtk_list_view_set_show_separators (self->listview, show_separators);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SEPARATORS]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_show_separators:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Returns whether the list box should show separators
|
||||
* between rows.
|
||||
*
|
||||
* Returns: %TRUE if the list box shows separators
|
||||
*/
|
||||
gboolean
|
||||
gtk_column_view_get_show_separators (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), FALSE);
|
||||
|
||||
return gtk_list_view_get_show_separators (self->listview);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_H__
|
||||
#define __GTK_COLUMN_VIEW_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistbase.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW (gtk_column_view_get_type ())
|
||||
#define GTK_COLUMN_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW, GtkColumnView))
|
||||
#define GTK_COLUMN_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW, GtkColumnViewClass))
|
||||
#define GTK_IS_COLUMN_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW))
|
||||
#define GTK_IS_COLUMN_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW))
|
||||
#define GTK_COLUMN_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW, GtkColumnViewClass))
|
||||
|
||||
/**
|
||||
* GtkColumnView:
|
||||
*
|
||||
* GtkColumnView is the simple list implementation for GTK's list widgets.
|
||||
*/
|
||||
typedef struct _GtkColumnView GtkColumnView;
|
||||
typedef struct _GtkColumnViewClass GtkColumnViewClass;
|
||||
/* forward declaration */
|
||||
typedef struct _GtkColumnViewColumn GtkColumnViewColumn;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_column_view_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_column_view_new (void);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_column_view_get_columns (GtkColumnView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_column_view_get_model (GtkColumnView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_set_model (GtkColumnView *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_get_show_separators (GtkColumnView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_set_show_separators (GtkColumnView *self,
|
||||
gboolean show_Separators);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_H__ */
|
||||
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* 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 "gtkcolumnviewcolumn.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
#include "gtklistitemmanagerprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkstylecontext.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcolumnviewcolumn
|
||||
* @title: GtkColumnViewColumn
|
||||
* @short_description: The column added to GtkColumnView
|
||||
* @see_also: #GtkColumnView
|
||||
*
|
||||
* GtkColumnViewColumn represents the columns being added to #GtkColumnView.
|
||||
*/
|
||||
|
||||
struct _GtkColumnViewColumn
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkListItemFactory *factory;
|
||||
char *title;
|
||||
|
||||
/* data for the view */
|
||||
GtkColumnView *view;
|
||||
};
|
||||
|
||||
struct _GtkColumnViewColumnClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_COLUMN_VIEW,
|
||||
PROP_FACTORY,
|
||||
PROP_TITLE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnViewColumn, gtk_column_view_column, G_TYPE_OBJECT)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_column_view_column_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
|
||||
|
||||
g_assert (self->view == NULL); /* would hold a ref otherwise */
|
||||
g_clear_object (&self->factory);
|
||||
g_clear_pointer (&self->title, g_free);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_column_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_COLUMN_VIEW:
|
||||
g_value_set_object (value, self->view);
|
||||
break;
|
||||
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, self->factory);
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
g_value_set_string (value, self->title);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
gtk_column_view_column_set_factory (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
gtk_column_view_column_set_title (self, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gtk_column_view_column_dispose;
|
||||
gobject_class->get_property = gtk_column_view_column_get_property;
|
||||
gobject_class->set_property = gtk_column_view_column_set_property;
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:column-view:
|
||||
*
|
||||
* #GtkColumnView this column is a part of
|
||||
*/
|
||||
properties[PROP_COLUMN_VIEW] =
|
||||
g_param_spec_object ("column-view",
|
||||
P_("Column view"),
|
||||
P_("Column view this column is a part of"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:factory:
|
||||
*
|
||||
* Factory for populating list items
|
||||
*/
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
P_("Factory"),
|
||||
P_("Factory for populating list items"),
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:title:
|
||||
*
|
||||
* Title displayed in the header
|
||||
*/
|
||||
properties[PROP_TITLE] =
|
||||
g_param_spec_string ("title",
|
||||
P_("Title"),
|
||||
P_("Title displayed in the header"),
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_init (GtkColumnViewColumn *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_new:
|
||||
* @title: (nullable): Title to use for this column
|
||||
*
|
||||
* Creates a new #GtkColumnViewColumn.
|
||||
*
|
||||
* You most likely want to call gtk_column_add_column() next.
|
||||
*
|
||||
* Returns: a new #GtkColumnViewColumn
|
||||
**/
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_column_new (const char *title)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_COLUMN_VIEW_COLUMN,
|
||||
"title", title,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_new_with_factory:
|
||||
* @title: (nullable): Title to use for this column
|
||||
* @factory: (transfer full): The factory to populate items with
|
||||
*
|
||||
* Creates a new #GtkColumnViewColumn that uses the given @factory for
|
||||
* mapping items to widgets.
|
||||
*
|
||||
* You most likely want to call gtk_column_add_column() next.
|
||||
*
|
||||
* The function takes ownership of the
|
||||
* argument, so you can write code like
|
||||
* ```
|
||||
* column = gtk_column_view_column_new_with_factory (_("Name"),
|
||||
* gtk_builder_list_item_factory_new_from_resource ("/name.ui"));
|
||||
* ```
|
||||
*
|
||||
* Returns: a new #GtkColumnViewColumn using the given @factory
|
||||
**/
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_column_new_with_factory (const char *title,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
GtkColumnViewColumn *result;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_COLUMN_VIEW_COLUMN,
|
||||
"factory", factory,
|
||||
"title", title,
|
||||
NULL);
|
||||
|
||||
g_object_unref (factory);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_column_view:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Gets the column view that's currently displaying this column.
|
||||
*
|
||||
* If @self has not been added to a column view yet, %NULL is returned.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The column view displaying @self.
|
||||
**/
|
||||
GtkColumnView *
|
||||
gtk_column_view_column_get_column_view (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
|
||||
|
||||
return self->view;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_factory:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Gets the factory that's currently used to populate list items for
|
||||
* this column.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The factory in use
|
||||
**/
|
||||
GtkListItemFactory *
|
||||
gtk_column_view_column_get_factory (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
|
||||
|
||||
return self->factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_factory:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
|
||||
*
|
||||
* Sets the #GtkListItemFactory to use for populating list items for this
|
||||
* column.
|
||||
**/
|
||||
void
|
||||
gtk_column_view_column_set_factory (GtkColumnViewColumn *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
|
||||
|
||||
if (!g_set_object (&self->factory, factory))
|
||||
return;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_title:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @title: (nullable): Title to use for this column
|
||||
*
|
||||
* Sets the title of this column. The title is displayed in the header of a
|
||||
* #GtkColumnView for this column and is therefor user-facing text that should
|
||||
* be translated.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_column_set_title (GtkColumnViewColumn *self,
|
||||
const char *title)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
|
||||
if (g_strcmp0 (self->title, title) == 0)
|
||||
return;
|
||||
|
||||
g_free (self->title);
|
||||
self->title = g_strdup (title);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_title:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Returns the title set with gtk_column_view_column_set_title().
|
||||
*
|
||||
* Returns: (nullable) The column's title
|
||||
*/
|
||||
const char *
|
||||
gtk_column_view_column_get_title (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), FALSE);
|
||||
|
||||
return self->title;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_COLUMN_H__
|
||||
#define __GTK_COLUMN_VIEW_COLUMN_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkcolumnview.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW_COLUMN (gtk_column_view_column_get_type ())
|
||||
#define GTK_COLUMN_VIEW_COLUMN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumn))
|
||||
#define GTK_COLUMN_VIEW_COLUMN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumnClass))
|
||||
#define GTK_IS_COLUMN_VIEW_COLUMN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_COLUMN))
|
||||
#define GTK_IS_COLUMN_VIEW_COLUMN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_COLUMN))
|
||||
#define GTK_COLUMN_VIEW_COLUMN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumnClass))
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:
|
||||
*
|
||||
* GtkColumnViewColumns are added to #GtkColumnViews.
|
||||
*/
|
||||
typedef struct _GtkColumnViewColumnClass GtkColumnViewColumnClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_column_view_column_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkColumnViewColumn * gtk_column_view_column_new (const char *title);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkColumnViewColumn * gtk_column_view_column_new_with_factory (const char *title,
|
||||
GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkColumnView * gtk_column_view_column_get_column_view (GtkColumnViewColumn *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_factory (GtkColumnViewColumn *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory * gtk_column_view_column_get_factory (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_title (GtkColumnViewColumn *self,
|
||||
const char *title);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const char * gtk_column_view_column_get_title (GtkColumnViewColumn *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */
|
||||
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcoverflow.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/* Extra items to display at most above + below the central item */
|
||||
#define GTK_COVER_FLOW_DISPLAY_ITEMS 5
|
||||
|
||||
/* Number of extra space we leave around the covers */
|
||||
#define GTK_COVER_FLOW_SCALE_ALONG 3
|
||||
#define GTK_COVER_FLOW_SCALE_ACROSS 2
|
||||
|
||||
/**
|
||||
* SECTION:gtkcoverflow
|
||||
* @title: GtkCoverFlow
|
||||
* @short_description: A coverflow list widget
|
||||
* @see_also: #GListModel
|
||||
*
|
||||
* GtkCoverFlow is a widget to present a coverflow list
|
||||
*/
|
||||
|
||||
struct _GtkCoverFlow
|
||||
{
|
||||
GtkListBase parent_instance;
|
||||
|
||||
int size_across;
|
||||
int size_along;
|
||||
};
|
||||
|
||||
struct _GtkCoverFlowClass
|
||||
{
|
||||
GtkListBaseClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_FACTORY,
|
||||
PROP_MODEL,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTIVATE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkCoverFlow, gtk_cover_flow, GTK_TYPE_LIST_BASE)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static gboolean
|
||||
gtk_cover_flow_get_allocation_along (GtkListBase *base,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (base);
|
||||
|
||||
if (offset)
|
||||
*offset = pos * self->size_along;
|
||||
if (size)
|
||||
*size = self->size_along;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_cover_flow_get_allocation_across (GtkListBase *base,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (base);
|
||||
|
||||
if (offset)
|
||||
*offset = pos * self->size_across;
|
||||
if (size)
|
||||
*size = self->size_across;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_cover_flow_move_focus_along (GtkListBase *base,
|
||||
guint pos,
|
||||
int steps)
|
||||
{
|
||||
if (steps < 0)
|
||||
return pos - MIN (pos, -steps);
|
||||
else
|
||||
{
|
||||
pos += MIN (gtk_list_base_get_n_items (base) - pos - 1, steps);
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_cover_flow_move_focus_across (GtkListBase *base,
|
||||
guint pos,
|
||||
int steps)
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_cover_flow_get_position_from_allocation (GtkListBase *base,
|
||||
int across,
|
||||
int along,
|
||||
guint *pos,
|
||||
cairo_rectangle_int_t *area)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (base);
|
||||
|
||||
if (across >= self->size_across ||
|
||||
along >= self->size_along * gtk_list_base_get_n_items (base))
|
||||
return FALSE;
|
||||
|
||||
*pos = along / self->size_along;
|
||||
|
||||
if (area)
|
||||
{
|
||||
area->x = 0;
|
||||
area->width = self->size_across;
|
||||
area->y = *pos * self->size_along;
|
||||
area->height = self->size_along;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure_children (GtkCoverFlow *self,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkListItemManagerItem *item;
|
||||
int min, nat, child_min, child_nat;
|
||||
|
||||
min = 0;
|
||||
nat = 0;
|
||||
|
||||
for (item = gtk_list_item_manager_get_first (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
|
||||
item != NULL;
|
||||
item = gtk_rb_tree_node_get_next (item))
|
||||
{
|
||||
/* ignore unavailable items */
|
||||
if (item->widget == NULL)
|
||||
continue;
|
||||
|
||||
gtk_widget_measure (item->widget,
|
||||
orientation, for_size,
|
||||
&child_min, &child_nat, NULL, NULL);
|
||||
min = MAX (min, child_min);
|
||||
nat = MAX (nat, child_nat);
|
||||
}
|
||||
|
||||
*minimum = min;
|
||||
*natural = nat;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure_across (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
|
||||
if (for_size > 0)
|
||||
for_size /= GTK_COVER_FLOW_SCALE_ALONG;
|
||||
|
||||
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
|
||||
|
||||
*minimum *= GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
*natural *= GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure_along (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
|
||||
if (for_size > 0)
|
||||
for_size /= GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
|
||||
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
|
||||
|
||||
*minimum *= GTK_COVER_FLOW_SCALE_ALONG;
|
||||
*natural *= GTK_COVER_FLOW_SCALE_ALONG;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
|
||||
if (orientation == gtk_list_base_get_orientation (GTK_LIST_BASE (self)))
|
||||
gtk_cover_flow_measure_along (widget, orientation, for_size, minimum, natural);
|
||||
else
|
||||
gtk_cover_flow_measure_across (widget, orientation, for_size, minimum, natural);
|
||||
}
|
||||
|
||||
static GskTransform *
|
||||
transform_translate_oriented (GskTransform *transform,
|
||||
GtkOrientation orientation,
|
||||
GtkTextDirection dir,
|
||||
float across,
|
||||
float along)
|
||||
{
|
||||
if (orientation == GTK_ORIENTATION_VERTICAL)
|
||||
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (across, along));
|
||||
else if (dir == GTK_TEXT_DIR_LTR)
|
||||
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (along, across));
|
||||
else
|
||||
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-along, across));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_size_allocate_child (GtkWidget *child,
|
||||
GtkOrientation orientation,
|
||||
GtkTextDirection dir,
|
||||
GskTransform *transform,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
if (orientation == GTK_ORIENTATION_VERTICAL)
|
||||
{
|
||||
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-width / 2, -height / 2));
|
||||
gtk_widget_allocate (child, width, height, -1, transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-height / 2, -width / 2));
|
||||
gtk_widget_allocate (child, height, width, -1, transform);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
GtkOrientation orientation, opposite_orientation;
|
||||
GtkTextDirection dir;
|
||||
GtkListItemManagerItem *item;
|
||||
GtkListItemManager *manager;
|
||||
guint i, pos;
|
||||
int x, y, along, across;
|
||||
|
||||
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
|
||||
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
|
||||
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
|
||||
|
||||
/* step 0: exit early if list is empty */
|
||||
if (gtk_list_item_manager_get_root (manager) == NULL)
|
||||
return;
|
||||
|
||||
/* step 1: determine size of children */
|
||||
along = orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
|
||||
across = opposite_orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
|
||||
self->size_along = along / GTK_COVER_FLOW_SCALE_ALONG;
|
||||
self->size_across = across / GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
|
||||
/* step 2: update the adjustments */
|
||||
gtk_list_base_update_adjustments (GTK_LIST_BASE (self),
|
||||
self->size_across,
|
||||
self->size_along * gtk_list_base_get_n_items (GTK_LIST_BASE (self)),
|
||||
self->size_across,
|
||||
self->size_along,
|
||||
&x, &y);
|
||||
pos = gtk_list_base_get_anchor (GTK_LIST_BASE (self));
|
||||
|
||||
/* step 4: actually allocate the widgets */
|
||||
dir = _gtk_widget_get_direction (widget);
|
||||
i = 0;
|
||||
|
||||
for (item = gtk_list_item_manager_get_first (manager);
|
||||
item != NULL;
|
||||
item = gtk_rb_tree_node_get_next (item))
|
||||
{
|
||||
if (item->widget)
|
||||
{
|
||||
/* start at the center */
|
||||
GskTransform *transform = transform_translate_oriented (NULL, orientation, dir, across / 2, along / 2);
|
||||
|
||||
if (i == pos)
|
||||
{
|
||||
/* nothing to do, we're already centered */
|
||||
}
|
||||
else if (MAX (pos, i) - MIN (pos, i) < GTK_COVER_FLOW_DISPLAY_ITEMS) /* ABS() doesn't work with guint */
|
||||
{
|
||||
int diff = i - pos;
|
||||
transform = gsk_transform_perspective (transform, MAX (width, height) * 2);
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
0,
|
||||
diff * self->size_along / 4);
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
0,
|
||||
(diff < 0 ? -1 : 1) * self->size_along / 2);
|
||||
if (orientation == GTK_ORIENTATION_VERTICAL)
|
||||
transform = gsk_transform_rotate_3d (transform,
|
||||
diff > 0 ? 60 : -60,
|
||||
graphene_vec3_x_axis ());
|
||||
else
|
||||
transform = gsk_transform_rotate_3d (transform,
|
||||
diff < 0 ? 60 : -60,
|
||||
graphene_vec3_y_axis ());
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
0,
|
||||
(diff < 0 ? 1 : -1) * self->size_along / 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
- 2 * self->size_across,
|
||||
- 2 * self->size_along);
|
||||
}
|
||||
gtk_cover_flow_size_allocate_child (item->widget,
|
||||
orientation, dir,
|
||||
transform,
|
||||
self->size_across,
|
||||
self->size_along);
|
||||
}
|
||||
|
||||
i += item->n_items;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListBase *base = GTK_LIST_BASE (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, gtk_list_item_manager_get_factory (gtk_list_base_get_manager (base)));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, gtk_list_base_get_model (base));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkWidget *child;
|
||||
GtkListItemManagerItem *item;
|
||||
|
||||
item = gtk_list_item_manager_get_nth (gtk_list_base_get_manager (GTK_LIST_BASE (widget)),
|
||||
gtk_list_base_get_anchor (GTK_LIST_BASE (widget)),
|
||||
NULL);
|
||||
if (item == NULL || item->widget == NULL)
|
||||
{
|
||||
GTK_WIDGET_CLASS (gtk_cover_flow_parent_class)->snapshot (widget, snapshot);
|
||||
return;
|
||||
}
|
||||
|
||||
for (child = _gtk_widget_get_first_child (widget);
|
||||
child != item->widget;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
gtk_widget_snapshot_child (widget, child, snapshot);
|
||||
|
||||
for (child = _gtk_widget_get_last_child (widget);
|
||||
child != item->widget;
|
||||
child = _gtk_widget_get_prev_sibling (child))
|
||||
gtk_widget_snapshot_child (widget, child, snapshot);
|
||||
|
||||
gtk_widget_snapshot_child (widget, item->widget, snapshot);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
gtk_cover_flow_set_factory (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_cover_flow_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_activate_item (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
guint pos;
|
||||
|
||||
if (!g_variant_check_format_string (parameter, "u", FALSE))
|
||||
return;
|
||||
|
||||
g_variant_get (parameter, "u", &pos);
|
||||
if (pos >= gtk_list_base_get_n_items (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
g_signal_emit (widget, signals[ACTIVATE], 0, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_class_init (GtkCoverFlowClass *klass)
|
||||
{
|
||||
GtkListBaseClass *list_base_class = GTK_LIST_BASE_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
list_base_class->list_item_name = "cover";
|
||||
list_base_class->list_item_size = sizeof (GtkListItemManagerItem);
|
||||
list_base_class->list_item_augment_size = sizeof (GtkListItemManagerItemAugment);
|
||||
list_base_class->list_item_augment_func = gtk_list_item_manager_augment_node;
|
||||
list_base_class->get_allocation_along = gtk_cover_flow_get_allocation_along;
|
||||
list_base_class->get_allocation_across = gtk_cover_flow_get_allocation_across;
|
||||
list_base_class->get_position_from_allocation = gtk_cover_flow_get_position_from_allocation;
|
||||
list_base_class->move_focus_along = gtk_cover_flow_move_focus_along;
|
||||
list_base_class->move_focus_across = gtk_cover_flow_move_focus_across;
|
||||
|
||||
widget_class->measure = gtk_cover_flow_measure;
|
||||
widget_class->size_allocate = gtk_cover_flow_size_allocate;
|
||||
widget_class->snapshot = gtk_cover_flow_snapshot;
|
||||
|
||||
gobject_class->get_property = gtk_cover_flow_get_property;
|
||||
gobject_class->set_property = gtk_cover_flow_set_property;
|
||||
|
||||
/**
|
||||
* GtkCoverFlow:factory:
|
||||
*
|
||||
* Factory for populating list items
|
||||
*/
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
P_("Factory"),
|
||||
P_("Factory for populating list items"),
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkCoverFlow:model:
|
||||
*
|
||||
* Model for the items displayed
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("Model for the items displayed"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
* GtkCoverFlow::activate:
|
||||
* @self: The #GtkCoverFlow
|
||||
* @position: position of item to activate
|
||||
*
|
||||
* The ::activate signal is emitted when an item has been activated by the user,
|
||||
* usually via activating the GtkCoverFlow|list.activate-item action.
|
||||
*
|
||||
* This allows for a convenient way to handle activation in a listview.
|
||||
* See gtk_list_item_set_activatable() for details on how to use this signal.
|
||||
*/
|
||||
signals[ACTIVATE] =
|
||||
g_signal_new (I_("activate"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_UINT);
|
||||
g_signal_set_va_marshaller (signals[ACTIVATE],
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
g_cclosure_marshal_VOID__UINTv);
|
||||
|
||||
/**
|
||||
* GtkCoverFlow|list.activate-item:
|
||||
* @position: position of item to activate
|
||||
*
|
||||
* Activates the item given in @position by emitting the GtkCoverFlow::activate
|
||||
* signal.
|
||||
*/
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"list.activate-item",
|
||||
"u",
|
||||
gtk_cover_flow_activate_item);
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("coverflow"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_init (GtkCoverFlow *self)
|
||||
{
|
||||
gtk_list_base_set_anchor_max_widgets (GTK_LIST_BASE (self),
|
||||
0,
|
||||
GTK_COVER_FLOW_DISPLAY_ITEMS);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_new:
|
||||
*
|
||||
* Creates a new empty #GtkCoverFlow.
|
||||
*
|
||||
* You most likely want to call gtk_cover_flow_set_factory() to
|
||||
* set up a way to map its items to widgets and gtk_cover_flow_set_model()
|
||||
* to set a model to provide items next.
|
||||
*
|
||||
* Returns: a new #GtkCoverFlow
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_cover_flow_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_COVER_FLOW, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_new_with_factory:
|
||||
* @factory: (transfer full): The factory to populate items with
|
||||
*
|
||||
* Creates a new #GtkCoverFlow that uses the given @factory for
|
||||
* mapping items to widgets.
|
||||
*
|
||||
* You most likely want to call gtk_cover_flow_set_model() to set
|
||||
* a model next.
|
||||
*
|
||||
* The function takes ownership of the
|
||||
* argument, so you can write code like
|
||||
* ```
|
||||
* cover_flow = gtk_cover_flow_new_with_factory (
|
||||
* gtk_builder_list_item_factory_newfrom_resource ("/resource.ui"));
|
||||
* ```
|
||||
*
|
||||
* Returns: a new #GtkCoverFlow using the given @factory
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_cover_flow_new_with_factory (GtkListItemFactory *factory)
|
||||
{
|
||||
GtkWidget *result;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_COVER_FLOW,
|
||||
"factory", factory,
|
||||
NULL);
|
||||
|
||||
g_object_unref (factory);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_get_model:
|
||||
* @self: a #GtkCoverFlow
|
||||
*
|
||||
* Gets the model that's currently used to read the items displayed.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model in use
|
||||
**/
|
||||
GListModel *
|
||||
gtk_cover_flow_get_model (GtkCoverFlow *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
|
||||
|
||||
return gtk_list_base_get_model (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_set_model:
|
||||
* @self: a #GtkCoverFlow
|
||||
* @model: (allow-none) (transfer none): the model to use or %NULL for none
|
||||
*
|
||||
* Sets the #GListModel to use.
|
||||
*
|
||||
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
|
||||
* Otherwise, @self creates a #GtkSingleSelection for the selection.
|
||||
**/
|
||||
void
|
||||
gtk_cover_flow_set_model (GtkCoverFlow *self,
|
||||
GListModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COVER_FLOW (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (!gtk_list_base_set_model (GTK_LIST_BASE (self), model))
|
||||
return;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_get_factory:
|
||||
* @self: a #GtkCoverFlow
|
||||
*
|
||||
* Gets the factory that's currently used to populate list items.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The factory in use
|
||||
**/
|
||||
GtkListItemFactory *
|
||||
gtk_cover_flow_get_factory (GtkCoverFlow *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
|
||||
|
||||
return gtk_list_item_manager_get_factory (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_set_factory:
|
||||
* @self: a #GtkCoverFlow
|
||||
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
|
||||
*
|
||||
* Sets the #GtkListItemFactory to use for populating list items.
|
||||
**/
|
||||
void
|
||||
gtk_cover_flow_set_factory (GtkCoverFlow *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
GtkListItemManager *manager;
|
||||
|
||||
g_return_if_fail (GTK_IS_COVER_FLOW (self));
|
||||
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
|
||||
|
||||
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
|
||||
|
||||
if (factory == gtk_list_item_manager_get_factory (manager))
|
||||
return;
|
||||
|
||||
gtk_list_item_manager_set_factory (manager, factory);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COVER_FLOW_H__
|
||||
#define __GTK_COVER_FLOW_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistbase.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COVER_FLOW (gtk_cover_flow_get_type ())
|
||||
#define GTK_COVER_FLOW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COVER_FLOW, GtkCoverFlow))
|
||||
#define GTK_COVER_FLOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COVER_FLOW, GtkCoverFlowClass))
|
||||
#define GTK_IS_COVER_FLOW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COVER_FLOW))
|
||||
#define GTK_IS_COVER_FLOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COVER_FLOW))
|
||||
#define GTK_COVER_FLOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COVER_FLOW, GtkCoverFlowClass))
|
||||
|
||||
/**
|
||||
* GtkCoverFlow:
|
||||
*
|
||||
* GtkCoverFlow is the simple list implementation for GTK's list widgets.
|
||||
*/
|
||||
typedef struct _GtkCoverFlow GtkCoverFlow;
|
||||
typedef struct _GtkCoverFlowClass GtkCoverFlowClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_cover_flow_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_cover_flow_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_cover_flow_new_with_factory (GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_cover_flow_get_model (GtkCoverFlow *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_cover_flow_set_model (GtkCoverFlow *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_cover_flow_set_factory (GtkCoverFlow *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory *
|
||||
gtk_cover_flow_get_factory (GtkCoverFlow *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COVER_FLOW_H__ */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
+1699
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GTK_EXPRESSION_PRIVATE_H__
|
||||
#define __GTK_EXPRESSION_PRIVATE_H__
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GtkExpression GtkExpression;
|
||||
|
||||
#define GTK_IS_EXPRESSION(expr) ((expr) != NULL)
|
||||
|
||||
GtkExpression * gtk_expression_new_assign (GObject *object,
|
||||
const char *property,
|
||||
GtkExpression *expr);
|
||||
|
||||
GtkExpression * gtk_expression_ref (GtkExpression *self);
|
||||
void gtk_expression_unref (GtkExpression *self);
|
||||
|
||||
void gtk_expression_print (GtkExpression *self,
|
||||
GString *string);
|
||||
char * gtk_expression_to_string (GtkExpression *self);
|
||||
GtkExpression * gtk_expression_parse (GtkBuilder *scope,
|
||||
const char *string,
|
||||
GError **error);
|
||||
|
||||
GType gtk_expression_get_value_type (GtkExpression *self);
|
||||
gboolean gtk_expression_evaluate (GtkExpression *self,
|
||||
GValue *value);
|
||||
|
||||
char * gtk_expression_value_to_string (const GValue *value);
|
||||
gboolean gtk_expression_value_to_boolean (const GValue *value);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_EXPRESSION_PRIVATE_H__ */
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
+1292
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_GRID_VIEW_H__
|
||||
#define __GTK_GRID_VIEW_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistbase.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_GRID_VIEW (gtk_grid_view_get_type ())
|
||||
#define GTK_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_GRID_VIEW, GtkGridView))
|
||||
#define GTK_GRID_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_GRID_VIEW, GtkGridViewClass))
|
||||
#define GTK_IS_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_GRID_VIEW))
|
||||
#define GTK_IS_GRID_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_GRID_VIEW))
|
||||
#define GTK_GRID_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_GRID_VIEW, GtkGridViewClass))
|
||||
|
||||
/**
|
||||
* GtkGridView:
|
||||
*
|
||||
* GtkGridView is a list widget implementation that arranges its items in
|
||||
* a grid.
|
||||
*/
|
||||
typedef struct _GtkGridView GtkGridView;
|
||||
typedef struct _GtkGridViewClass GtkGridViewClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_grid_view_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_grid_view_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_grid_view_new_with_factory (GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_grid_view_get_model (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_model (GtkGridView *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_factory (GtkGridView *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory *
|
||||
gtk_grid_view_get_factory (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_grid_view_get_min_columns (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_min_columns (GtkGridView *self,
|
||||
guint min_columns);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_grid_view_get_max_columns (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_max_columns (GtkGridView *self,
|
||||
guint max_columns);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_GRID_VIEW_H__ */
|
||||
+1542
File diff suppressed because it is too large
Load Diff
@@ -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_LIST_BASE_H__
|
||||
#define __GTK_LIST_BASE_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include <gtk/gtklistitem.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_BASE (gtk_list_base_get_type ())
|
||||
#define GTK_LIST_BASE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_BASE, GtkListBase))
|
||||
#define GTK_LIST_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_BASE, GtkListBaseClass))
|
||||
#define GTK_IS_LIST_BASE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_BASE))
|
||||
#define GTK_IS_LIST_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_BASE))
|
||||
#define GTK_LIST_BASE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_BASE, GtkListBaseClass))
|
||||
|
||||
/**
|
||||
* GtkListBase:
|
||||
*
|
||||
* GtkListBase is the abstract base class for GTK's list widgets.
|
||||
*/
|
||||
typedef struct _GtkListBase GtkListBase;
|
||||
typedef struct _GtkListBaseClass GtkListBaseClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_list_base_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_BASE_H__ */
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_BASE_PRIVATE_H__
|
||||
#define __GTK_LIST_BASE_PRIVATE_H__
|
||||
|
||||
#include "gtklistbase.h"
|
||||
|
||||
#include "gtklistitemmanagerprivate.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
struct _GtkListBase
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
};
|
||||
|
||||
struct _GtkListBaseClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
|
||||
const char * list_item_name;
|
||||
gsize list_item_size;
|
||||
gsize list_item_augment_size;
|
||||
GtkRbTreeAugmentFunc list_item_augment_func;
|
||||
|
||||
void (* adjustment_value_changed) (GtkListBase *self,
|
||||
GtkOrientation orientation);
|
||||
gboolean (* get_allocation_along) (GtkListBase *self,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size);
|
||||
gboolean (* get_allocation_across) (GtkListBase *self,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size);
|
||||
gboolean (* get_position_from_allocation) (GtkListBase *self,
|
||||
int across,
|
||||
int along,
|
||||
guint *pos,
|
||||
cairo_rectangle_int_t *area);
|
||||
guint (* move_focus_along) (GtkListBase *self,
|
||||
guint pos,
|
||||
int steps);
|
||||
guint (* move_focus_across) (GtkListBase *self,
|
||||
guint pos,
|
||||
int steps);
|
||||
};
|
||||
|
||||
GtkOrientation gtk_list_base_get_orientation (GtkListBase *self);
|
||||
#define gtk_list_base_get_opposite_orientation(self) OPPOSITE_ORIENTATION(gtk_list_base_get_orientation(self))
|
||||
guint gtk_list_base_get_focus_position (GtkListBase *self);
|
||||
GtkListItemManager * gtk_list_base_get_manager (GtkListBase *self);
|
||||
GtkScrollablePolicy gtk_list_base_get_scroll_policy (GtkListBase *self,
|
||||
GtkOrientation orientation);
|
||||
guint gtk_list_base_get_n_items (GtkListBase *self);
|
||||
GListModel * gtk_list_base_get_model (GtkListBase *self);
|
||||
gboolean gtk_list_base_set_model (GtkListBase *self,
|
||||
GListModel *model);
|
||||
void gtk_list_base_update_adjustments (GtkListBase *self,
|
||||
int total_across,
|
||||
int total_along,
|
||||
int page_across,
|
||||
int page_along,
|
||||
int *across,
|
||||
int *along);
|
||||
|
||||
guint gtk_list_base_get_anchor (GtkListBase *self);
|
||||
void gtk_list_base_set_anchor (GtkListBase *self,
|
||||
guint anchor_pos,
|
||||
double anchor_align_across,
|
||||
GtkPackType anchor_side_across,
|
||||
double anchor_align_along,
|
||||
GtkPackType anchor_side_along);
|
||||
void gtk_list_base_set_anchor_max_widgets (GtkListBase *self,
|
||||
guint n_center,
|
||||
guint n_above_below);
|
||||
void gtk_list_base_select_item (GtkListBase *self,
|
||||
guint pos,
|
||||
gboolean modify,
|
||||
gboolean extend);
|
||||
gboolean gtk_list_base_grab_focus_on_item (GtkListBase *self,
|
||||
guint pos,
|
||||
gboolean select,
|
||||
gboolean modify,
|
||||
gboolean extend);
|
||||
|
||||
#endif /* __GTK_LIST_BASE_PRIVATE_H__ */
|
||||
@@ -0,0 +1,727 @@
|
||||
/*
|
||||
* 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 "gtkbindings.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 activatable : 1;
|
||||
guint selectable : 1;
|
||||
guint selected : 1;
|
||||
};
|
||||
|
||||
struct _GtkListItemClass
|
||||
{
|
||||
GtkBinClass parent_class;
|
||||
|
||||
void (* activate_signal) (GtkListItem *self);
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_ACTIVATABLE,
|
||||
PROP_ITEM,
|
||||
PROP_POSITION,
|
||||
PROP_SELECTABLE,
|
||||
PROP_SELECTED,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ACTIVATE_SIGNAL,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkListItem, gtk_list_item, GTK_TYPE_BIN)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
gtk_list_item_activate_signal (GtkListItem *self)
|
||||
{
|
||||
if (!self->activatable)
|
||||
return;
|
||||
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.activate-item",
|
||||
"u",
|
||||
self->position);
|
||||
}
|
||||
|
||||
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_is_focus (widget))
|
||||
return FALSE;
|
||||
|
||||
if (!gtk_widget_get_can_focus (widget) ||
|
||||
!self->selectable)
|
||||
return FALSE;
|
||||
|
||||
return gtk_widget_grab_focus (widget);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_item_grab_focus (GtkWidget *widget)
|
||||
{
|
||||
GtkWidget *child;
|
||||
|
||||
child = gtk_bin_get_child (GTK_BIN (widget));
|
||||
if (child && gtk_widget_grab_focus (child))
|
||||
return TRUE;
|
||||
|
||||
return GTK_WIDGET_CLASS (gtk_list_item_parent_class)->grab_focus (widget);
|
||||
}
|
||||
|
||||
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_ACTIVATABLE:
|
||||
g_value_set_boolean (value, self->activatable);
|
||||
break;
|
||||
|
||||
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_ACTIVATABLE:
|
||||
gtk_list_item_set_activatable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_SELECTABLE:
|
||||
gtk_list_item_set_selectable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_select_action (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (widget);
|
||||
gboolean modify, extend;
|
||||
|
||||
if (!self->selectable)
|
||||
return;
|
||||
|
||||
g_variant_get (parameter, "(bb)", &modify, &extend);
|
||||
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.select-item",
|
||||
"(ubb)",
|
||||
self->position, modify, extend);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_class_init (GtkListItemClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GtkBindingSet *binding_set;
|
||||
|
||||
klass->activate_signal = gtk_list_item_activate_signal;
|
||||
|
||||
widget_class->focus = gtk_list_item_focus;
|
||||
widget_class->grab_focus = gtk_list_item_grab_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:activatable:
|
||||
*
|
||||
* If the item can be activated by the user
|
||||
*/
|
||||
properties[PROP_ACTIVATABLE] =
|
||||
g_param_spec_boolean ("activatable",
|
||||
P_("Activatable"),
|
||||
P_("If the item can be activated by the user"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem: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);
|
||||
|
||||
/**
|
||||
* GtkListItem::activate-signal:
|
||||
*
|
||||
* This is a keybinding signal, which will cause this row to be activated.
|
||||
*
|
||||
* Do not use it, it is an implementation detail.
|
||||
*
|
||||
* If you want to be notified when the user activates a listitem (by key or not),
|
||||
* look at the list widget this item is contained in.
|
||||
*/
|
||||
signals[ACTIVATE_SIGNAL] =
|
||||
g_signal_new (I_("activate-keybinding"),
|
||||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET (GtkListItemClass, activate_signal),
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
widget_class->activate_signal = signals[ACTIVATE_SIGNAL];
|
||||
|
||||
/**
|
||||
* GtkListItem|listitem.select:
|
||||
* @modify: %TRUE to toggle the existing selection, %FALSE to select
|
||||
* @extend: %TRUE to extend the selection
|
||||
*
|
||||
* Changes selection if the item is selectable.
|
||||
* If the item is not selectable, nothing happens.
|
||||
*
|
||||
* This function will emit the list.select-item action and the resulting
|
||||
* behavior, in particular the interpretation of @modify and @extend
|
||||
* depends on the view containing this listitem. See for example
|
||||
* GtkListView|list.select-item or GtkGridView|list.select-item.
|
||||
*/
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"listitem.select",
|
||||
"(bb)",
|
||||
gtk_list_item_select_action);
|
||||
|
||||
binding_set = gtk_binding_set_by_class (klass);
|
||||
|
||||
gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0,
|
||||
"activate-keybinding", 0);
|
||||
gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0,
|
||||
"activate-keybinding", 0);
|
||||
gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0,
|
||||
"activate-keybinding", 0);
|
||||
|
||||
/* note that some of these may get overwritten by child widgets,
|
||||
* such as GtkTreeExpander */
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, 0,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, 0,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
|
||||
/* 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);
|
||||
|
||||
if (!self->selectable && !self->activatable)
|
||||
{
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->selectable)
|
||||
{
|
||||
GdkModifierType state;
|
||||
GdkModifierType mask;
|
||||
gboolean extend = FALSE, modify = FALSE;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (self->activatable)
|
||||
{
|
||||
if (n_press == 2)
|
||||
{
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.activate-item",
|
||||
"u",
|
||||
self->position);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE);
|
||||
|
||||
if (gtk_widget_get_focus_on_click (widget))
|
||||
gtk_widget_grab_focus (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_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;
|
||||
self->activatable = 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]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_activatable:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Checks if a list item has been set to be activatable via
|
||||
* gtk_list_item_set_activatable().
|
||||
*
|
||||
* Returns: %TRUE if the item is activatable
|
||||
**/
|
||||
gboolean
|
||||
gtk_list_item_get_activatable (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
|
||||
|
||||
return self->activatable;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_set_activatable:
|
||||
* @self: a #GtkListItem
|
||||
* @activatable: if the item should be activatable
|
||||
*
|
||||
* Sets @self to be activatable.
|
||||
*
|
||||
* If an item is activatable, double-clicking on the item, using
|
||||
* the <Return> key or calling gtk_widget_activate() will activate
|
||||
* the item. Activating instructs the containing view to handle
|
||||
* activation. #GtkListView for example will be emitting the
|
||||
* GtkListView::activate signal.
|
||||
*
|
||||
* By default, list items are activatable
|
||||
**/
|
||||
void
|
||||
gtk_list_item_set_activatable (GtkListItem *self,
|
||||
gboolean activatable)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
|
||||
if (self->activatable == activatable)
|
||||
return;
|
||||
|
||||
self->activatable = activatable;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVATABLE]);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM (gtk_list_item_get_type ())
|
||||
#define GTK_LIST_ITEM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM, GtkListItem))
|
||||
#define GTK_LIST_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM, GtkListItemClass))
|
||||
#define GTK_IS_LIST_ITEM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM))
|
||||
#define GTK_IS_LIST_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM))
|
||||
#define GTK_LIST_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM, GtkListItemClass))
|
||||
|
||||
typedef struct _GtkListItemClass GtkListItemClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_list_item_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gpointer gtk_list_item_get_item (GtkListItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_list_item_get_position (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_selected (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_selectable (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_item_set_selectable (GtkListItem *self,
|
||||
gboolean selectable);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_activatable (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_item_set_activatable (GtkListItem *self,
|
||||
gboolean activatable);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_H__ */
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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__ */
|
||||
@@ -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__ */
|
||||
+47
-17
@@ -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);
|
||||
|
||||
|
||||
@@ -0,0 +1,975 @@
|
||||
/*
|
||||
* 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 "gtklistview.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
#include "gtklistitemmanagerprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkstylecontext.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/* Maximum number of list items created by the listview.
|
||||
* For debugging, you can set this to G_MAXUINT to ensure
|
||||
* there's always a list item for every row.
|
||||
*/
|
||||
#define GTK_LIST_VIEW_MAX_LIST_ITEMS 200
|
||||
|
||||
/* Extra items to keep above + below every tracker */
|
||||
#define GTK_LIST_VIEW_EXTRA_ITEMS 2
|
||||
|
||||
/**
|
||||
* SECTION:gtklistview
|
||||
* @title: GtkListView
|
||||
* @short_description: A widget for displaying lists
|
||||
* @see_also: #GListModel
|
||||
*
|
||||
* GtkListView is a widget to present a view into a large dynamic list of items.
|
||||
*/
|
||||
|
||||
typedef struct _ListRow ListRow;
|
||||
typedef struct _ListRowAugment ListRowAugment;
|
||||
|
||||
struct _GtkListView
|
||||
{
|
||||
GtkListBase parent_instance;
|
||||
|
||||
GtkListItemManager *item_manager;
|
||||
gboolean show_separators;
|
||||
|
||||
int list_width;
|
||||
};
|
||||
|
||||
struct _GtkListViewClass
|
||||
{
|
||||
GtkListBaseClass parent_class;
|
||||
};
|
||||
|
||||
struct _ListRow
|
||||
{
|
||||
GtkListItemManagerItem parent;
|
||||
guint height; /* per row */
|
||||
};
|
||||
|
||||
struct _ListRowAugment
|
||||
{
|
||||
GtkListItemManagerItemAugment parent;
|
||||
guint height; /* total */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_FACTORY,
|
||||
PROP_MODEL,
|
||||
PROP_SHOW_SEPARATORS,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTIVATE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkListView, gtk_list_view, GTK_TYPE_LIST_BASE)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void G_GNUC_UNUSED
|
||||
dump (GtkListView *self)
|
||||
{
|
||||
ListRow *row;
|
||||
guint n_widgets, n_list_rows;
|
||||
|
||||
n_widgets = 0;
|
||||
n_list_rows = 0;
|
||||
//g_print ("ANCHOR: %u - %u\n", self->anchor_start, self->anchor_end);
|
||||
for (row = gtk_list_item_manager_get_first (self->item_manager);
|
||||
row;
|
||||
row = gtk_rb_tree_node_get_next (row))
|
||||
{
|
||||
if (row->parent.widget)
|
||||
n_widgets++;
|
||||
n_list_rows++;
|
||||
g_print (" %4u%s (%upx)\n", row->parent.n_items, row->parent.widget ? " (widget)" : "", row->height);
|
||||
}
|
||||
|
||||
g_print (" => %u widgets in %u list rows\n", n_widgets, n_list_rows);
|
||||
}
|
||||
|
||||
static void
|
||||
list_row_augment (GtkRbTree *tree,
|
||||
gpointer node_augment,
|
||||
gpointer node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
ListRow *row = node;
|
||||
ListRowAugment *aug = node_augment;
|
||||
|
||||
gtk_list_item_manager_augment_node (tree, node_augment, node, left, right);
|
||||
|
||||
aug->height = row->height * row->parent.n_items;
|
||||
|
||||
if (left)
|
||||
{
|
||||
ListRowAugment *left_aug = gtk_rb_tree_get_augment (tree, left);
|
||||
|
||||
aug->height += left_aug->height;
|
||||
}
|
||||
|
||||
if (right)
|
||||
{
|
||||
ListRowAugment *right_aug = gtk_rb_tree_get_augment (tree, right);
|
||||
|
||||
aug->height += right_aug->height;
|
||||
}
|
||||
}
|
||||
|
||||
static ListRow *
|
||||
gtk_list_view_get_row_at_y (GtkListView *self,
|
||||
int y,
|
||||
int *offset)
|
||||
{
|
||||
ListRow *row, *tmp;
|
||||
|
||||
row = gtk_list_item_manager_get_root (self->item_manager);
|
||||
|
||||
while (row)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_left (row);
|
||||
if (tmp)
|
||||
{
|
||||
ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, tmp);
|
||||
if (y < aug->height)
|
||||
{
|
||||
row = tmp;
|
||||
continue;
|
||||
}
|
||||
y -= aug->height;
|
||||
}
|
||||
|
||||
if (y < row->height * row->parent.n_items)
|
||||
break;
|
||||
y -= row->height * row->parent.n_items;
|
||||
|
||||
row = gtk_rb_tree_node_get_right (row);
|
||||
}
|
||||
|
||||
if (offset)
|
||||
*offset = row ? y : 0;
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
static int
|
||||
list_row_get_y (GtkListView *self,
|
||||
ListRow *row)
|
||||
{
|
||||
ListRow *parent, *left;
|
||||
int y;
|
||||
|
||||
left = gtk_rb_tree_node_get_left (row);
|
||||
if (left)
|
||||
{
|
||||
ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, left);
|
||||
y = aug->height;
|
||||
}
|
||||
else
|
||||
y = 0;
|
||||
|
||||
for (parent = gtk_rb_tree_node_get_parent (row);
|
||||
parent != NULL;
|
||||
parent = gtk_rb_tree_node_get_parent (row))
|
||||
{
|
||||
left = gtk_rb_tree_node_get_left (parent);
|
||||
|
||||
if (left != row)
|
||||
{
|
||||
if (left)
|
||||
{
|
||||
ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, left);
|
||||
y += aug->height;
|
||||
}
|
||||
y += parent->height * parent->parent.n_items;
|
||||
}
|
||||
|
||||
row = parent;
|
||||
}
|
||||
|
||||
return y ;
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_list_view_get_list_height (GtkListView *self)
|
||||
{
|
||||
ListRow *row;
|
||||
ListRowAugment *aug;
|
||||
|
||||
row = gtk_list_item_manager_get_root (self->item_manager);
|
||||
if (row == NULL)
|
||||
return 0;
|
||||
|
||||
aug = gtk_list_item_manager_get_item_augment (self->item_manager, row);
|
||||
return aug->height;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_view_get_allocation_along (GtkListBase *base,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (base);
|
||||
ListRow *row;
|
||||
guint skip;
|
||||
int y;
|
||||
|
||||
row = gtk_list_item_manager_get_nth (self->item_manager, pos, &skip);
|
||||
if (row == NULL)
|
||||
{
|
||||
if (offset)
|
||||
*offset = 0;
|
||||
if (size)
|
||||
*size = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
y = list_row_get_y (self, row);
|
||||
y += skip * row->height;
|
||||
|
||||
if (offset)
|
||||
*offset = y;
|
||||
if (size)
|
||||
*size = row->height;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_view_get_allocation_across (GtkListBase *base,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (base);
|
||||
|
||||
if (offset)
|
||||
*offset = 0;
|
||||
if (size)
|
||||
*size = self->list_width;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_list_view_move_focus_along (GtkListBase *base,
|
||||
guint pos,
|
||||
int steps)
|
||||
{
|
||||
if (steps < 0)
|
||||
return pos - MIN (pos, -steps);
|
||||
else
|
||||
{
|
||||
pos += MIN (gtk_list_base_get_n_items (base) - pos - 1, steps);
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_view_get_position_from_allocation (GtkListBase *base,
|
||||
int across,
|
||||
int along,
|
||||
guint *pos,
|
||||
cairo_rectangle_int_t *area)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (base);
|
||||
ListRow *row;
|
||||
int remaining;
|
||||
|
||||
if (across >= self->list_width)
|
||||
return FALSE;
|
||||
|
||||
row = gtk_list_view_get_row_at_y (self, along, &remaining);
|
||||
if (row == NULL)
|
||||
return FALSE;
|
||||
|
||||
*pos = gtk_list_item_manager_get_item_position (self->item_manager, row);
|
||||
g_assert (remaining < row->height * row->parent.n_items);
|
||||
*pos += remaining / row->height;
|
||||
|
||||
if (area)
|
||||
{
|
||||
area->x = 0;
|
||||
area->width = self->list_width;
|
||||
area->y = along - remaining % row->height;
|
||||
area->height = row->height;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_list_view_move_focus_across (GtkListBase *base,
|
||||
guint pos,
|
||||
int steps)
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int
|
||||
compare_ints (gconstpointer first,
|
||||
gconstpointer second)
|
||||
{
|
||||
return *(int *) first - *(int *) second;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_list_view_get_unknown_row_height (GtkListView *self,
|
||||
GArray *heights)
|
||||
{
|
||||
g_return_val_if_fail (heights->len > 0, 0);
|
||||
|
||||
/* return the median and hope rows are generally uniform with few outliers */
|
||||
g_array_sort (heights, compare_ints);
|
||||
|
||||
return g_array_index (heights, int, heights->len / 2);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_measure_across (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (widget);
|
||||
ListRow *row;
|
||||
int min, nat, child_min, child_nat;
|
||||
/* XXX: Figure out how to split a given height into per-row heights.
|
||||
* Good luck! */
|
||||
for_size = -1;
|
||||
|
||||
min = 0;
|
||||
nat = 0;
|
||||
|
||||
for (row = gtk_list_item_manager_get_first (self->item_manager);
|
||||
row != NULL;
|
||||
row = gtk_rb_tree_node_get_next (row))
|
||||
{
|
||||
/* ignore unavailable rows */
|
||||
if (row->parent.widget == NULL)
|
||||
continue;
|
||||
|
||||
gtk_widget_measure (row->parent.widget,
|
||||
orientation, for_size,
|
||||
&child_min, &child_nat, NULL, NULL);
|
||||
min = MAX (min, child_min);
|
||||
nat = MAX (nat, child_nat);
|
||||
}
|
||||
|
||||
*minimum = min;
|
||||
*natural = nat;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_measure_list (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (widget);
|
||||
ListRow *row;
|
||||
int min, nat, child_min, child_nat;
|
||||
GArray *min_heights, *nat_heights;
|
||||
guint n_unknown;
|
||||
|
||||
min_heights = g_array_new (FALSE, FALSE, sizeof (int));
|
||||
nat_heights = g_array_new (FALSE, FALSE, sizeof (int));
|
||||
n_unknown = 0;
|
||||
min = 0;
|
||||
nat = 0;
|
||||
|
||||
for (row = gtk_list_item_manager_get_first (self->item_manager);
|
||||
row != NULL;
|
||||
row = gtk_rb_tree_node_get_next (row))
|
||||
{
|
||||
if (row->parent.widget)
|
||||
{
|
||||
gtk_widget_measure (row->parent.widget,
|
||||
orientation, for_size,
|
||||
&child_min, &child_nat, NULL, NULL);
|
||||
g_array_append_val (min_heights, child_min);
|
||||
g_array_append_val (nat_heights, child_nat);
|
||||
min += child_min;
|
||||
nat += child_nat;
|
||||
}
|
||||
else
|
||||
{
|
||||
n_unknown += row->parent.n_items;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_unknown)
|
||||
{
|
||||
min += n_unknown * gtk_list_view_get_unknown_row_height (self, min_heights);
|
||||
nat += n_unknown * gtk_list_view_get_unknown_row_height (self, nat_heights);
|
||||
}
|
||||
g_array_free (min_heights, TRUE);
|
||||
g_array_free (nat_heights, TRUE);
|
||||
|
||||
*minimum = min;
|
||||
*natural = nat;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (widget);
|
||||
|
||||
if (orientation == gtk_list_base_get_orientation (GTK_LIST_BASE (self)))
|
||||
gtk_list_view_measure_list (widget, orientation, for_size, minimum, natural);
|
||||
else
|
||||
gtk_list_view_measure_across (widget, orientation, for_size, minimum, natural);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_size_allocate_child (GtkListView *self,
|
||||
GtkWidget *child,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GtkAllocation child_allocation;
|
||||
|
||||
if (gtk_list_base_get_orientation (GTK_LIST_BASE (self)) == GTK_ORIENTATION_VERTICAL)
|
||||
{
|
||||
child_allocation.x = x;
|
||||
child_allocation.y = y;
|
||||
child_allocation.width = width;
|
||||
child_allocation.height = height;
|
||||
}
|
||||
else if (_gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_LTR)
|
||||
{
|
||||
child_allocation.x = y;
|
||||
child_allocation.y = x;
|
||||
child_allocation.width = height;
|
||||
child_allocation.height = width;
|
||||
}
|
||||
else
|
||||
{
|
||||
int mirror_point = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
|
||||
child_allocation.x = mirror_point - y - height;
|
||||
child_allocation.y = x;
|
||||
child_allocation.width = height;
|
||||
child_allocation.height = width;
|
||||
}
|
||||
|
||||
gtk_widget_size_allocate (child, &child_allocation, -1);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (widget);
|
||||
ListRow *row;
|
||||
GArray *heights;
|
||||
int min, nat, row_height;
|
||||
int x, y;
|
||||
GtkOrientation orientation, opposite_orientation;
|
||||
GtkScrollablePolicy scroll_policy;
|
||||
|
||||
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
|
||||
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
|
||||
scroll_policy = gtk_list_base_get_scroll_policy (GTK_LIST_BASE (self), orientation);
|
||||
|
||||
/* step 0: exit early if list is empty */
|
||||
if (gtk_list_item_manager_get_root (self->item_manager) == NULL)
|
||||
return;
|
||||
|
||||
/* step 1: determine width of the list */
|
||||
gtk_widget_measure (widget, opposite_orientation,
|
||||
-1,
|
||||
&min, &nat, NULL, NULL);
|
||||
self->list_width = orientation == GTK_ORIENTATION_VERTICAL ? width : height;
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
self->list_width = MAX (min, self->list_width);
|
||||
else
|
||||
self->list_width = MAX (nat, self->list_width);
|
||||
|
||||
/* step 2: determine height of known list items */
|
||||
heights = g_array_new (FALSE, FALSE, sizeof (int));
|
||||
|
||||
for (row = gtk_list_item_manager_get_first (self->item_manager);
|
||||
row != NULL;
|
||||
row = gtk_rb_tree_node_get_next (row))
|
||||
{
|
||||
if (row->parent.widget == NULL)
|
||||
continue;
|
||||
|
||||
gtk_widget_measure (row->parent.widget, orientation,
|
||||
self->list_width,
|
||||
&min, &nat, NULL, NULL);
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
row_height = min;
|
||||
else
|
||||
row_height = nat;
|
||||
if (row->height != row_height)
|
||||
{
|
||||
row->height = row_height;
|
||||
gtk_rb_tree_node_mark_dirty (row);
|
||||
}
|
||||
g_array_append_val (heights, row_height);
|
||||
}
|
||||
|
||||
/* step 3: determine height of unknown items */
|
||||
row_height = gtk_list_view_get_unknown_row_height (self, heights);
|
||||
g_array_free (heights, TRUE);
|
||||
|
||||
for (row = gtk_list_item_manager_get_first (self->item_manager);
|
||||
row != NULL;
|
||||
row = gtk_rb_tree_node_get_next (row))
|
||||
{
|
||||
if (row->parent.widget)
|
||||
continue;
|
||||
|
||||
if (row->height != row_height)
|
||||
{
|
||||
row->height = row_height;
|
||||
gtk_rb_tree_node_mark_dirty (row);
|
||||
}
|
||||
}
|
||||
|
||||
/* step 3: update the adjustments */
|
||||
gtk_list_base_update_adjustments (GTK_LIST_BASE (self),
|
||||
self->list_width,
|
||||
gtk_list_view_get_list_height (self),
|
||||
gtk_widget_get_size (widget, opposite_orientation),
|
||||
gtk_widget_get_size (widget, orientation),
|
||||
&x, &y);
|
||||
x = -x;
|
||||
y = -y;
|
||||
|
||||
/* step 4: actually allocate the widgets */
|
||||
|
||||
for (row = gtk_list_item_manager_get_first (self->item_manager);
|
||||
row != NULL;
|
||||
row = gtk_rb_tree_node_get_next (row))
|
||||
{
|
||||
if (row->parent.widget)
|
||||
{
|
||||
gtk_list_view_size_allocate_child (self,
|
||||
row->parent.widget,
|
||||
x,
|
||||
y,
|
||||
self->list_width,
|
||||
row->height);
|
||||
}
|
||||
|
||||
y += row->height * row->parent.n_items;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_dispose (GObject *object)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (object);
|
||||
|
||||
self->item_manager = NULL;
|
||||
|
||||
G_OBJECT_CLASS (gtk_list_view_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, gtk_list_item_manager_get_factory (self->item_manager));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, gtk_list_base_get_model (GTK_LIST_BASE (self)));
|
||||
break;
|
||||
|
||||
case PROP_SHOW_SEPARATORS:
|
||||
g_value_set_boolean (value, self->show_separators);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
gtk_list_view_set_factory (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_list_view_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SHOW_SEPARATORS:
|
||||
gtk_list_view_set_show_separators (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_activate_item (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (widget);
|
||||
guint pos;
|
||||
|
||||
if (!g_variant_check_format_string (parameter, "u", FALSE))
|
||||
return;
|
||||
|
||||
g_variant_get (parameter, "u", &pos);
|
||||
if (pos >= gtk_list_base_get_n_items (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
g_signal_emit (widget, signals[ACTIVATE], 0, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_class_init (GtkListViewClass *klass)
|
||||
{
|
||||
GtkListBaseClass *list_base_class = GTK_LIST_BASE_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
list_base_class->list_item_name = "row";
|
||||
list_base_class->list_item_size = sizeof (ListRow);
|
||||
list_base_class->list_item_augment_size = sizeof (ListRowAugment);
|
||||
list_base_class->list_item_augment_func = list_row_augment;
|
||||
list_base_class->get_allocation_along = gtk_list_view_get_allocation_along;
|
||||
list_base_class->get_allocation_across = gtk_list_view_get_allocation_across;
|
||||
list_base_class->get_position_from_allocation = gtk_list_view_get_position_from_allocation;
|
||||
list_base_class->move_focus_along = gtk_list_view_move_focus_along;
|
||||
list_base_class->move_focus_across = gtk_list_view_move_focus_across;
|
||||
|
||||
widget_class->measure = gtk_list_view_measure;
|
||||
widget_class->size_allocate = gtk_list_view_size_allocate;
|
||||
|
||||
gobject_class->dispose = gtk_list_view_dispose;
|
||||
gobject_class->get_property = gtk_list_view_get_property;
|
||||
gobject_class->set_property = gtk_list_view_set_property;
|
||||
|
||||
/**
|
||||
* GtkListView:factory:
|
||||
*
|
||||
* Factory for populating list items
|
||||
*/
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
P_("Factory"),
|
||||
P_("Factory for populating list items"),
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListView:model:
|
||||
*
|
||||
* Model for the items displayed
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("Model for the items displayed"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListView:show-separators:
|
||||
*
|
||||
* Show separators between rows
|
||||
*/
|
||||
properties[PROP_SHOW_SEPARATORS] =
|
||||
g_param_spec_boolean ("show-separators",
|
||||
P_("Show separators"),
|
||||
P_("Show separators between rows"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
* GtkListView::activate:
|
||||
* @self: The #GtkListView
|
||||
* @position: position of item to activate
|
||||
*
|
||||
* The ::activate signal is emitted when a row has been activated by the user,
|
||||
* usually via activating the GtkListView|list.activate-item action.
|
||||
*
|
||||
* This allows for a convenient way to handle activation in a listview.
|
||||
* See gtk_list_item_set_activatable() for details on how to use this signal.
|
||||
*/
|
||||
signals[ACTIVATE] =
|
||||
g_signal_new (I_("activate"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_UINT);
|
||||
g_signal_set_va_marshaller (signals[ACTIVATE],
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
g_cclosure_marshal_VOID__UINTv);
|
||||
|
||||
/**
|
||||
* GtkListView|list.activate-item:
|
||||
* @position: position of item to activate
|
||||
*
|
||||
* Activates the item given in @position by emitting the GtkListView::activate
|
||||
* signal.
|
||||
*/
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"list.activate-item",
|
||||
"u",
|
||||
gtk_list_view_activate_item);
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("list"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_init (GtkListView *self)
|
||||
{
|
||||
self->item_manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
|
||||
|
||||
gtk_list_base_set_anchor_max_widgets (GTK_LIST_BASE (self),
|
||||
GTK_LIST_VIEW_MAX_LIST_ITEMS,
|
||||
GTK_LIST_VIEW_EXTRA_ITEMS);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_new:
|
||||
*
|
||||
* Creates a new empty #GtkListView.
|
||||
*
|
||||
* You most likely want to call gtk_list_view_set_factory() to
|
||||
* set up a way to map its items to widgets and gtk_list_view_set_model()
|
||||
* to set a model to provide items next.
|
||||
*
|
||||
* Returns: a new #GtkListView
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_list_view_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_LIST_VIEW, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_new_with_factory:
|
||||
* @factory: (transfer full): The factory to populate items with
|
||||
*
|
||||
* Creates a new #GtkListView that uses the given @factory for
|
||||
* mapping items to widgets.
|
||||
*
|
||||
* You most likely want to call gtk_list_view_set_model() to set
|
||||
* a model next.
|
||||
*
|
||||
* The function takes ownership of the
|
||||
* argument, so you can write code like
|
||||
* ```
|
||||
* list_view = gtk_list_view_new_with_factory (
|
||||
* gtk_builder_list_item_factory_newfrom_resource ("/resource.ui"));
|
||||
* ```
|
||||
*
|
||||
* Returns: a new #GtkListView using the given @factory
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_list_view_new_with_factory (GtkListItemFactory *factory)
|
||||
{
|
||||
GtkWidget *result;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_LIST_VIEW,
|
||||
"factory", factory,
|
||||
NULL);
|
||||
|
||||
g_object_unref (factory);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_get_model:
|
||||
* @self: a #GtkListView
|
||||
*
|
||||
* Gets the model that's currently used to read the items displayed.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model in use
|
||||
**/
|
||||
GListModel *
|
||||
gtk_list_view_get_model (GtkListView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), NULL);
|
||||
|
||||
return gtk_list_base_get_model (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_set_model:
|
||||
* @self: a #GtkListView
|
||||
* @model: (allow-none) (transfer none): the model to use or %NULL for none
|
||||
*
|
||||
* Sets the #GListModel to use.
|
||||
*
|
||||
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
|
||||
* Otherwise, @self creates a #GtkSingleSelection for the selection.
|
||||
**/
|
||||
void
|
||||
gtk_list_view_set_model (GtkListView *self,
|
||||
GListModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_VIEW (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (!gtk_list_base_set_model (GTK_LIST_BASE (self), model))
|
||||
return;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_get_factory:
|
||||
* @self: a #GtkListView
|
||||
*
|
||||
* Gets the factory that's currently used to populate list items.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The factory in use
|
||||
**/
|
||||
GtkListItemFactory *
|
||||
gtk_list_view_get_factory (GtkListView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), NULL);
|
||||
|
||||
return gtk_list_item_manager_get_factory (self->item_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_set_factory:
|
||||
* @self: a #GtkListView
|
||||
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
|
||||
*
|
||||
* Sets the #GtkListItemFactory to use for populating list items.
|
||||
**/
|
||||
void
|
||||
gtk_list_view_set_factory (GtkListView *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_VIEW (self));
|
||||
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
|
||||
|
||||
if (factory == gtk_list_item_manager_get_factory (self->item_manager))
|
||||
return;
|
||||
|
||||
gtk_list_item_manager_set_factory (self->item_manager, factory);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_set_show_separators:
|
||||
* @self: a #GtkListView
|
||||
* @show_separators: %TRUE to show separators
|
||||
*
|
||||
* Sets whether the list box should show separators
|
||||
* between rows.
|
||||
*/
|
||||
void
|
||||
gtk_list_view_set_show_separators (GtkListView *self,
|
||||
gboolean show_separators)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_VIEW (self));
|
||||
|
||||
if (self->show_separators == show_separators)
|
||||
return;
|
||||
|
||||
self->show_separators = show_separators;
|
||||
|
||||
if (show_separators)
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "separators");
|
||||
else
|
||||
gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "separators");
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SEPARATORS]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_get_show_separators:
|
||||
* @self: a #GtkListView
|
||||
*
|
||||
* Returns whether the list box should show separators
|
||||
* between rows.
|
||||
*
|
||||
* Returns: %TRUE if the list box shows separators
|
||||
*/
|
||||
gboolean
|
||||
gtk_list_view_get_show_separators (GtkListView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), FALSE);
|
||||
|
||||
return self->show_separators;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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/gtklistbase.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_VIEW (gtk_list_view_get_type ())
|
||||
#define GTK_LIST_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_VIEW, GtkListView))
|
||||
#define GTK_LIST_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_VIEW, GtkListViewClass))
|
||||
#define GTK_IS_LIST_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_VIEW))
|
||||
#define GTK_IS_LIST_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_VIEW))
|
||||
#define GTK_LIST_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_VIEW, GtkListViewClass))
|
||||
|
||||
/**
|
||||
* GtkListView:
|
||||
*
|
||||
* GtkListView is the simple list implementation for GTK's list widgets.
|
||||
*/
|
||||
typedef struct _GtkListView GtkListView;
|
||||
typedef struct _GtkListViewClass GtkListViewClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_list_view_get_type (void) G_GNUC_CONST;
|
||||
|
||||
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;
|
||||
|
||||
+5
-20
@@ -27,7 +27,6 @@
|
||||
#include "gtkradiobutton.h"
|
||||
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtktogglebuttonprivate.h"
|
||||
#include "gtkcheckbuttonprivate.h"
|
||||
#include "gtklabel.h"
|
||||
#include "gtkmarshalers.h"
|
||||
@@ -242,7 +241,7 @@ gtk_radio_button_init (GtkRadioButton *radio_button)
|
||||
|
||||
gtk_widget_set_receives_default (GTK_WIDGET (radio_button), FALSE);
|
||||
|
||||
_gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button), TRUE);
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button), TRUE);
|
||||
|
||||
priv->group = g_slist_prepend (NULL, radio_button);
|
||||
|
||||
@@ -729,9 +728,6 @@ gtk_radio_button_clicked (GtkButton *button)
|
||||
GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
|
||||
GtkToggleButton *tmp_button;
|
||||
GSList *tmp_list;
|
||||
gint toggled;
|
||||
|
||||
toggled = FALSE;
|
||||
|
||||
g_object_ref (GTK_WIDGET (button));
|
||||
|
||||
@@ -753,17 +749,13 @@ gtk_radio_button_clicked (GtkButton *button)
|
||||
}
|
||||
|
||||
if (tmp_button)
|
||||
{
|
||||
toggled = TRUE;
|
||||
_gtk_toggle_button_set_active (toggle_button,
|
||||
!gtk_toggle_button_get_active (toggle_button));
|
||||
}
|
||||
gtk_toggle_button_set_active (toggle_button,
|
||||
!gtk_toggle_button_get_active (toggle_button));
|
||||
}
|
||||
else
|
||||
{
|
||||
toggled = TRUE;
|
||||
_gtk_toggle_button_set_active (toggle_button,
|
||||
!gtk_toggle_button_get_active (toggle_button));
|
||||
gtk_toggle_button_set_active (toggle_button,
|
||||
!gtk_toggle_button_get_active (toggle_button));
|
||||
|
||||
tmp_list = priv->group;
|
||||
while (tmp_list)
|
||||
@@ -779,13 +771,6 @@ gtk_radio_button_clicked (GtkButton *button)
|
||||
}
|
||||
}
|
||||
|
||||
if (toggled)
|
||||
{
|
||||
gtk_toggle_button_toggled (toggle_button);
|
||||
|
||||
g_object_notify (G_OBJECT (toggle_button), "active");
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (button));
|
||||
|
||||
g_object_unref (button);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+10
-20
@@ -24,7 +24,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtktogglebuttonprivate.h"
|
||||
#include "gtktogglebutton.h"
|
||||
|
||||
#include "gtkbuttonprivate.h"
|
||||
#include "gtkintl.h"
|
||||
@@ -293,8 +293,9 @@ gtk_toggle_button_get_property (GObject *object,
|
||||
*
|
||||
* Sets the status of the toggle button. Set to %TRUE if you want the
|
||||
* GtkToggleButton to be “pressed in”, and %FALSE to raise it.
|
||||
* This action causes the #GtkToggleButton::toggled signal and the
|
||||
* #GtkButton::clicked signal to be emitted.
|
||||
*
|
||||
* If the status of the button changes, this action causes the
|
||||
* #GtkToggleButton::toggled signal to be emitted.
|
||||
*/
|
||||
void
|
||||
gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
|
||||
@@ -306,18 +307,8 @@ gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
|
||||
|
||||
is_active = is_active != FALSE;
|
||||
|
||||
if (priv->active != is_active)
|
||||
{
|
||||
g_signal_emit_by_name (toggle_button, "clicked");
|
||||
g_object_notify_by_pspec (G_OBJECT (toggle_button), toggle_button_props[PROP_ACTIVE]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
|
||||
gboolean is_active)
|
||||
{
|
||||
GtkToggleButtonPrivate *priv = gtk_toggle_button_get_instance_private (toggle_button);
|
||||
if (priv->active == is_active)
|
||||
return;
|
||||
|
||||
priv->active = is_active;
|
||||
|
||||
@@ -326,6 +317,9 @@ _gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
|
||||
else
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (toggle_button), GTK_STATE_FLAG_CHECKED);
|
||||
|
||||
gtk_toggle_button_toggled (toggle_button);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (toggle_button), toggle_button_props[PROP_ACTIVE]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -387,11 +381,7 @@ gtk_toggle_button_clicked (GtkButton *button)
|
||||
GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
|
||||
GtkToggleButtonPrivate *priv = gtk_toggle_button_get_instance_private (toggle_button);
|
||||
|
||||
_gtk_toggle_button_set_active (toggle_button, !priv->active);
|
||||
|
||||
gtk_toggle_button_toggled (toggle_button);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (toggle_button), toggle_button_props[PROP_ACTIVE]);
|
||||
gtk_toggle_button_set_active (toggle_button, !priv->active);
|
||||
|
||||
if (GTK_BUTTON_CLASS (gtk_toggle_button_parent_class)->clicked)
|
||||
GTK_BUTTON_CLASS (gtk_toggle_button_parent_class)->clicked (button);
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
* Copyright (C) 2014 Red Hat, Inc.
|
||||
*
|
||||
* 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 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#ifndef __GTK_TOGGLE_BUTTON_PRIVATE_H__
|
||||
#define __GTK_TOGGLE_BUTTON_PRIVATE_H__
|
||||
|
||||
|
||||
#include <gtk/gtktogglebutton.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void _gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
|
||||
gboolean is_active);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __GTK_TOGGLE_BUTTON_PRIVATE_H__ */
|
||||
@@ -0,0 +1,699 @@
|
||||
/*
|
||||
* 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_is_focus (widget))
|
||||
return FALSE;
|
||||
|
||||
if (!gtk_widget_get_can_focus (widget))
|
||||
return FALSE;
|
||||
|
||||
gtk_widget_grab_focus (widget);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_tree_expander_grab_focus (GtkWidget *widget)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
|
||||
|
||||
if (self->child && gtk_widget_grab_focus (self->child))
|
||||
return TRUE;
|
||||
|
||||
return GTK_WIDGET_CLASS (gtk_tree_expander_parent_class)->grab_focus (widget);
|
||||
}
|
||||
|
||||
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,
|
||||
GVariant *args,
|
||||
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,
|
||||
GVariant *args,
|
||||
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;
|
||||
widget_class->grab_focus = gtk_tree_expander_grab_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, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Right, GDK_SHIFT_MASK,
|
||||
expand_collapse_right, NULL, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
expand_collapse_right, NULL, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
expand_collapse_right, NULL, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_Left, GDK_SHIFT_MASK,
|
||||
expand_collapse_left, NULL, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Left, GDK_SHIFT_MASK,
|
||||
expand_collapse_left, NULL, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
expand_collapse_left, NULL, NULL, NULL);
|
||||
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
expand_collapse_left, NULL, 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));
|
||||
}
|
||||
|
||||
@@ -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,8 @@ typedef struct _GtkClipboard GtkClipboard;
|
||||
typedef struct _GtkEventController GtkEventController;
|
||||
typedef struct _GtkGesture GtkGesture;
|
||||
typedef struct _GtkLayoutManager GtkLayoutManager;
|
||||
typedef struct _GtkListItem GtkListItem;
|
||||
typedef struct _GtkListItemFactory GtkListItemFactory;
|
||||
typedef struct _GtkNative GtkNative;
|
||||
typedef struct _GtkRequisition GtkRequisition;
|
||||
typedef struct _GtkRoot GtkRoot;
|
||||
|
||||
+26
-1
@@ -6549,7 +6549,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);
|
||||
}
|
||||
@@ -13402,6 +13402,31 @@ gtk_widget_get_height (GtkWidget *widget)
|
||||
return priv->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_get_size:
|
||||
* @widget: a #GtkWidget
|
||||
* @orientation: the orientation to query
|
||||
*
|
||||
* Returns the content width or height of the widget, depending on @orientation.
|
||||
* This is equivalent to calling gtk_widget_get_width() for %GTK_ORIENTATION_HORIZONTAL
|
||||
* or gtk_widget_get_height() for %GTK_ORIENTATION_VERTICAL, but can be used when
|
||||
* writing orientation-independent code, such as when implementing #GtkOrientable
|
||||
* widgets.
|
||||
*
|
||||
* Returns: The size of @widget in @orientation.
|
||||
*/
|
||||
int
|
||||
gtk_widget_get_size (GtkWidget *widget,
|
||||
GtkOrientation orientation)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
return gtk_widget_get_width (widget);
|
||||
else
|
||||
return gtk_widget_get_height (widget);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_class_set_layout_manager_type:
|
||||
* @widget_class: class to set the layout manager type for
|
||||
|
||||
@@ -576,6 +576,9 @@ GDK_AVAILABLE_IN_ALL
|
||||
int gtk_widget_get_width (GtkWidget *widget);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gtk_widget_get_height (GtkWidget *widget);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gtk_widget_get_size (GtkWidget *widget,
|
||||
GtkOrientation orientation);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_widget_child_focus (GtkWidget *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));
|
||||
|
||||
+74
-78
@@ -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',
|
||||
@@ -203,6 +204,8 @@ gtk_public_sources = files([
|
||||
'gtkcolorchooserdialog.c',
|
||||
'gtkcolorchooserwidget.c',
|
||||
'gtkcolorutils.c',
|
||||
'gtkcolumnview.c',
|
||||
'gtkcolumnviewcolumn.c',
|
||||
'gtkcombobox.c',
|
||||
'gtkcomboboxtext.c',
|
||||
'gtkcomposetable.c',
|
||||
@@ -210,8 +213,10 @@ gtk_public_sources = files([
|
||||
'gtkconstraintlayout.c',
|
||||
'gtkconstraint.c',
|
||||
'gtkcontainer.c',
|
||||
'gtkcoverflow.c',
|
||||
'gtkcssprovider.c',
|
||||
'gtkdialog.c',
|
||||
'gtkdirectorylist.c',
|
||||
'gtkdnd.c',
|
||||
'gtkdragdest.c',
|
||||
'gtkdragsource.c',
|
||||
@@ -228,6 +233,7 @@ gtk_public_sources = files([
|
||||
'gtkeventcontrollermotion.c',
|
||||
'gtkeventcontrollerscroll.c',
|
||||
'gtkexpander.c',
|
||||
'gtkexpression.c',
|
||||
'gtkfilechooser.c',
|
||||
'gtkfilechooserbutton.c',
|
||||
'gtkfilechooserdialog.c',
|
||||
@@ -245,6 +251,7 @@ gtk_public_sources = files([
|
||||
'gtkfontchooserutils.c',
|
||||
'gtkfontchooserwidget.c',
|
||||
'gtkframe.c',
|
||||
'gtkfunctionslistitemfactory.c',
|
||||
'gtkgesture.c',
|
||||
'gtkgesturedrag.c',
|
||||
'gtkgesturelongpress.c',
|
||||
@@ -258,6 +265,7 @@ gtk_public_sources = files([
|
||||
'gtkglarea.c',
|
||||
'gtkgrid.c',
|
||||
'gtkgridlayout.c',
|
||||
'gtkgridview.c',
|
||||
'gtkheaderbar.c',
|
||||
'gtkicontheme.c',
|
||||
'gtkiconview.c',
|
||||
@@ -272,10 +280,15 @@ gtk_public_sources = files([
|
||||
'gtklayoutchild.c',
|
||||
'gtklayoutmanager.c',
|
||||
'gtklevelbar.c',
|
||||
'gtklistbase.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 +403,7 @@ gtk_public_sources = files([
|
||||
'gtktooltip.c',
|
||||
'gtktooltipwindow.c',
|
||||
'gtktreednd.c',
|
||||
'gtktreeexpander.c',
|
||||
'gtktreelistmodel.c',
|
||||
'gtktreemenu.c',
|
||||
'gtktreemodel.c',
|
||||
@@ -442,6 +456,7 @@ gtk_public_headers = files([
|
||||
'gtkboxlayout.h',
|
||||
'gtkbuildable.h',
|
||||
'gtkbuilder.h',
|
||||
'gtkbuilderlistitemfactory.h',
|
||||
'gtkbutton.h',
|
||||
'gtkcalendar.h',
|
||||
'gtkcenterbox.h',
|
||||
@@ -468,16 +483,20 @@ gtk_public_headers = files([
|
||||
'gtkcolorchooserdialog.h',
|
||||
'gtkcolorchooserwidget.h',
|
||||
'gtkcolorutils.h',
|
||||
'gtkcolumnview.h',
|
||||
'gtkcolumnviewcolumn.h',
|
||||
'gtkcombobox.h',
|
||||
'gtkcomboboxtext.h',
|
||||
'gtkconstraintguide.h',
|
||||
'gtkconstraintlayout.h',
|
||||
'gtkconstraint.h',
|
||||
'gtkcontainer.h',
|
||||
'gtkcoverflow.h',
|
||||
'gtkcssprovider.h',
|
||||
'gtkcustomlayout.h',
|
||||
'gtkdebug.h',
|
||||
'gtkdialog.h',
|
||||
'gtkdirectorylist.h',
|
||||
'gtkdnd.h',
|
||||
'gtkdragdest.h',
|
||||
'gtkdragsource.h',
|
||||
@@ -509,6 +528,7 @@ gtk_public_headers = files([
|
||||
'gtkfontchooserdialog.h',
|
||||
'gtkfontchooserwidget.h',
|
||||
'gtkframe.h',
|
||||
'gtkfunctionslistitemfactory.h',
|
||||
'gtkgesture.h',
|
||||
'gtkgesturedrag.h',
|
||||
'gtkgesturelongpress.h',
|
||||
@@ -522,6 +542,7 @@ gtk_public_headers = files([
|
||||
'gtkglarea.h',
|
||||
'gtkgrid.h',
|
||||
'gtkgridlayout.h',
|
||||
'gtkgridview.h',
|
||||
'gtkheaderbar.h',
|
||||
'gtkicontheme.h',
|
||||
'gtkiconview.h',
|
||||
@@ -536,8 +557,12 @@ gtk_public_headers = files([
|
||||
'gtklayoutmanager.h',
|
||||
'gtklevelbar.h',
|
||||
'gtklinkbutton.h',
|
||||
'gtklistbase.h',
|
||||
'gtklistbox.h',
|
||||
'gtklistitem.h',
|
||||
'gtklistitemfactory.h',
|
||||
'gtkliststore.h',
|
||||
'gtklistview.h',
|
||||
'gtklockbutton.h',
|
||||
'gtkmain.h',
|
||||
'gtkmaplistmodel.h',
|
||||
@@ -632,6 +657,7 @@ gtk_public_headers = files([
|
||||
'gtktoolshell.h',
|
||||
'gtktooltip.h',
|
||||
'gtktreednd.h',
|
||||
'gtktreeexpander.h',
|
||||
'gtktreelistmodel.h',
|
||||
'gtktreemodel.h',
|
||||
'gtktreemodelfilter.h',
|
||||
|
||||
@@ -160,6 +160,12 @@ flowbox {
|
||||
}
|
||||
}
|
||||
|
||||
coverflow cover {
|
||||
color: $text_color;
|
||||
background-color: $base_color;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.content-view .tile {
|
||||
margin: 2px;
|
||||
background-color: if($variant=='light', transparent, black);
|
||||
@@ -3730,7 +3736,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ flowbox flowboxchild { padding: 3px; }
|
||||
|
||||
flowbox flowboxchild:selected { outline-offset: -2px; }
|
||||
|
||||
coverflow cover { color: white; background-color: #2d2d2d; }
|
||||
|
||||
.content-view .tile { margin: 2px; background-color: black; border-radius: 0; padding: 0; }
|
||||
|
||||
.content-view .tile:backdrop { background-color: #232323; }
|
||||
@@ -82,9 +84,9 @@ assistant .sidebar label { padding: 6px 12px; }
|
||||
|
||||
assistant .sidebar label.highlight { background-color: #5a5a59; }
|
||||
|
||||
.csd popover.background > contents.touch-selection, .csd popover.background > contents.magnifier, popover.background > contents.touch-selection, popover.background > contents.magnifier, .csd popover.background > contents.osd, popover.background > contents.osd, .app-notification, .app-notification.frame, .osd .scale-popup, .osd { color: #eeeeec; border: none; background-color: rgba(37, 37, 38, 0.7); background-clip: padding-box; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
|
||||
.osd popover.background > arrow, .osd popover.background > contents, popover.background.touch-selection > arrow, popover.background.touch-selection > contents, popover.background.magnifier > arrow, popover.background.magnifier > contents, .app-notification, .app-notification.frame, .osd .scale-popup, .osd { color: #eeeeec; border: none; background-color: rgba(38, 38, 38, 0.7); background-clip: padding-box; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
|
||||
|
||||
.csd popover.background > contents.touch-selection:backdrop, .csd popover.background > contents.magnifier:backdrop, popover.background > contents.touch-selection:backdrop, popover.background > contents.magnifier:backdrop, .csd popover.background > contents.osd:backdrop, popover.background > contents.osd:backdrop, .app-notification:backdrop, .osd .scale-popup:backdrop, .osd:backdrop { text-shadow: none; -gtk-icon-shadow: none; }
|
||||
.osd popover.background > arrow:backdrop, .osd popover.background > contents:backdrop, popover.background.touch-selection > arrow:backdrop, popover.background.touch-selection > contents:backdrop, popover.background.magnifier > arrow:backdrop, popover.background.magnifier > contents:backdrop, .app-notification:backdrop, .osd .scale-popup:backdrop, .osd:backdrop { text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
/********************* Spinner Animation * */
|
||||
@keyframes spin { to { -gtk-icon-transform: rotate(1turn); } }
|
||||
@@ -267,7 +269,7 @@ row:selected button.sidebar-button:not(:active):not(:checked):not(:hover):not(di
|
||||
|
||||
row:selected button.sidebar-button:not(:active):not(:checked):not(:hover):not(disabled):backdrop, row:selected button.flat:not(:active):not(:checked):not(:hover):not(disabled):backdrop { color: #919190; }
|
||||
|
||||
button.osd { min-width: 26px; min-height: 32px; color: #eeeeec; border-radius: 5px; color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); border: none; box-shadow: none; }
|
||||
button.osd { min-width: 26px; min-height: 32px; color: #eeeeec; border-radius: 5px; color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); border: none; box-shadow: none; }
|
||||
|
||||
button.osd.image-button { min-width: 30px; }
|
||||
|
||||
@@ -279,27 +281,27 @@ button.osd:active, button.osd:checked { color: white; border-color: rgba(0, 0, 0
|
||||
|
||||
button.osd:disabled:backdrop, button.osd:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; border: none; }
|
||||
|
||||
button.osd:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; border: none; }
|
||||
button.osd:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; border: none; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button, .csd popover.background > contents.magnifier button, popover.background > contents.touch-selection button, popover.background > contents.magnifier button, .app-notification button, .app-notification.frame button, .osd button { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
popover.background.touch-selection button, popover.background.magnifier button, .app-notification button, .app-notification.frame button, .osd button { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.csd popover.background > contents.touch-selection button:hover, .csd popover.background > contents.magnifier button:hover, popover.background > contents.touch-selection button:hover, popover.background > contents.magnifier button:hover, .app-notification button:hover, .osd button:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
popover.background.touch-selection button:hover, popover.background.magnifier button:hover, .app-notification button:hover, .osd button:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.csd popover.background > contents.touch-selection button:active:backdrop, .csd popover.background > contents.magnifier button:active:backdrop, popover.background > contents.touch-selection button:active:backdrop, popover.background > contents.magnifier button:active:backdrop, .app-notification button:active:backdrop, .csd popover.background > contents.touch-selection button:active, .csd popover.background > contents.magnifier button:active, popover.background > contents.touch-selection button:active, popover.background > contents.magnifier button:active, .app-notification button:active, .csd popover.background > contents.touch-selection button:checked:backdrop, .csd popover.background > contents.magnifier button:checked:backdrop, popover.background > contents.touch-selection button:checked:backdrop, popover.background > contents.magnifier button:checked:backdrop, .app-notification button:checked:backdrop, .csd popover.background > contents.touch-selection button:checked, .csd popover.background > contents.magnifier button:checked, popover.background > contents.touch-selection button:checked, popover.background > contents.magnifier button:checked, .app-notification button:checked, .osd button:active:backdrop, .osd button:active, .osd button:checked:backdrop, .osd button:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
popover.background.touch-selection button:active:backdrop, popover.background.magnifier button:active:backdrop, .app-notification button:active:backdrop, popover.background.touch-selection button:active, popover.background.magnifier button:active, .app-notification button:active, popover.background.touch-selection button:checked:backdrop, popover.background.magnifier button:checked:backdrop, .app-notification button:checked:backdrop, popover.background.touch-selection button:checked, popover.background.magnifier button:checked, .app-notification button:checked, .osd button:active:backdrop, .osd button:active, .osd button:checked:backdrop, .osd button:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.csd popover.background > contents.touch-selection button:disabled:backdrop, .csd popover.background > contents.magnifier button:disabled:backdrop, popover.background > contents.touch-selection button:disabled:backdrop, popover.background > contents.magnifier button:disabled:backdrop, .app-notification button:disabled:backdrop, .csd popover.background > contents.touch-selection button:disabled, .csd popover.background > contents.magnifier button:disabled, popover.background > contents.touch-selection button:disabled, popover.background > contents.magnifier button:disabled, .app-notification button:disabled, .osd button:disabled:backdrop, .osd button:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
popover.background.touch-selection button:disabled:backdrop, popover.background.magnifier button:disabled:backdrop, .app-notification button:disabled:backdrop, popover.background.touch-selection button:disabled, popover.background.magnifier button:disabled, .app-notification button:disabled, .osd button:disabled:backdrop, .osd button:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button:backdrop, .csd popover.background > contents.magnifier button:backdrop, popover.background > contents.touch-selection button:backdrop, popover.background > contents.magnifier button:backdrop, .app-notification button:backdrop, .osd button:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
popover.background.touch-selection button:backdrop, popover.background.magnifier button:backdrop, .app-notification button:backdrop, .osd button:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button.flat, .csd popover.background > contents.magnifier button.flat, popover.background > contents.touch-selection button.flat, popover.background > contents.magnifier button.flat, .app-notification button.flat, .osd button.flat { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; box-shadow: none; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
|
||||
popover.background.touch-selection button.flat, popover.background.magnifier button.flat, .app-notification button.flat, .osd button.flat { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; box-shadow: none; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button.flat:hover, .csd popover.background > contents.magnifier button.flat:hover, popover.background > contents.touch-selection button.flat:hover, popover.background > contents.magnifier button.flat:hover, .app-notification button.flat:hover, .osd button.flat:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
popover.background.touch-selection button.flat:hover, popover.background.magnifier button.flat:hover, .app-notification button.flat:hover, .osd button.flat:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.csd popover.background > contents.touch-selection button.flat:disabled, .csd popover.background > contents.magnifier button.flat:disabled, popover.background > contents.touch-selection button.flat:disabled, popover.background > contents.magnifier button.flat:disabled, .app-notification button.flat:disabled, .osd button.flat:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-image: none; border-color: transparent; box-shadow: none; }
|
||||
popover.background.touch-selection button.flat:disabled, popover.background.magnifier button.flat:disabled, .app-notification button.flat:disabled, .osd button.flat:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-image: none; border-color: transparent; box-shadow: none; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button.flat:backdrop, .csd popover.background > contents.magnifier button.flat:backdrop, popover.background > contents.touch-selection button.flat:backdrop, popover.background > contents.magnifier button.flat:backdrop, .app-notification button.flat:backdrop, .osd button.flat:backdrop { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; }
|
||||
popover.background.touch-selection button.flat:backdrop, popover.background.magnifier button.flat:backdrop, .app-notification button.flat:backdrop, .osd button.flat:backdrop { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button.flat:active, .csd popover.background > contents.magnifier button.flat:active, popover.background > contents.touch-selection button.flat:active, popover.background > contents.magnifier button.flat:active, .app-notification button.flat:active, .csd popover.background > contents.touch-selection button.flat:checked, .csd popover.background > contents.magnifier button.flat:checked, popover.background > contents.touch-selection button.flat:checked, popover.background > contents.magnifier button.flat:checked, .app-notification button.flat:checked, .osd button.flat:active, .osd button.flat:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
popover.background.touch-selection button.flat:active, popover.background.magnifier button.flat:active, .app-notification button.flat:active, popover.background.touch-selection button.flat:checked, popover.background.magnifier button.flat:checked, .app-notification button.flat:checked, .osd button.flat:active, .osd button.flat:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
button.suggested-action { color: white; outline-color: rgba(255, 255, 255, 0.3); border-color: #0f3b71; border-bottom-color: #092444; background-image: linear-gradient(to top, #155099 2px, #15539e); text-shadow: 0 -1px rgba(0, 0, 0, 0.719216); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.719216); box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px 2px rgba(0, 0, 0, 0.07); }
|
||||
|
||||
@@ -499,7 +501,7 @@ button:link > label:active, button:visited > label:active, *:link:active, button
|
||||
|
||||
*:selected button:link > label:active, *:selected button:visited > label:active, *:selected *:link:active, *:selected button:active:link, *:selected button:active:visited { color: #d0ddec; }
|
||||
|
||||
button:link > label:disabled, button:visited > label:disabled, button:link > label:disabled:backdrop, button:visited > label:disabled:backdrop, *:link:disabled, button:disabled:link, button:disabled:visited, *:link:disabled:backdrop, button:disabled:backdrop:link, button:disabled:backdrop:visited { color: rgba(140, 140, 141, 0.8); }
|
||||
button:link > label:disabled, button:visited > label:disabled, button:link > label:disabled:backdrop, button:visited > label:disabled:backdrop, *:link:disabled, button:disabled:link, button:disabled:visited, *:link:disabled:backdrop, button:disabled:backdrop:link, button:disabled:backdrop:visited { color: rgba(141, 141, 141, 0.8); }
|
||||
|
||||
button:link > label:backdrop:backdrop:hover, button:visited > label:backdrop:backdrop:hover, button:link > label:backdrop:backdrop:hover:selected, button:visited > label:backdrop:backdrop:hover:selected, button:link > label:backdrop, button:visited > label:backdrop, *:link:backdrop:backdrop:hover, button:backdrop:backdrop:hover:link, button:backdrop:backdrop:hover:visited, *:link:backdrop:backdrop:hover:selected, button:backdrop:backdrop:hover:selected:link, button:backdrop:backdrop:hover:selected:visited, .selection-mode .titlebar:not(headerbar) .subtitle:backdrop:backdrop:hover:link, .selection-mode.titlebar:not(headerbar) .subtitle:backdrop:backdrop:hover:link, .selection-mode headerbar .subtitle:backdrop:backdrop:hover:link, headerbar.selection-mode .subtitle:backdrop:backdrop:hover:link, *:link:backdrop, button:backdrop:link, button:backdrop:visited { color: #15539e; }
|
||||
|
||||
@@ -566,7 +568,7 @@ spinbutton.vertical button.up { border-bottom-style: none; border-bottom-left-ra
|
||||
|
||||
spinbutton.vertical button.down { border-top-style: none; border-top-left-radius: 0; border-top-right-radius: 0; }
|
||||
|
||||
.osd spinbutton.vertical button:first-child { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
.osd spinbutton.vertical button:first-child { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.osd spinbutton.vertical button:first-child:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
@@ -574,7 +576,7 @@ spinbutton.vertical button.down { border-top-style: none; border-top-left-radius
|
||||
|
||||
.osd spinbutton.vertical button:first-child:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.osd spinbutton.vertical button:first-child:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
.osd spinbutton.vertical button:first-child:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
treeview spinbutton:not(.vertical) { min-height: 0; border-style: none; border-radius: 0; }
|
||||
|
||||
@@ -592,7 +594,7 @@ toolbar { padding: 4px 3px 3px 4px; }
|
||||
|
||||
.osd toolbar { background-color: transparent; }
|
||||
|
||||
toolbar.osd { padding: 13px; border: none; border-radius: 5px; background-color: rgba(37, 37, 38, 0.7); }
|
||||
toolbar.osd { padding: 13px; border: none; border-radius: 5px; background-color: rgba(38, 38, 38, 0.7); }
|
||||
|
||||
toolbar.osd.left, toolbar.osd.right, toolbar.osd.top, toolbar.osd.bottom { border-radius: 0; }
|
||||
|
||||
@@ -615,7 +617,7 @@ searchbar > revealer > box { padding: 6px; border-width: 0 0 1px; }
|
||||
.inline-toolbar:backdrop, .location-bar:backdrop, searchbar > revealer > box:backdrop { border-color: #202020; background-color: #2e2e2e; box-shadow: none; transition: 200ms ease-out; }
|
||||
|
||||
/*************** Header bars * */
|
||||
.titlebar:not(headerbar), headerbar { padding: 0 6px; min-height: 46px; border-width: 0 0 1px; border-style: solid; border-color: #070707; border-radius: 0; border-spacing: 6px; background: #1b1b1b linear-gradient(to top, #252526, #2b2b2b); box-shadow: inset 0 1px rgba(238, 238, 236, 0.07); /* Darken switchbuttons for headerbars. issue #1588 */ /* hide the close button separator */ }
|
||||
.titlebar:not(headerbar), headerbar { padding: 0 6px; min-height: 46px; border-width: 0 0 1px; border-style: solid; border-color: #070707; border-radius: 0; border-spacing: 6px; background: #1b1b1b linear-gradient(to top, #262626, #2b2b2b); box-shadow: inset 0 1px rgba(238, 238, 236, 0.07); /* Darken switchbuttons for headerbars. issue #1588 */ /* hide the close button separator */ }
|
||||
|
||||
.titlebar:backdrop:not(headerbar), headerbar:backdrop { border-color: #202020; background-color: #353535; background-image: none; box-shadow: inset 0 1px rgba(238, 238, 236, 0.07); transition: 200ms ease-out; }
|
||||
|
||||
@@ -881,25 +883,23 @@ menuitem check:dir(ltr), menuitem radio:dir(ltr) { margin-right: 7px; }
|
||||
menuitem check:dir(rtl), menuitem radio:dir(rtl) { margin-left: 7px; }
|
||||
|
||||
/*************** Popovers * */
|
||||
popover.background { background-color: transparent; padding: 0px; }
|
||||
popover.background { background-color: transparent; font: initial; }
|
||||
|
||||
popover.menu > arrow, popover > arrow { background-color: #353535; border: 1px solid #1b1b1b; }
|
||||
popover.background > arrow, popover.background > contents { background-color: #353535; border: 1px solid #1b1b1b; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); }
|
||||
|
||||
popover > contents { padding: 8px; background-color: #353535; border: 1px solid #1b1b1b; margin: 0px; }
|
||||
popover.background > arrow:backdrop, popover.background > contents:backdrop { background-color: #353535; border-color: #202020; box-shadow: none; }
|
||||
|
||||
popover.background > contents { background-color: #353535; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); }
|
||||
|
||||
.csd popover.background > contents, popover.background > contents { border: 1px solid #1b1b1b; border-radius: 9px; }
|
||||
|
||||
popover.background > contents:backdrop { background-color: #353535; box-shadow: none; }
|
||||
popover.background > contents { padding: 8px; border-radius: 9px; }
|
||||
|
||||
popover.background > contents > list, popover.background > contents > .view, popover.background > contents > iconview, popover.background > contents > toolbar { border-style: none; background-color: transparent; }
|
||||
|
||||
.csd popover.background > contents.touch-selection, .csd popover.background > contents.magnifier, popover.background > contents.touch-selection, popover.background > contents.magnifier { border: 1px solid rgba(255, 255, 255, 0.1); }
|
||||
|
||||
popover.background > contents separator { margin: 3px; }
|
||||
|
||||
popover.background > contents list separator { margin: 0px; }
|
||||
popover.background > contents list separator { margin: 0; }
|
||||
|
||||
.osd popover.background, popover.background.touch-selection, popover.background.magnifier { background-color: transparent; }
|
||||
|
||||
.osd popover.background > arrow, .osd popover.background > contents, popover.background.touch-selection > arrow, popover.background.touch-selection > contents, popover.background.magnifier > arrow, popover.background.magnifier > contents { border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: none; }
|
||||
|
||||
/************* Notebooks * */
|
||||
notebook > header { padding: 1px; border-color: #1b1b1b; border-width: 1px; background-color: #282828; }
|
||||
@@ -1149,7 +1149,7 @@ switch:backdrop:disabled slider label, switch:backdrop:disabled slider { color:
|
||||
|
||||
.view.content-view.check:active:not(list), iconview.content-view.check:active:not(list), .content-view .tile check:active:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(21, 83, 158, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.view.content-view.check:backdrop:not(list), iconview.content-view.check:backdrop:not(list), .content-view .tile check:backdrop:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(89, 89, 90, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
|
||||
.view.content-view.check:backdrop:not(list), iconview.content-view.check:backdrop:not(list), .content-view .tile check:backdrop:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(90, 90, 90, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.view.content-view.check:checked:not(list), iconview.content-view.check:checked:not(list), .content-view .tile check:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: #eeeeec; background-color: rgba(21, 83, 158, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
|
||||
|
||||
@@ -1157,11 +1157,11 @@ switch:backdrop:disabled slider label, switch:backdrop:disabled slider { color:
|
||||
|
||||
.view.content-view.check:checked:active:not(list), iconview.content-view.check:checked:active:not(list), .content-view .tile check:checked:active:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: #eeeeec; background-color: rgba(21, 83, 158, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
|
||||
|
||||
.view.content-view.check:backdrop:checked:not(list), iconview.content-view.check:backdrop:checked:not(list), .content-view .tile check:backdrop:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: rgba(238, 238, 236, 0.8); background-color: rgba(89, 89, 90, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
|
||||
.view.content-view.check:backdrop:checked:not(list), iconview.content-view.check:backdrop:checked:not(list), .content-view .tile check:backdrop:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: rgba(238, 238, 236, 0.8); background-color: rgba(90, 90, 90, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
|
||||
|
||||
checkbutton.text-button, radiobutton.text-button { padding: 2px 0; outline-offset: 0; border-spacing: 4px; }
|
||||
|
||||
check, radio { margin: 0 4px; min-height: 14px; min-width: 14px; border: 1px solid; -gtk-icon-source: none; color: #eeeeec; outline-color: rgba(238, 238, 236, 0.3); border-color: #070707; text-shadow: 0 -1px rgba(0, 0, 0, 0.834353); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.834353); background-image: linear-gradient(to bottom, #2d2d2d 20%, #252526 90%); box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.07); }
|
||||
check, radio { margin: 0 4px; min-height: 14px; min-width: 14px; border: 1px solid; -gtk-icon-source: none; color: #eeeeec; outline-color: rgba(238, 238, 236, 0.3); border-color: #070707; text-shadow: 0 -1px rgba(0, 0, 0, 0.834353); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.834353); background-image: linear-gradient(to bottom, #2d2d2d 20%, #262626 90%); box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.07); }
|
||||
|
||||
check:only-child, radio:only-child { margin: 0; }
|
||||
|
||||
@@ -1189,13 +1189,13 @@ check:backdrop:disabled, radio:backdrop:disabled { border-color: #202020; backgr
|
||||
|
||||
check:backdrop:disabled label, check:backdrop:disabled, radio:backdrop:disabled label, radio:backdrop:disabled { color: #5b5b5b; }
|
||||
|
||||
.osd check, .osd radio { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
.osd check, .osd radio { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.osd check:hover, .osd radio:hover { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
.osd check:hover, .osd radio:hover { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.osd check:active, .osd radio:active { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.osd check:backdrop, .osd radio:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
.osd check:backdrop, .osd radio:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.osd check:disabled, .osd radio:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
@@ -1288,7 +1288,7 @@ scale fill:disabled:backdrop, scale fill:disabled { border-color: transparent; b
|
||||
|
||||
.osd scale fill:disabled:backdrop, .osd scale fill:disabled { border-color: transparent; background-color: transparent; }
|
||||
|
||||
scale slider { color: #eeeeec; outline-color: rgba(238, 238, 236, 0.3); border-color: #070707; text-shadow: 0 -1px rgba(0, 0, 0, 0.834353); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.834353); background-image: linear-gradient(to bottom, #2d2d2d 20%, #252526 90%); box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.07); border: 1px solid black; border-radius: 100%; transition: all 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94); transition-property: background, border, box-shadow; }
|
||||
scale slider { color: #eeeeec; outline-color: rgba(238, 238, 236, 0.3); border-color: #070707; text-shadow: 0 -1px rgba(0, 0, 0, 0.834353); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.834353); background-image: linear-gradient(to bottom, #2d2d2d 20%, #262626 90%); box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.07); border: 1px solid black; border-radius: 100%; transition: all 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94); transition-property: background, border, box-shadow; }
|
||||
|
||||
scale slider:hover { color: #eeeeec; outline-color: rgba(238, 238, 236, 0.3); border-color: #070707; box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.07); background-image: linear-gradient(to bottom, #353535 20%, #2b2b2b 90%); }
|
||||
|
||||
@@ -1308,17 +1308,17 @@ scale slider:backdrop:disabled label, scale slider:backdrop:disabled { color: #5
|
||||
|
||||
row:selected scale slider:disabled, row:selected scale slider { border-color: #030c17; }
|
||||
|
||||
.osd scale slider { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); border-color: rgba(0, 0, 0, 0.7); background-color: #252526; }
|
||||
.osd scale slider { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); border-color: rgba(0, 0, 0, 0.7); background-color: #262626; }
|
||||
|
||||
.osd scale slider:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); background-color: #252526; }
|
||||
.osd scale slider:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); background-color: #262626; }
|
||||
|
||||
.osd scale slider:active { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); background-color: #252526; }
|
||||
.osd scale slider:active { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); background-color: #262626; }
|
||||
|
||||
.osd scale slider:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-color: #252526; }
|
||||
.osd scale slider:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-color: #262626; }
|
||||
|
||||
.osd scale slider:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-color: #252526; }
|
||||
.osd scale slider:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-color: #262626; }
|
||||
|
||||
.osd scale slider:backdrop:disabled { background-color: #252526; }
|
||||
.osd scale slider:backdrop:disabled { background-color: #262626; }
|
||||
|
||||
scale value { color: alpha(currentColor,0.55); }
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -1648,7 +1650,7 @@ row.activatable:selected.has-open-popup, row.activatable:selected:hover { backgr
|
||||
row.activatable:selected:backdrop { background-color: #15539e; }
|
||||
|
||||
/********************* App Notifications * */
|
||||
.app-notification, .app-notification.frame { padding: 10px; border-radius: 0 0 5px 5px; background-color: rgba(37, 37, 38, 0.7); background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.2), transparent 2px); background-clip: padding-box; }
|
||||
.app-notification, .app-notification.frame { padding: 10px; border-radius: 0 0 5px 5px; background-color: rgba(38, 38, 38, 0.7); background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.2), transparent 2px); background-clip: padding-box; }
|
||||
|
||||
.app-notification:backdrop, .app-notification.frame:backdrop { background-image: none; transition: 200ms ease-out; }
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ flowbox flowboxchild { padding: 3px; }
|
||||
|
||||
flowbox flowboxchild:selected { outline-offset: -2px; }
|
||||
|
||||
coverflow cover { color: black; background-color: #ffffff; }
|
||||
|
||||
.content-view .tile { margin: 2px; background-color: transparent; border-radius: 0; padding: 0; }
|
||||
|
||||
.content-view .tile:backdrop { background-color: transparent; }
|
||||
@@ -82,9 +84,9 @@ assistant .sidebar label { padding: 6px 12px; }
|
||||
|
||||
assistant .sidebar label.highlight { background-color: #cecece; }
|
||||
|
||||
.csd popover.background > contents.touch-selection, .csd popover.background > contents.magnifier, popover.background > contents.touch-selection, popover.background > contents.magnifier, .csd popover.background > contents.osd, popover.background > contents.osd, .app-notification, .app-notification.frame, .osd .scale-popup, .osd { color: #eeeeec; border: none; background-color: rgba(53, 53, 53, 0.7); background-clip: padding-box; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
|
||||
.osd popover.background > arrow, .osd popover.background > contents, popover.background.touch-selection > arrow, popover.background.touch-selection > contents, popover.background.magnifier > arrow, popover.background.magnifier > contents, .app-notification, .app-notification.frame, .osd .scale-popup, .osd { color: #eeeeec; border: none; background-color: rgba(53, 53, 53, 0.7); background-clip: padding-box; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
|
||||
|
||||
.csd popover.background > contents.touch-selection:backdrop, .csd popover.background > contents.magnifier:backdrop, popover.background > contents.touch-selection:backdrop, popover.background > contents.magnifier:backdrop, .csd popover.background > contents.osd:backdrop, popover.background > contents.osd:backdrop, .app-notification:backdrop, .osd .scale-popup:backdrop, .osd:backdrop { text-shadow: none; -gtk-icon-shadow: none; }
|
||||
.osd popover.background > arrow:backdrop, .osd popover.background > contents:backdrop, popover.background.touch-selection > arrow:backdrop, popover.background.touch-selection > contents:backdrop, popover.background.magnifier > arrow:backdrop, popover.background.magnifier > contents:backdrop, .app-notification:backdrop, .osd .scale-popup:backdrop, .osd:backdrop { text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
/********************* Spinner Animation * */
|
||||
@keyframes spin { to { -gtk-icon-transform: rotate(1turn); } }
|
||||
@@ -283,25 +285,25 @@ button.osd:disabled:backdrop, button.osd:disabled { color: #919190; border-color
|
||||
|
||||
button.osd:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(53, 53, 53, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; border: none; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button, .csd popover.background > contents.magnifier button, popover.background > contents.touch-selection button, popover.background > contents.magnifier button, .app-notification button, .app-notification.frame button, .osd button { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(53, 53, 53, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
popover.background.touch-selection button, popover.background.magnifier button, .app-notification button, .app-notification.frame button, .osd button { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(53, 53, 53, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.csd popover.background > contents.touch-selection button:hover, .csd popover.background > contents.magnifier button:hover, popover.background > contents.touch-selection button:hover, popover.background > contents.magnifier button:hover, .app-notification button:hover, .osd button:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(83, 83, 83, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
popover.background.touch-selection button:hover, popover.background.magnifier button:hover, .app-notification button:hover, .osd button:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(83, 83, 83, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.csd popover.background > contents.touch-selection button:active:backdrop, .csd popover.background > contents.magnifier button:active:backdrop, popover.background > contents.touch-selection button:active:backdrop, popover.background > contents.magnifier button:active:backdrop, .app-notification button:active:backdrop, .csd popover.background > contents.touch-selection button:active, .csd popover.background > contents.magnifier button:active, popover.background > contents.touch-selection button:active, popover.background > contents.magnifier button:active, .app-notification button:active, .csd popover.background > contents.touch-selection button:checked:backdrop, .csd popover.background > contents.magnifier button:checked:backdrop, popover.background > contents.touch-selection button:checked:backdrop, popover.background > contents.magnifier button:checked:backdrop, .app-notification button:checked:backdrop, .csd popover.background > contents.touch-selection button:checked, .csd popover.background > contents.magnifier button:checked, popover.background > contents.touch-selection button:checked, popover.background > contents.magnifier button:checked, .app-notification button:checked, .osd button:active:backdrop, .osd button:active, .osd button:checked:backdrop, .osd button:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
popover.background.touch-selection button:active:backdrop, popover.background.magnifier button:active:backdrop, .app-notification button:active:backdrop, popover.background.touch-selection button:active, popover.background.magnifier button:active, .app-notification button:active, popover.background.touch-selection button:checked:backdrop, popover.background.magnifier button:checked:backdrop, .app-notification button:checked:backdrop, popover.background.touch-selection button:checked, popover.background.magnifier button:checked, .app-notification button:checked, .osd button:active:backdrop, .osd button:active, .osd button:checked:backdrop, .osd button:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.csd popover.background > contents.touch-selection button:disabled:backdrop, .csd popover.background > contents.magnifier button:disabled:backdrop, popover.background > contents.touch-selection button:disabled:backdrop, popover.background > contents.magnifier button:disabled:backdrop, .app-notification button:disabled:backdrop, .csd popover.background > contents.touch-selection button:disabled, .csd popover.background > contents.magnifier button:disabled, popover.background > contents.touch-selection button:disabled, popover.background > contents.magnifier button:disabled, .app-notification button:disabled, .osd button:disabled:backdrop, .osd button:disabled { color: #919190; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(71, 71, 71, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
popover.background.touch-selection button:disabled:backdrop, popover.background.magnifier button:disabled:backdrop, .app-notification button:disabled:backdrop, popover.background.touch-selection button:disabled, popover.background.magnifier button:disabled, .app-notification button:disabled, .osd button:disabled:backdrop, .osd button:disabled { color: #919190; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(71, 71, 71, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button:backdrop, .csd popover.background > contents.magnifier button:backdrop, popover.background > contents.touch-selection button:backdrop, popover.background > contents.magnifier button:backdrop, .app-notification button:backdrop, .osd button:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(53, 53, 53, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
popover.background.touch-selection button:backdrop, popover.background.magnifier button:backdrop, .app-notification button:backdrop, .osd button:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(53, 53, 53, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button.flat, .csd popover.background > contents.magnifier button.flat, popover.background > contents.touch-selection button.flat, popover.background > contents.magnifier button.flat, .app-notification button.flat, .osd button.flat { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; box-shadow: none; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
|
||||
popover.background.touch-selection button.flat, popover.background.magnifier button.flat, .app-notification button.flat, .osd button.flat { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; box-shadow: none; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button.flat:hover, .csd popover.background > contents.magnifier button.flat:hover, popover.background > contents.touch-selection button.flat:hover, popover.background > contents.magnifier button.flat:hover, .app-notification button.flat:hover, .osd button.flat:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(83, 83, 83, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
popover.background.touch-selection button.flat:hover, popover.background.magnifier button.flat:hover, .app-notification button.flat:hover, .osd button.flat:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(83, 83, 83, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
.csd popover.background > contents.touch-selection button.flat:disabled, .csd popover.background > contents.magnifier button.flat:disabled, popover.background > contents.touch-selection button.flat:disabled, popover.background > contents.magnifier button.flat:disabled, .app-notification button.flat:disabled, .osd button.flat:disabled { color: #919190; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(71, 71, 71, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-image: none; border-color: transparent; box-shadow: none; }
|
||||
popover.background.touch-selection button.flat:disabled, popover.background.magnifier button.flat:disabled, .app-notification button.flat:disabled, .osd button.flat:disabled { color: #919190; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(71, 71, 71, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-image: none; border-color: transparent; box-shadow: none; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button.flat:backdrop, .csd popover.background > contents.magnifier button.flat:backdrop, popover.background > contents.touch-selection button.flat:backdrop, popover.background > contents.magnifier button.flat:backdrop, .app-notification button.flat:backdrop, .osd button.flat:backdrop { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; }
|
||||
popover.background.touch-selection button.flat:backdrop, popover.background.magnifier button.flat:backdrop, .app-notification button.flat:backdrop, .osd button.flat:backdrop { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.csd popover.background > contents.touch-selection button.flat:active, .csd popover.background > contents.magnifier button.flat:active, popover.background > contents.touch-selection button.flat:active, popover.background > contents.magnifier button.flat:active, .app-notification button.flat:active, .csd popover.background > contents.touch-selection button.flat:checked, .csd popover.background > contents.magnifier button.flat:checked, popover.background > contents.touch-selection button.flat:checked, popover.background > contents.magnifier button.flat:checked, .app-notification button.flat:checked, .osd button.flat:active, .osd button.flat:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
popover.background.touch-selection button.flat:active, popover.background.magnifier button.flat:active, .app-notification button.flat:active, popover.background.touch-selection button.flat:checked, popover.background.magnifier button.flat:checked, .app-notification button.flat:checked, .osd button.flat:active, .osd button.flat:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
|
||||
|
||||
button.suggested-action { color: white; outline-color: rgba(255, 255, 255, 0.3); border-color: #1b6acb; border-bottom-color: #15539e; background-image: linear-gradient(to top, #2379e2 2px, #3584e4); text-shadow: 0 -1px rgba(0, 0, 0, 0.559216); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.559216); box-shadow: inset 0 1px rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.07); }
|
||||
|
||||
@@ -889,25 +891,23 @@ menuitem check:dir(ltr), menuitem radio:dir(ltr) { margin-right: 7px; }
|
||||
menuitem check:dir(rtl), menuitem radio:dir(rtl) { margin-left: 7px; }
|
||||
|
||||
/*************** Popovers * */
|
||||
popover.background { background-color: transparent; padding: 0px; }
|
||||
popover.background { background-color: transparent; font: initial; }
|
||||
|
||||
popover.menu > arrow, popover > arrow { background-color: #f6f5f4; border: 1px solid #cdc7c2; }
|
||||
popover.background > arrow, popover.background > contents { background-color: #f6f5f4; border: 1px solid #cdc7c2; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); }
|
||||
|
||||
popover > contents { padding: 8px; background-color: #f6f5f4; border: 1px solid #cdc7c2; margin: 0px; }
|
||||
popover.background > arrow:backdrop, popover.background > contents:backdrop { background-color: #f6f5f4; border-color: #d5d0cc; box-shadow: none; }
|
||||
|
||||
popover.background > contents { background-color: #f6f5f4; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); }
|
||||
|
||||
.csd popover.background > contents, popover.background > contents { border: 1px solid #cdc7c2; border-radius: 9px; }
|
||||
|
||||
popover.background > contents:backdrop { background-color: #f6f5f4; box-shadow: none; }
|
||||
popover.background > contents { padding: 8px; border-radius: 9px; }
|
||||
|
||||
popover.background > contents > list, popover.background > contents > .view, popover.background > contents > iconview, popover.background > contents > toolbar { border-style: none; background-color: transparent; }
|
||||
|
||||
.csd popover.background > contents.touch-selection, .csd popover.background > contents.magnifier, popover.background > contents.touch-selection, popover.background > contents.magnifier { border: 1px solid rgba(255, 255, 255, 0.1); }
|
||||
|
||||
popover.background > contents separator { margin: 3px; }
|
||||
|
||||
popover.background > contents list separator { margin: 0px; }
|
||||
popover.background > contents list separator { margin: 0; }
|
||||
|
||||
.osd popover.background, popover.background.touch-selection, popover.background.magnifier { background-color: transparent; }
|
||||
|
||||
.osd popover.background > arrow, .osd popover.background > contents, popover.background.touch-selection > arrow, popover.background.touch-selection > contents, popover.background.magnifier > arrow, popover.background.magnifier > contents { border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: none; }
|
||||
|
||||
/************* Notebooks * */
|
||||
notebook > header { padding: 1px; border-color: #cdc7c2; border-width: 1px; background-color: #e1dedb; }
|
||||
@@ -1163,7 +1163,7 @@ row:selected switch slider:checked, row:selected switch slider { border-color: #
|
||||
|
||||
.view.content-view.check:active:not(list), iconview.content-view.check:active:not(list), .content-view .tile check:active:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(53, 132, 228, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.view.content-view.check:backdrop:not(list), iconview.content-view.check:backdrop:not(list), .content-view .tile check:backdrop:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(140, 140, 141, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
|
||||
.view.content-view.check:backdrop:not(list), iconview.content-view.check:backdrop:not(list), .content-view .tile check:backdrop:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(141, 141, 141, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
|
||||
|
||||
.view.content-view.check:checked:not(list), iconview.content-view.check:checked:not(list), .content-view .tile check:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: #eeeeec; background-color: rgba(53, 132, 228, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
|
||||
|
||||
@@ -1171,7 +1171,7 @@ row:selected switch slider:checked, row:selected switch slider { border-color: #
|
||||
|
||||
.view.content-view.check:checked:active:not(list), iconview.content-view.check:checked:active:not(list), .content-view .tile check:checked:active:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: #eeeeec; background-color: rgba(53, 132, 228, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
|
||||
|
||||
.view.content-view.check:backdrop:checked:not(list), iconview.content-view.check:backdrop:checked:not(list), .content-view .tile check:backdrop:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: rgba(238, 238, 236, 0.8); background-color: rgba(140, 140, 141, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
|
||||
.view.content-view.check:backdrop:checked:not(list), iconview.content-view.check:backdrop:checked:not(list), .content-view .tile check:backdrop:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: rgba(238, 238, 236, 0.8); background-color: rgba(141, 141, 141, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
|
||||
|
||||
checkbutton.text-button, radiobutton.text-button { padding: 2px 0; outline-offset: 0; border-spacing: 4px; }
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,681 @@
|
||||
#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_label_set_max_width_chars (GTK_LABEL (data->name), 25);
|
||||
gtk_label_set_ellipsize (GTK_LABEL (data->name), PANGO_ELLIPSIZE_END);
|
||||
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), 800, 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_grid_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_grid_view_set_model (GTK_GRID_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