Compare commits

...

71 Commits

Author SHA1 Message Date
Matthias Clasen 543cf3f907 Add search support
Add a filter to GtkListBase, and move the selection
to the first matching item whenever the filter changes.
This is meant to be used with single selection and
a string filter that is hooked up to a search entry.
2020-06-01 08:40:36 -04:00
Matthias Clasen e904668cb1 gtk-demo: Make gridview demo use rubberbanding 2020-06-01 08:39:52 -04:00
Matthias Clasen 15d494ddc4 Add rubberband api
Add an enable-rubberband property to GtkListView,
GtkGridView and GtkColumnView.
2020-06-01 08:39:52 -04:00
Matthias Clasen 92729a9551 listbase: Add rubberband selection
Implement the typical rubberband selection, including
autoscroll. This is only useful with multiselection,
and not very compatible with single-click-activate.
Therefore, it is not enabled by default, and needs
to be turned on with the enable-rubber-band property.
2020-06-01 08:39:52 -04:00
Matthias Clasen e98f70d6d5 Add GtkMultiSelection
This is implemented using a private GtkSet helper.

Includes tests.
2020-06-01 08:39:52 -04:00
Matthias Clasen 84b6d14ad2 inspector: Expand the actions list 2020-06-01 08:19:37 -04:00
Matthias Clasen 3f5e859795 inspector: Expand the resource list
This is an experiment with adding a filler column.
2020-06-01 08:19:37 -04:00
Matthias Clasen 780a9c1a1f inspector: Expand the property list
It looks better this way.
2020-06-01 08:19:37 -04:00
Matthias Clasen b9c3a15d60 inspector: Expand the object tree
This is how it used to look, and it looks better that way.
2020-06-01 08:19:37 -04:00
Matthias Clasen 076a40c9bb columnview: Take expand into account
When allocating columns, distribute extra space
to columns that have expand set to TRUE.
2020-06-01 08:19:37 -04:00
Matthias Clasen d8ab29a431 columnview: Add a GtkColumnViewColumn:expand property
This will be used to determine how to distribute
available extra space in a column view.
2020-06-01 08:19:37 -04:00
Matthias Clasen ea0b36b330 columnview: Add autoscroll
Autoscroll when the pointer gets close to the
edge during column resizing or reordering. This
is similar to what the treeview does, but it is
implemented using a tick callback, and has
variable speed.
2020-06-01 08:16:56 -04:00
Matthias Clasen c8157480ce columnview: Allow to cancel reorder with Escape
The treeview does this too.
2020-06-01 08:16:56 -04:00
Matthias Clasen 3eb0f92705 columnview: Interactive column reordering
Allow rearranging columns by dragging, in the same
way the treeview does.

We add the "dnd" style class to the header while
it is dragged, and we move the header of the dragged
column to the end of its parents children, so that
it gets drawn on top.
2020-06-01 08:16:56 -04:00
Matthias Clasen 10ac5cd977 columnview: Add a GtkColumnViewColumn:reorderable property
This will be used for interactive column reordering
in the future.
2020-06-01 08:16:56 -04:00
Matthias Clasen d41fe03b73 columnviewlayout: Use header allocation for titles
Normally, this will be identical to the column
allocation, but we will temporarily change it
during column reordering.
2020-06-01 08:16:56 -04:00
Matthias Clasen bf8856e3fb columnviewcolumn: Add reordering helpers
Add helper functions that let us temporarily give
a different allocation to headers. These will be
used to implement interactive column reordering
in GtkColumnView.
2020-06-01 08:16:56 -04:00
Matthias Clasen 4a46e8331e columnviewtitle: Trigger action on release
This is necessary to make drag-to-reorder work
without triggering resorting.
2020-06-01 08:16:56 -04:00
Matthias Clasen 241b20c39f columnview: Interactive column resizing
This copies just enough of the treeview code to
get columns moving.
2020-06-01 08:10:53 -04:00
Matthias Clasen 151cd0a296 columnviewcolumn: Add a helper
We need to check whether clicks are in the headers
of columns, so let the column view get at the the
header widget.
2020-06-01 08:10:53 -04:00
Matthias Clasen 05b6becd2f columnview: Add a GtkColumnViewColumn:resizable property
This will be used for interactive column resizing
in the future.
2020-06-01 08:10:53 -04:00
Matthias Clasen a2d6f1f0cb columnview: Add a helper
The column code needs to get access to the
listitem widgets that are children of the listview,
so add a getter.
2020-06-01 08:10:53 -04:00
Matthias Clasen d2af96f9c8 columnview: Add GtkColumnViewColumn:fixed-width
Add a fixed-width property similar to the same property
of GtkTreeViewColumn.
2020-06-01 08:10:53 -04:00
Matthias Clasen b1ab5e187d columnview: Implement horizontal scrolling
The listview inside always thinks it gets its full size,
and updates its horizontal adjustment accordingly.

So keep our own adjustment, and update it when allocating.
2020-06-01 07:47:49 -04:00
Matthias Clasen fd51444967 columnview: Revise scroll-minimum handling
Tweak the behavior slightly. We don't show
a scrollbar as long as we have at least
min-size available, but we still give the
entire size to the child, up to nat-size.

This matches how viewports handle scroll-minimum.
2020-06-01 07:47:49 -04:00
Matthias Clasen 843999265b print backend: Fix list model handling in dispose
The print backends do some complicated dispose handling
where the implementations call gtk_print_backend_destroy().

Our tests (in particular, the templates test) trigger
situations where we use print backends after dispose,
and they can't handle the printers listmodel being
NULL at that time. So just remove the printers in
dispose, keep the empty liststore until finalize.
2020-06-01 07:46:58 -04:00
Matthias Clasen 8dfbef5e6f Merge branch 'matthiasc/for-master' into 'master'
columnview: Add column reordering

See merge request GNOME/gtk!2005
2020-06-01 11:28:28 +00:00
Emmanuele Bassi 0b9a3fc160 Merge branch 'ebassi/scroll-position' into 'master'
Remove position from GdkScrollEvent

See merge request GNOME/gtk!2012
2020-06-01 10:32:55 +00:00
Emmanuele Bassi 79105962a7 Remove position from GdkScrollEvent
Scroll events do not have a position, so they shouldn't implement the
GdkEventClass.get_position() virtual function; nor they should have an x
and y fields that never get updated.
2020-06-01 10:49:33 +01:00
Matthias Clasen cc878160bd gtk-demo: Fix a crash in the puzzle
Avoid a crash when clicking the refresh button
after solving the puzzle.
2020-06-01 00:11:50 -04:00
Matthias Clasen 382153e82d gtk-demo: Fix keynav in the puzzle
We need to make shortcut controller have global
scope, otherwise the shortcuts lose against the
window keybindings.
2020-06-01 00:11:43 -04:00
Matthias Clasen e195341e30 gtk-demo: Flesh out the settings demo
Add columns for type and default value.
2020-05-31 23:43:58 -04:00
Matthias Clasen c6c8263704 columnview: Add column reordering
Add an API to allow reordering columns.
2020-05-31 21:56:30 -04:00
Matthias Clasen ed02bea20d Merge branch 'action-tests' into 'master'
Action tests

See merge request GNOME/gtk!2003
2020-05-31 23:32:44 +00:00
Matthias Clasen f89a1b05fe Merge branch 'matthiasc/for-master' into 'master'
Add a forgotten export

See merge request GNOME/gtk!2002
2020-05-31 22:54:38 +00:00
Matthias Clasen af65b7e6d7 More action tests
Test actions under hierarchy changes.
2020-05-31 18:13:50 -04:00
Matthias Clasen 1995ad6d63 Add a forgotten export
gtk_column_view_sort_by was missing a GDK_AVAILABLE_IN_ALL
annotation, causing it to not be exported.
2020-05-31 16:47:53 -04:00
Matthias Clasen 83c0937a46 testsuite: Beef up action tests
Check return values from  gtk_widget_activate.
2020-05-31 16:40:50 -04:00
Matthias Clasen f5cc2c0d20 Merge branch 'column-visible' into 'master'
columnview: Add a GtkColumnViewColumn:visible property

See merge request GNOME/gtk!1997
2020-05-31 20:39:06 +00:00
Emmanuele Bassi 931d68bc92 Merge branch 'drop-coverflow' into 'master'
Drop GtkCoverFlow for now

See merge request GNOME/gtk!2000
2020-05-31 19:12:32 +00:00
Yuri Chornoivan 06946c35d3 Update Ukrainian translation 2020-05-31 19:05:20 +00:00
Matthias Clasen cbe81fd443 Merge branch 'drop-fallback-c89' into 'master'
Drop fallback-c89.c

See merge request GNOME/gtk!1999
2020-05-31 18:14:14 +00:00
Matthias Clasen 173e919d2a Merge branch 'matthiasc/for-master' into 'master'
Matthiasc/for master

See merge request GNOME/gtk!1995
2020-05-31 18:13:36 +00:00
Matthias Clasen 73fdb8f7b0 Drop GtkCoverFlow for now
There is no agreement that a coverflow widget is
appropriate for GTK 4.

It would be ok as a demo if it could live in gtk-demo,
but that requires us to make GtkListBase public first.
The demo is also somewhat rough and needs more work
to look plausible.

Drop GtkCoverFlow and the related demo for now.
2020-05-31 13:15:51 -04:00
Matthias Clasen 8e0ba37866 gtk-demo: Fixes for the sliding puzzle
We were calling check_solved only for key presses,
meaning you could never solve the puzzle with the
mouse.
2020-05-31 13:01:17 -04:00
Matthias Clasen 8780f50e7e gtk-demo: Some fixes to awards
Fix things up enough to make the list show up.
Still not a working example, but closer.
2020-05-31 13:01:17 -04:00
Matthias Clasen 06dc357ea0 printer: Fix the default value of icon-name
Just set this to "printer", so we don't have
to fix it up in the print dialog.
2020-05-31 13:01:17 -04:00
Matthias Clasen fb587cb529 printbackend: Add a list model getter
Now that we have a list model for printers,
we can start using it.
2020-05-31 13:01:17 -04:00
Matthias Clasen a026677849 printbackend: Use a list store 2020-05-31 13:01:17 -04:00
Matthias Clasen 1c389b1f24 gtk-demo: Add more scrolling benchmarks
Add a listview and gridview to the scrolling
benchmarks.
2020-05-31 13:01:17 -04:00
Emmanuele Bassi 55b9aa0309 Merge branch 'ebassi/annotations' into 'master'
Ebassi/annotations

See merge request GNOME/gtk!1996
2020-05-31 15:18:36 +00:00
Christoph Reiter 2d5cf2b4c4 Drop fallback-c89.c
We require a C compiler supporting C99 now. The main purpose of
these fallbacks was for MSVC. From what I can see this is now all supported
by MSVC 2015+ anyway.

The only other change this includes is to replace isnanf() with the
(type infering) C99 isnan() macro, because MSVC doesn't provide isnanf().
2020-05-31 17:09:23 +02:00
Matthias Clasen 4877bb5b5c columnviewcolumn: Add a visible property
This lets us hide columns, which is an expected
feature of columned lists.
2020-05-31 10:47:48 -04:00
Emmanuele Bassi b9269d5644 Annotate the GtkFunctionListItemFactory constructor
But skip it, since it cannot really be used by language bindings, as it
binds the same user data to two separate functions.
2020-05-31 15:14:26 +01:00
Emmanuele Bassi 1a4723988e Fix annotations for gtk_cclosure_expression_new()
Missing scope annotations for the various functions.
2020-05-31 15:10:27 +01:00
Matthias Clasen 93353888ca Merge branch 'columnview-1' into 'master'
Convert the inspector to column views

See merge request GNOME/gtk!1994
2020-05-31 14:06:31 +00:00
Emmanuele Bassi c3a90b76d0 Add missing separator
An annotation is separated from the description by a ':'.
2020-05-31 15:05:59 +01:00
Emmanuele Bassi 43e5ca9a1b docs: Fix annotations
- Use the same name for the argument in the declaration, definition,
   and documentation
 - Use "optional" instead of the deprecated "allow-none"
2020-05-31 15:04:51 +01:00
Christoph Reiter c5829bd0e2 fallback-c89: Try to make isnanf work 2020-05-31 15:50:09 +02:00
Matthias Clasen 2a9d2cc7e9 inspector: Touch up list styling
This is just the minimal amount of work to make
headers recognizable.
2020-05-31 09:24:19 -04:00
Matthias Clasen 0d9873d8bc inspector: Use a column view for actions
A straight conversion from list box to column view.
2020-05-31 09:24:12 -04:00
Matthias Clasen 23bc38ed06 inspector: Make the resource list sortable
This is using a GtkTreeListRowSorter to keep expanded
state of the tree while changing the sorting.
2020-05-31 09:24:04 -04:00
Matthias Clasen 42d26b4b05 inspector: Use a column view for the resource list
A conversion from tree view to column view.
2020-05-31 09:23:51 -04:00
Matthias Clasen 04700789e1 inspector: Use a column view for properties
Just a straight conversion from list box to column view.
2020-05-31 09:23:45 -04:00
Matthias Clasen e23baa59f0 inspector: Add columns to the object tree
Add columnview columns in the object tree.
We do the same for treeview columns.
2020-05-31 09:23:37 -04:00
Matthias Clasen e8e28b61db Merge branch 'fix-mnemonic-zero' into 'master'
label: Fix mnemonic zero

Closes #2805

See merge request GNOME/gtk!1993
2020-05-31 13:02:51 +00:00
Yuri Chornoivan b766707f28 Update Ukrainian translation 2020-05-31 06:12:49 +00:00
Yuri Chornoivan 0787f262a0 Update Ukrainian translation 2020-05-31 06:09:05 +00:00
Matthias Clasen d833f4a632 Merge branch 'dropdown-for-merge' into 'master'
Dropdown for merge

Closes #2214

See merge request GNOME/gtk!1992
2020-05-31 05:34:18 +00:00
Matthias Clasen ce1e364ab8 Merge branch 'listview-for-merge' into 'master'
Listview for merge

Closes #2214

See merge request GNOME/gtk!1991
2020-05-31 05:32:37 +00:00
Matthias Clasen adb5b299c9 label: Fix mnemonic zero
We were confusing '\0' and '0' when parsing the label
for mnemonics.

Fixes: #2805
2020-05-30 21:42:54 -04:00
96 changed files with 5934 additions and 2932 deletions
-27
View File
@@ -27,18 +27,6 @@
/* Define to 1 if you have the `dcgettext' function. */
#mesondefine HAVE_DCGETTEXT
/* Define to 1 if you have the declaration of `isinf', and to 0 if you don't.
*/
#mesondefine HAVE_DECL_ISINF
/* Define to 1 if you have the declaration of `isnan', and to 0 if you don't.
*/
#mesondefine HAVE_DECL_ISNAN
/* Define to 1 if you have the declaration of `isnanf', and to 0 if you don't.
*/
#mesondefine HAVE_DECL_ISNANF
/* Define to 1 if you have the <dlfcn.h> header file. */
#mesondefine HAVE_DLFCN_H
@@ -84,9 +72,6 @@
/* Define to 1 if you have a working `mmap' system call. */
#mesondefine HAVE_MMAP
/* Define to 1 if you have the `nearbyint' function. */
#mesondefine HAVE_NEARBYINT
/* Define to 1 if you have the `posix_fallocate' function. */
#mesondefine HAVE_POSIX_FALLOCATE
@@ -96,21 +81,9 @@
/* Have the Xrandr 1.5 extension library */
#mesondefine HAVE_RANDR15
/* Define to 1 if you have the `rint' function. */
#mesondefine HAVE_RINT
/* Define to 1 if you have the `round' function. */
#mesondefine HAVE_ROUND
/* Define to 1 if you have the `sincos' function. */
#mesondefine HAVE_SINCOS
/* Define to 1 if you have the `log2` function */
#mesondefine HAVE_LOG2
/* Define to 1 if you ahve the `exp2` function */
#mesondefine HAVE_EXP2
/* Define to 1 if you have the <stdint.h> header file. */
#mesondefine HAVE_STDINT_H
+9 -2
View File
@@ -3,8 +3,15 @@
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="label" bind-source="GtkListItem" bind-property="position"></property>
<property name="margin">6</property>
<binding name="label">
<lookup name="title" type="GtkAward">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
</object>
</property>
</template>
-1
View File
@@ -218,7 +218,6 @@
<file>infobar.c</file>
<file>links.c</file>
<file>listbox.c</file>
<file>listview_applauncher.c</file>
<file>listview_colors.c</file>
<file>listview_clocks.c</file>
<file>listview_filebrowser.c</file>
+50 -1
View File
@@ -12,7 +12,7 @@ static GtkWidget *window = NULL;
static GtkWidget *scrolledwindow;
static int selected;
#define N_WIDGET_TYPES 4
#define N_WIDGET_TYPES 6
static int hincrement = 5;
@@ -64,6 +64,7 @@ populate_icons (void)
gtk_grid_attach (GTK_GRID (grid), create_icon (), left, top, 1, 1);
hincrement = 0;
vincrement = 5;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_NEVER,
@@ -100,6 +101,7 @@ populate_text (gboolean hilight)
gtk_text_view_set_buffer (GTK_TEXT_VIEW (textview), buffer);
hincrement = 0;
vincrement = 5;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_NEVER,
@@ -124,6 +126,7 @@ populate_image (void)
gtk_picture_set_can_shrink (GTK_PICTURE (image), FALSE);
hincrement = 5;
vincrement = 5;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_AUTOMATIC,
@@ -131,6 +134,42 @@ populate_image (void)
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolledwindow), image);
}
extern GtkWidget *create_weather_view (void);
static void
populate_list (void)
{
GtkWidget *list;
list = create_weather_view ();
hincrement = 5;
vincrement = 0;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolledwindow), list);
}
extern GtkWidget *create_color_grid (void);
static void
populate_grid (void)
{
GtkWidget *list;
list = create_color_grid ();
hincrement = 0;
vincrement = 5;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolledwindow), list);
}
static void
set_widget_type (int type)
{
@@ -164,6 +203,16 @@ set_widget_type (int type)
populate_image ();
break;
case 4:
gtk_window_set_title (GTK_WINDOW (window), "Scrolling a list");
populate_list ();
break;
case 5:
gtk_window_set_title (GTK_WINDOW (window), "Scrolling a grid");
populate_grid ();
break;
default:
g_assert_not_reached ();
}
-199
View File
@@ -1,199 +0,0 @@
/* Lists/Application launcher
*
* This demo uses the GtkCoverFlow widget as a fancy application launcher.
*
* It is also a very small introduction to listviews.
*/
#include <gtk/gtk.h>
/* This is the function that creates the #GListModel that we need.
* GTK list widgets need a #GListModel to display, as models support change
* notifications.
* Unfortunately various older APIs do not provide list models, so we create
* our own.
*/
static GListModel *
create_application_list (void)
{
GListStore *store;
GList *apps, *l;
/* We use a #GListStore here, which is a simple array-like list implementation
* for manual management.
* List models need to know what type of data they provide, so we need to
* provide the type here. As we want to do a list of applications, #GAppInfo
* is the object we provide.
*/
store = g_list_store_new (G_TYPE_APP_INFO);
apps = g_app_info_get_all ();
for (l = apps; l; l = l->next)
{
g_list_store_append (store, l->data);
}
g_list_free_full (apps, g_object_unref);
return G_LIST_MODEL (store);
}
/* This is the function we use for setting up new listitems to display.
* We add just a #GtkImage here to display the application's icon as this is just
* a simple demo.
*/
static void
setup_listitem_cb (GtkListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *image;
image = gtk_image_new ();
gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
gtk_list_item_set_child (list_item, image);
}
/* Here we need to prepare the listitem for displaying its item. We get the
* listitem already set up from the previous function, so we can reuse the
* #GtkImage widget we set up above.
* We get the item - which we know is a #GAppInfo because it comes out of
* the model we set up above, grab its icon and display it.
*/
static void
bind_listitem_cb (GtkListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *image;
GAppInfo *app_info;
image = gtk_list_item_get_child (list_item);
app_info = gtk_list_item_get_item (list_item);
gtk_image_set_from_gicon (GTK_IMAGE (image), g_app_info_get_icon (app_info));
}
/* In more complex code, we would also need functions to unbind and teardown
* the listitem, but this is simple code, so the default implementations are
* enough. If we had connected signals, this step would have been necessary.
*
* The #GtkSignalListItemFactory documentation contains more information about
* this step.
*/
/* This function is called whenever an item in the list is activated. This is
* the simple way to allow reacting to the Enter key or double-clicking on a
* listitem.
* Of course, it is possible to use far more complex interactions by turning
* off activation and adding buttons or other widgets in the setup function
* above, but this is a simple demo, so we'll use the simple way.
*/
static void
activate_cb (GtkCoverFlow *coverflow,
guint position,
gpointer unused)
{
GAppInfo *app_info;
GdkAppLaunchContext *context;
GError *error = NULL;
app_info = g_list_model_get_item (gtk_cover_flow_get_model (coverflow), position);
/* Prepare the context for launching the application and launch it. This
* code is explained in detail in the documentation for #GdkAppLaunchContext
* and #GAppInfo.
*/
context = gdk_display_get_app_launch_context (gtk_widget_get_display (GTK_WIDGET (coverflow)));
if (!g_app_info_launch (app_info,
NULL,
G_APP_LAUNCH_CONTEXT (context),
&error))
{
GtkWidget *dialog;
/* And because error handling is important, even a simple demo has it:
* We display an error dialog that something went wrong.
*/
dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (coverflow))),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"Could not launch %s", g_app_info_get_display_name (app_info));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
g_clear_error (&error);
gtk_widget_show (dialog);
}
g_object_unref (context);
g_object_unref (app_info);
}
static GtkWidget *window = NULL;
GtkWidget *
do_listview_applauncher (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *coverflow, *sw;;
GListModel *model;
GtkListItemFactory *factory;
/* Create a window and set a few defaults */
window = gtk_window_new ();
gtk_window_set_default_size (GTK_WINDOW (window), 640, 320);
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Application Launcher");
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
/* The #GtkListitemFactory is what is used to create #GtkListItems
* to display the data from the model. So it is absolutely necessary
* to create one.
* We will use a #GtkSignalListItemFactory because it is the simplest
* one to use. Different ones are available for different use cases.
* The most powerful one is #GtkBuilderListItemFactory which uses
* #GtkBuilder .ui files, so it requires little code.
*/
factory = gtk_signal_list_item_factory_new ();
g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
g_signal_connect (factory, "bind", G_CALLBACK (bind_listitem_cb), NULL);
/* Create the list widget here: We use a coverflow widget because it's
* the coolest one. We could just as well use other list widgets such
* as a #GtkListView or a #GtkGridView and the code would look very
* similar.
*/
coverflow = gtk_cover_flow_new_with_factory (factory);
/* We connect the activate signal here. It's the function we defined
* above for launching the selected application.
*/
g_signal_connect (coverflow, "activate", G_CALLBACK (activate_cb), NULL);
/* And of course we need to set the data model. Here we call the function
* we wrote above that gives us the list of applications. Then we set
* it on the coverflow list widget.
* The coverflow will now take items from the model and use the factory
* to create as many listitems as it needs to show itself to the user.
*/
model = create_application_list ();
gtk_cover_flow_set_model (GTK_COVER_FLOW (coverflow), model);
g_object_unref (model);
/* List widgets should always be contained in a #GtkScrolledWindow,
* because otherwise they might get too large or they might not
* be scrollable.
*/
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_window_set_child (GTK_WINDOW (window), sw);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), coverflow);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
+33 -12
View File
@@ -310,6 +310,7 @@ setup_simple_listitem_cb (GtkListItemFactory *factory,
color_expression = gtk_property_expression_new (GTK_TYPE_LIST_ITEM, expression, "item");
picture = gtk_picture_new ();
gtk_widget_set_size_request (picture, 32, 32);
gtk_expression_bind (color_expression, picture, "paintable", NULL);
gtk_list_item_set_child (list_item, picture);
@@ -404,6 +405,34 @@ set_item (GBinding *binding,
return TRUE;
}
GtkWidget *
create_color_grid (void)
{
GtkWidget *gridview;
GtkListItemFactory *factory;
GListModel *model, *selection;
gridview = gtk_grid_view_new ();
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
factory = gtk_signal_list_item_factory_new ();
g_signal_connect (factory, "setup", G_CALLBACK (setup_simple_listitem_cb), NULL);
gtk_grid_view_set_factory (GTK_GRID_VIEW (gridview), factory);
g_object_unref (factory);
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), 24);
gtk_grid_view_set_enable_rubberband (GTK_GRID_VIEW (gridview), TRUE);
model = G_LIST_MODEL (gtk_sort_list_model_new (create_colors_model (), NULL));
selection = G_LIST_MODEL (gtk_multi_selection_new (model));
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), selection);
g_object_unref (selection);
g_object_unref (model);
return gridview;
}
static GtkWidget *window = NULL;
GtkWidget *
@@ -415,7 +444,7 @@ do_listview_colors (GtkWidget *do_widget)
GtkListItemFactory *factory;
GListStore *factories;
GListModel *model;
GtkNoSelection *selection;
GtkSorter *sorter;
GtkSorter *multi_sorter;
GListStore *sorters;
@@ -435,17 +464,10 @@ do_listview_colors (GtkWidget *do_widget)
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_window_set_child (GTK_WINDOW (window), sw);
gridview = gtk_grid_view_new ();
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), 24);
model = G_LIST_MODEL (gtk_sort_list_model_new (create_colors_model (), NULL));
selection = gtk_no_selection_new (model);
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), G_LIST_MODEL (selection));
gridview = create_color_grid ();
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), gridview);
g_object_unref (selection);
model = gtk_grid_view_get_model (GTK_GRID_VIEW (gridview));
g_object_get (model, "model", &model, NULL);
sorters = g_list_store_new (GTK_TYPE_SORTER);
@@ -559,7 +581,6 @@ do_listview_colors (GtkWidget *do_widget)
G_BINDING_SYNC_CREATE,
set_item, NULL,
NULL, NULL);
g_object_unref (model);
}
+21
View File
@@ -25,6 +25,8 @@ enum {
PROP_SUMMARY,
PROP_DESCRIPTION,
PROP_VALUE,
PROP_TYPE,
PROP_DEFAULT_VALUE,
N_PROPS
};
@@ -65,6 +67,21 @@ settings_key_get_property (GObject *object,
}
break;
case PROP_TYPE:
{
const GVariantType *type = g_settings_schema_key_get_value_type (self->key);
g_value_set_string (value, g_variant_type_peek_string (type));
}
break;
case PROP_DEFAULT_VALUE:
{
GVariant *variant = g_settings_schema_key_get_default_value (self->key);
g_value_take_string (value, g_variant_print (variant, FALSE));
g_variant_unref (variant);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -98,6 +115,10 @@ settings_key_class_init (SettingsKeyClass *klass)
g_param_spec_string ("summary", NULL, NULL, NULL, G_PARAM_READABLE);
properties[PROP_VALUE] =
g_param_spec_string ("value", NULL, NULL, NULL, G_PARAM_READABLE);
properties[PROP_TYPE] =
g_param_spec_string ("type", NULL, NULL, NULL, G_PARAM_READABLE);
properties[PROP_DEFAULT_VALUE] =
g_param_spec_string ("default-value", NULL, NULL, NULL, G_PARAM_READABLE);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
+52
View File
@@ -120,6 +120,58 @@
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn">
<property name="title">Type</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<lookup name="type" type="SettingsKey">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn">
<property name="title">Default</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<lookup name="default-value" type="SettingsKey">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
+22 -14
View File
@@ -275,13 +275,33 @@ bind_widget (GtkListItem *list_item,
static GtkWidget *window = NULL;
GtkWidget *
create_weather_view (void)
{
GtkWidget *listview;
GListModel *model, *selection;
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);
return listview;
}
GtkWidget *
do_listview_weather (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *listview, *sw;;
GListModel *model, *selection;
window = gtk_window_new ();
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
@@ -293,19 +313,7 @@ do_listview_weather (GtkWidget *do_widget)
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_window_set_child (GTK_WINDOW (window), sw);
listview = gtk_list_view_new_with_factory (
gtk_functions_list_item_factory_new (setup_widget,
bind_widget,
NULL, NULL));
gtk_orientable_set_orientation (GTK_ORIENTABLE (listview), GTK_ORIENTATION_HORIZONTAL);
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
model = create_weather_model ();
selection = G_LIST_MODEL (gtk_no_selection_new (model));
gtk_list_view_set_model (GTK_LIST_VIEW (listview), selection);
g_object_unref (selection);
g_object_unref (model);
listview = create_weather_view ();
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), listview);
}
-1
View File
@@ -42,7 +42,6 @@ demos = files([
'listbox.c',
'flowbox.c',
'list_store.c',
'listview_applauncher.c',
'listview_clocks.c',
'listview_colors.c',
'listview_filebrowser.c',
+12 -3
View File
@@ -251,6 +251,8 @@ puzzle_button_pressed (GtkGestureClick *gesture,
{
gtk_widget_error_bell (grid);
}
check_solved (grid);
}
static void
@@ -291,6 +293,8 @@ start_puzzle (GdkPaintable *paintable)
/* Add shortcuts so people can use the arrow
* keys to move the puzzle */
controller = gtk_shortcut_controller_new ();
gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER (controller),
GTK_SHORTCUT_SCOPE_GLOBAL);
add_move_binding (GTK_SHORTCUT_CONTROLLER (controller),
GDK_KEY_Left, GDK_KEY_KP_Left,
-1, 0);
@@ -351,11 +355,16 @@ reshuffle (void)
{
GtkWidget *grid;
grid = gtk_aspect_frame_get_child (GTK_ASPECT_FRAME (frame));
if (solved)
start_puzzle (puzzle);
{
start_puzzle (puzzle);
grid = gtk_aspect_frame_get_child (GTK_ASPECT_FRAME (frame));
}
else
shuffle_puzzle (grid);
{
grid = gtk_aspect_frame_get_child (GTK_ASPECT_FRAME (frame));
shuffle_puzzle (grid);
}
gtk_widget_grab_focus (grid);
}
+1
View File
@@ -70,6 +70,7 @@
<xi:include href="xml/gtkselectionmodel.xml" />
<xi:include href="xml/gtknoselection.xml" />
<xi:include href="xml/gtksingleselection.xml" />
<xi:include href="xml/gtkmultiselection.xml" />
<xi:include href="xml/gtkdirectorylist.xml" />
</chapter>
+34
View File
@@ -392,6 +392,16 @@ gtk_single_selection_set_can_unselect
gtk_single_selection_get_type
</SECTION>
<SECTION>
<FILE>gtkmultiselection</FILE>
<TITLE>GtkMultiSeledction</TITLE>
GtkMultiSelection
gtk_multi_selection_new
gtk_multi_selection_copy
<SUBSECTION Private>
gtk_multi_selection_get_type
</SECTION>
<SECTION>
<FILE>gtklistitem</FILE>
<TITLE>GtkListItem</TITLE>
@@ -479,6 +489,11 @@ gtk_list_view_set_show_separators
gtk_list_view_get_show_separators
gtk_list_view_set_single_click_activate
gtk_list_view_get_single_click_activate
gtk_list_view_set_enable_rubberband
gtk_list_view_get_enable_rubberband
gtk_list_view_set_search_filter
gtk_list_view_get_search_filter
gtk_list_view_next_match
<SUBSECTION Standard>
GTK_LIST_VIEW
GTK_LIST_VIEW_CLASS
@@ -496,6 +511,7 @@ gtk_list_view_get_type
GtkColumnView
gtk_column_view_new
gtk_column_view_append_column
gtk_column_view_insert_column
gtk_column_view_remove_column
gtk_column_view_get_columns
gtk_column_view_get_model
@@ -506,6 +522,11 @@ gtk_column_view_set_show_separators
gtk_column_view_sort_by_column
gtk_column_view_set_single_click_activate
gtk_column_view_get_single_click_activate
gtk_column_view_set_enable_rubberband
gtk_column_view_get_enable_rubberband
gtk_column_view_set_search_filter
gtk_column_view_get_search_filter
gtk_column_view_next_match
<SUBSECTION Standard>
GTK_COLUMN_VIEW
GTK_COLUMN_VIEW_CLASS
@@ -530,6 +551,14 @@ gtk_column_view_column_set_title
gtk_column_view_column_get_title
gtk_column_view_column_set_sorter
gtk_column_view_column_get_sorter
gtk_column_view_column_set_visible
gtk_column_view_column_get_visible
gtk_column_view_column_set_reorderable
gtk_column_view_column_get_reorderable
gtk_column_view_column_set_fixed_width
gtk_column_view_column_get_fixed_width
gtk_column_view_column_set_expand
gtk_column_view_column_get_expand
<SUBSECTION Standard>
GTK_COLUMN_VIEW_COLUMN
GTK_COLUMN_VIEW_COLUMN_CLASS
@@ -554,6 +583,11 @@ gtk_grid_view_set_min_columns
gtk_grid_view_get_min_columns
gtk_grid_view_set_single_click_activate
gtk_grid_view_get_single_click_activate
gtk_grid_view_set_enable_rubberband
gtk_grid_view_get_enable_rubberband
gtk_grid_view_set_search_filter
gtk_grid_view_get_search_filter
gtk_grid_view_next_match
<SUBSECTION Standard>
GTK_GRID_VIEW
GTK_GRID_VIEW_CLASS
+1
View File
@@ -139,6 +139,7 @@ gtk_menu_button_get_type
gtk_message_dialog_get_type
gtk_mount_operation_get_type
gtk_multi_filter_get_type
gtk_multi_selection_get_type
gtk_multi_sorter_get_type
gtk_native_get_type
gtk_native_dialog_get_type
-58
View File
@@ -1,58 +0,0 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 2011 Chun-wei Fan <fanc999@yahoo.com.tw>
*
* Author: Chun-wei Fan <fanc999@yahoo.com.tw>
*
* 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/>.
*/
#include "config.h"
#include <float.h>
#ifndef HAVE_DECL_ISNAN
/* it seems of the supported compilers only
* MSVC does not have isnan(), but it does
* have _isnan() which does the same as isnan()
*/
static inline gboolean
isnan (double x)
{
return _isnan (x);
}
#endif
#ifndef HAVE_DECL_ISINF
/* Unfortunately MSVC does not have finite()
* but it does have _finite() which is the same
* as finite() except when x is a NaN
*/
static inline gboolean
isinf (double x)
{
return (!_finite (x) && !_isnan (x));
}
#endif
/* Workaround for round() for non-GCC/non-C99 compilers */
#ifndef HAVE_ROUND
static inline double
round (double x)
{
if (x >= 0)
return floor (x + 0.5);
else
return ceil (x - 0.5);
}
#endif
-3
View File
@@ -26,9 +26,6 @@
#include "gdkintl.h"
#include "gdkkeysprivate.h"
/* for the use of round() */
#include "fallback-c89.c"
/**
* SECTION:gdkdevice
* @Short_description: Object representing an input device
+4 -7
View File
@@ -38,9 +38,6 @@
#include <math.h>
#include <glib.h>
/* for the use of round() */
#include "fallback-c89.c"
/**
* SECTION:gdkdisplay
* @Short_description: Controls a set of monitors and their associated input devices
@@ -1735,11 +1732,11 @@ gdk_display_map_keycode (GdkDisplay *display,
* @keycode: a keycode
* @state: a modifier state
* @group: active keyboard group
* @keyval: (out) (allow-none): return location for keyval, or %NULL
* @effective_group: (out) (allow-none): return location for effective
* @keyval: (out) (optional): return location for keyval, or %NULL
* @effective_group: (out) (optional): return location for effective
* group, or %NULL
* @level: (out) (allow-none): return location for level, or %NULL
* @consumed_modifiers: (out) (allow-none): return location for modifiers
* @level: (out) (optional): return location for level, or %NULL
* @consumed: (out) (optional): return location for modifiers
* that were used to determine the group or level, or %NULL
*
* Translates the contents of a #GdkEventKey (ie @keycode, @state, and @group)
+3 -25
View File
@@ -2130,7 +2130,7 @@ gdk_focus_event_new (GdkSurface *surface,
/**
* gdk_focus_event_get_in:
* @event: (type GdkScrollEvent): a focus change event
* @event: (type GdkFocusEvent): a focus change event
*
* Extracts whether this event is about focus entering or
* leaving the surface.
@@ -2152,15 +2152,6 @@ gdk_focus_event_get_in (GdkEvent *event)
/* {{{ GdkScrollEvent */
static void
gdk_scroll_event_init (GdkEvent *event)
{
GdkScrollEvent *self = (GdkScrollEvent *) event;
self->x = NAN;
self->y = NAN;
}
static void
gdk_scroll_event_finalize (GdkEvent *event)
{
@@ -2179,19 +2170,6 @@ gdk_scroll_event_get_state (GdkEvent *event)
return self->state;
}
static gboolean
gdk_scroll_event_get_position (GdkEvent *event,
double *x,
double *y)
{
GdkScrollEvent *self = (GdkScrollEvent *) event;
*x = self->x;
*y = self->y;
return TRUE;
}
static GdkDeviceTool *
gdk_scroll_event_get_tool (GdkEvent *event)
{
@@ -2202,10 +2180,10 @@ gdk_scroll_event_get_tool (GdkEvent *event)
static const GdkEventTypeInfo gdk_scroll_event_info = {
sizeof (GdkScrollEvent),
gdk_scroll_event_init,
NULL,
gdk_scroll_event_finalize,
gdk_scroll_event_get_state,
gdk_scroll_event_get_position,
NULL,
NULL,
gdk_scroll_event_get_tool,
NULL,
-2
View File
@@ -225,8 +225,6 @@ struct _GdkScrollEvent
{
GdkEvent parent_instance;
double x;
double y;
GdkModifierType state;
GdkScrollDirection direction;
double delta_x;
-2
View File
@@ -30,8 +30,6 @@
#include <errno.h>
#include <math.h>
#include "fallback-c89.c"
/**
* SECTION:rgba_colors
* @Short_description: RGBA colors
-3
View File
@@ -47,9 +47,6 @@
#include <epoxy/gl.h>
/* for the use of round() */
#include "fallback-c89.c"
#ifdef GDK_WINDOWING_WAYLAND
#include "wayland/gdkwayland.h"
#endif
-1
View File
@@ -49,7 +49,6 @@
#include <cairo-win32.h>
#include <dwmapi.h>
#include <math.h>
#include "fallback-c89.c"
static void gdk_surface_win32_finalize (GObject *object);
-144
View File
@@ -1,144 +0,0 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 2011 Chun-wei Fan <fanc999@yahoo.com.tw>
*
* Author: Chun-wei Fan <fanc999@yahoo.com.tw>
*
* 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/>.
*/
#include "config.h"
#include <math.h>
/* Workaround for round() for non-GCC/non-C99 compilers */
#ifndef HAVE_ROUND
static inline double
round (double x)
{
if (x >= 0)
return floor (x + 0.5);
else
return ceil (x - 0.5);
}
#endif
/* Workaround for rint() for non-GCC/non-C99 compilers */
#ifndef HAVE_RINT
static inline double
rint (double x)
{
if (ceil (x + 0.5) == floor (x + 0.5))
{
int a;
a = (int) ceil (x);
if (a % 2 == 0)
return ceil (x);
else
return floor (x);
}
else
{
if (x >= 0)
return floor (x + 0.5);
else
return ceil (x - 0.5);
}
}
#endif
#ifndef HAVE_NEARBYINT
/* Workaround for nearbyint() for non-GCC/non-C99 compilers */
/* This is quite similar to rint() in most respects */
static inline double
nearbyint (double x)
{
return floor (x + 0.5);
}
#endif
#ifndef HAVE_DECL_ISINF
/* Unfortunately MSVC does not have finite()
* but it does have _finite() which is the same
* as finite() except when x is a NaN
*/
static inline gboolean
isinf (double x)
{
return (!_finite (x) && !_isnan (x));
}
#endif
#ifndef HAVE_DECL_ISNAN
/* it seems of the supported compilers only
* MSVC does not have isnan(), but it does
* have _isnan() which does the same as isnan()
*/
static inline gboolean
isnan (double x)
{
return _isnan (x);
}
#endif
#ifndef HAVE_DECL_ISNANF
#if 1
#define isnanf(x) isnan(x)
#else
/* it seems of the supported compilers only
* MSVC does not have isnanf(), but it does
* have _isnanf() which does the same as isnanf()
*/
#ifdef _MSC_VER
static inline gboolean
isnanf (float x)
{
return _isnanf (x);
}
#elif defined (__GNUC__)
/* gcc has an intern function that it warns about when
* using -Wshadow but no header properly declares it,
* so we do it instead.
*/
extern int isnanf (float x);
#endif
#endif
#endif
#ifndef INFINITY
/* define INFINITY for compilers that lack support for it */
# ifdef HUGE_VALF
# define INFINITY HUGE_VALF
# else
# define INFINITY (float)HUGE_VAL
# endif
#endif
#ifndef HAVE_LOG2
/* Use a simple implementation for log2() for compilers that lack it */
static inline double
log2 (double x)
{
return log (x) / log (2.0);
}
#endif
#ifndef HAVE_EXP2
/* Use a simple implementation for exp2() for compilers that lack it */
static inline double
exp2 (double x)
{
return pow (2.0, x);
}
#endif
+1 -1
View File
@@ -85,7 +85,6 @@
#include <gtk/gtkcomboboxtext.h>
#include <gtk/gtkconstraintlayout.h>
#include <gtk/gtkconstraint.h>
#include <gtk/gtkcoverflow.h>
#include <gtk/gtkcssprovider.h>
#include <gtk/gtkcustomlayout.h>
#include <gtk/gtkcustomsorter.h>
@@ -175,6 +174,7 @@
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkmountoperation.h>
#include <gtk/gtkmultifilter.h>
#include <gtk/gtkmultiselection.h>
#include <gtk/gtkmultisorter.h>
#include <gtk/gtknative.h>
#include <gtk/gtknativedialog.h>
+691 -36
View File
@@ -34,6 +34,13 @@
#include "gtkprivate.h"
#include "gtkscrollable.h"
#include "gtkwidgetprivate.h"
#include "gtksizerequest.h"
#include "gtkadjustment.h"
#include "gtkgesturedrag.h"
#include "gtkeventcontrollermotion.h"
#include "gtkdragsource.h"
#include "gtkeventcontrollerkey.h"
#include "gtklistbaseprivate.h"
/**
* SECTION:gtkcolumnview
@@ -63,6 +70,21 @@ struct _GtkColumnView
GtkColumnListItemFactory *factory;
GtkSorter *sorter;
GtkAdjustment *hadjustment;
gboolean in_column_resize;
gboolean in_column_reorder;
int drag_pos;
int drag_x;
int drag_offset;
int drag_column_x;
GtkGesture *drag_gesture;
guint autoscroll_id;
double autoscroll_x;
double autoscroll_delta;
};
struct _GtkColumnViewClass
@@ -82,6 +104,8 @@ enum
PROP_VADJUSTMENT,
PROP_VSCROLL_POLICY,
PROP_SINGLE_CLICK_ACTIVATE,
PROP_ENABLE_RUBBERBAND,
PROP_SEARCH_FILTER,
N_PROPS
};
@@ -169,48 +193,77 @@ gtk_column_view_allocate_columns (GtkColumnView *self,
int width)
{
GtkScrollablePolicy scroll_policy;
int col_min, col_nat, widget_min, widget_nat, extra, col_size, x;
int col_min, col_nat, extra, col_size, x;
int n, n_expand, expand_size, n_extra;
guint i;
GtkRequestedSize *sizes;
gtk_column_view_measure_across (self, &col_min, &col_nat);
gtk_widget_measure (GTK_WIDGET (self),
GTK_ORIENTATION_HORIZONTAL, -1,
&widget_min, &widget_nat,
NULL, NULL);
scroll_policy = gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview));
if (scroll_policy == GTK_SCROLL_MINIMUM)
{
extra = widget_min - col_min;
col_size = col_min;
}
else
{
extra = widget_nat - col_nat;
col_size = col_nat;
}
width -= extra;
width = MAX (width, col_size);
x = 0;
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
n = g_list_model_get_n_items (G_LIST_MODEL (self->columns));
n_expand = 0;
sizes = g_newa (GtkRequestedSize, n);
for (i = 0; i < n; i++)
{
GtkColumnViewColumn *column;
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
gtk_column_view_column_measure (column, &col_min, &col_nat);
if (scroll_policy == GTK_SCROLL_MINIMUM)
col_size = col_min;
if (gtk_column_view_column_get_visible (column))
{
gtk_column_view_column_measure (column, &sizes[i].minimum_size, &sizes[i].natural_size);
if (gtk_column_view_column_get_expand (column))
n_expand++;
}
else
col_size = col_nat;
sizes[i].minimum_size = sizes[i].natural_size = 0;
g_object_unref (column);
}
gtk_column_view_column_allocate (column, x, col_size);
x += col_size;
gtk_column_view_measure_across (self, &col_min, &col_nat);
scroll_policy = gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview));
if (scroll_policy == GTK_SCROLL_MINIMUM)
extra = MAX (width - col_min, 0);
else
extra = MAX (width - col_min, col_nat - col_min);
extra = gtk_distribute_natural_allocation (extra, n, sizes);
if (n_expand > 0)
{
expand_size = extra / n_expand;
n_extra = extra % n_expand;
}
else
expand_size = n_extra = 0;
x = 0;
for (i = 0; i < n; i++)
{
GtkColumnViewColumn *column;
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
if (gtk_column_view_column_get_visible (column))
{
col_size = sizes[i].minimum_size;
if (gtk_column_view_column_get_expand (column))
{
col_size += expand_size;
if (n_extra > 0)
{
col_size++;
n_extra--;
}
}
gtk_column_view_column_allocate (column, x, col_size);
if (self->in_column_reorder && i == self->drag_pos)
gtk_column_view_column_set_header_position (column, self->drag_x);
x += col_size;
}
g_object_unref (column);
}
return width + extra;
return x;
}
static void
@@ -220,8 +273,9 @@ gtk_column_view_allocate (GtkWidget *widget,
int baseline)
{
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
int full_width, header_height, min, nat;
int full_width, header_height, min, nat, x;
x = gtk_adjustment_get_value (self->hadjustment);
full_width = gtk_column_view_allocate_columns (self, width);
gtk_widget_measure (self->header, GTK_ORIENTATION_VERTICAL, full_width, &min, &nat, NULL, NULL);
@@ -229,11 +283,14 @@ gtk_column_view_allocate (GtkWidget *widget,
header_height = min;
else
header_height = nat;
gtk_widget_allocate (self->header, full_width, header_height, -1, NULL);
gtk_widget_allocate (self->header, full_width, header_height, -1,
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (-x, 0)));
gtk_widget_allocate (GTK_WIDGET (self->listview),
full_width, height - header_height, -1,
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (0, header_height)));
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (-x, header_height)));
gtk_adjustment_configure (self->hadjustment, x, 0, full_width, width * 0.1, width * 0.9, width);
}
static void
@@ -244,6 +301,23 @@ gtk_column_view_activate_cb (GtkListView *listview,
g_signal_emit (self, signals[ACTIVATE], 0, pos);
}
static void
adjustment_value_changed_cb (GtkAdjustment *adjustment,
GtkColumnView *self)
{
gtk_widget_queue_allocate (GTK_WIDGET (self));
}
static void
clear_adjustment (GtkColumnView *self)
{
if (self->hadjustment == NULL)
return;
g_signal_handlers_disconnect_by_func (self->hadjustment, adjustment_value_changed_cb, self);
g_clear_object (&self->hadjustment);
}
static void
gtk_column_view_dispose (GObject *object)
{
@@ -262,6 +336,7 @@ gtk_column_view_dispose (GObject *object)
g_clear_object (&self->factory);
g_clear_object (&self->sorter);
clear_adjustment (self);
G_OBJECT_CLASS (gtk_column_view_parent_class)->dispose (object);
}
@@ -291,7 +366,7 @@ gtk_column_view_get_property (GObject *object,
break;
case PROP_HADJUSTMENT:
g_value_set_object (value, gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)));
g_value_set_object (value, self->hadjustment);
break;
case PROP_HSCROLL_POLICY:
@@ -322,6 +397,14 @@ gtk_column_view_get_property (GObject *object,
g_value_set_boolean (value, gtk_column_view_get_single_click_activate (self));
break;
case PROP_ENABLE_RUBBERBAND:
g_value_set_boolean (value, gtk_column_view_get_enable_rubberband (self));
break;
case PROP_SEARCH_FILTER:
g_value_set_object (value, gtk_column_view_get_search_filter (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -335,13 +418,24 @@ gtk_column_view_set_property (GObject *object,
GParamSpec *pspec)
{
GtkColumnView *self = GTK_COLUMN_VIEW (object);
GtkAdjustment *adjustment;
switch (property_id)
{
case PROP_HADJUSTMENT:
if (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)) != g_value_get_object (value))
adjustment = g_value_get_object (value);
if (adjustment == NULL)
adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
g_object_ref_sink (adjustment);
if (self->hadjustment != adjustment)
{
gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (self->listview), g_value_get_object (value));
clear_adjustment (self);
self->hadjustment = adjustment;
g_signal_connect (adjustment, "value-changed", G_CALLBACK (adjustment_value_changed_cb), self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HADJUSTMENT]);
}
break;
@@ -382,6 +476,14 @@ gtk_column_view_set_property (GObject *object,
gtk_column_view_set_single_click_activate (self, g_value_get_boolean (value));
break;
case PROP_ENABLE_RUBBERBAND:
gtk_column_view_set_enable_rubberband (self, g_value_get_boolean (value));
break;
case PROP_SEARCH_FILTER:
gtk_column_view_set_search_filter (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -478,6 +580,30 @@ gtk_column_view_class_init (GtkColumnViewClass *klass)
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkColumnView:enable-rubberband:
*
* Allow rubberband selection
*/
properties[PROP_ENABLE_RUBBERBAND] =
g_param_spec_boolean ("enable-rubberband",
P_("Enable rubberband selection"),
P_("Allow selecting items by dragging with the mouse"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkColumnView:search-filter:
*
* Filter used for search
*/
properties[PROP_SEARCH_FILTER] =
g_param_spec_object ("search-filter",
P_("Search filter"),
P_("Filter used for searching"),
GTK_TYPE_FILTER,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
/**
@@ -507,9 +633,366 @@ gtk_column_view_class_init (GtkColumnViewClass *klass)
gtk_widget_class_set_css_name (widget_class, I_("treeview"));
}
static void update_column_resize (GtkColumnView *self,
double x);
static void update_column_reorder (GtkColumnView *self,
double x);
static gboolean
autoscroll_cb (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer data)
{
GtkColumnView *self = data;
gtk_adjustment_set_value (self->hadjustment,
gtk_adjustment_get_value (self->hadjustment) + self->autoscroll_delta);
self->autoscroll_x += self->autoscroll_delta;
if (self->in_column_resize)
update_column_resize (self, self->autoscroll_x);
else if (self->in_column_reorder)
update_column_reorder (self, self->autoscroll_x);
return G_SOURCE_CONTINUE;
}
static void
add_autoscroll (GtkColumnView *self,
double x,
double delta)
{
self->autoscroll_x = x;
self->autoscroll_delta = delta;
if (self->autoscroll_id == 0)
self->autoscroll_id = gtk_widget_add_tick_callback (GTK_WIDGET (self), autoscroll_cb, self, NULL);
}
static void
remove_autoscroll (GtkColumnView *self)
{
if (self->autoscroll_id != 0)
{
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->autoscroll_id);
self->autoscroll_id = 0;
}
}
#define DRAG_WIDTH 6
static gboolean
gtk_column_view_in_resize_rect (GtkColumnView *self,
GtkColumnViewColumn *column,
double x,
double y)
{
GtkWidget *header;
graphene_rect_t rect;
header = gtk_column_view_column_get_header (column);
if (!gtk_widget_compute_bounds (header, self->header, &rect))
return FALSE;
rect.origin.x += rect.size.width - DRAG_WIDTH / 2;
rect.size.width = DRAG_WIDTH;
return graphene_rect_contains_point (&rect, &(graphene_point_t) { x, y});
}
static gboolean
gtk_column_view_in_header (GtkColumnView *self,
GtkColumnViewColumn *column,
double x,
double y)
{
GtkWidget *header;
graphene_rect_t rect;
header = gtk_column_view_column_get_header (column);
if (!gtk_widget_compute_bounds (header, self->header, &rect))
return FALSE;
return graphene_rect_contains_point (&rect, &(graphene_point_t) { x, y});
}
static void
header_drag_begin (GtkGestureDrag *gesture,
double start_x,
double start_y,
GtkColumnView *self)
{
int i, n;
self->drag_pos = -1;
n = g_list_model_get_n_items (G_LIST_MODEL (self->columns));
for (i = 0; !self->in_column_resize && i < n; i++)
{
GtkColumnViewColumn *column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
if (!gtk_column_view_column_get_visible (column))
{
g_object_unref (column);
continue;
}
if (i + 1 < n &&
gtk_column_view_column_get_resizable (column) &&
gtk_column_view_in_resize_rect (self, column, start_x, start_y))
{
int size;
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
if (!gtk_widget_has_focus (GTK_WIDGET (self)))
gtk_widget_grab_focus (GTK_WIDGET (self));
gtk_column_view_column_get_allocation (column, NULL, &size);
gtk_column_view_column_set_fixed_width (column, size);
self->drag_pos = i;
self->drag_x = start_x - size;
self->in_column_resize = TRUE;
g_object_unref (column);
break;
}
if (gtk_column_view_column_get_reorderable (column) &&
gtk_column_view_in_header (self, column, start_x, start_y))
{
int pos;
gtk_column_view_column_get_allocation (column, &pos, NULL);
self->drag_pos = i;
self->drag_offset = start_x - pos;
g_object_unref (column);
break;
}
g_object_unref (column);
}
}
static void
header_drag_end (GtkGestureDrag *gesture,
double offset_x,
double offset_y,
GtkColumnView *self)
{
double start_x, x;
gtk_gesture_drag_get_start_point (gesture, &start_x, NULL);
x = start_x + offset_x;
remove_autoscroll (self);
if (self->in_column_resize)
{
self->in_column_resize = FALSE;
}
else if (self->in_column_reorder)
{
GdkEventSequence *sequence;
GtkColumnViewColumn *column;
GtkWidget *header;
int i;
self->in_column_reorder = FALSE;
if (self->drag_pos == -1)
return;
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
header = gtk_column_view_column_get_header (column);
gtk_style_context_remove_class (gtk_widget_get_style_context (header), "dnd");
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
return;
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
{
GtkColumnViewColumn *col = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
if (gtk_column_view_column_get_visible (col))
{
int pos, size;
gtk_column_view_column_get_allocation (col, &pos, &size);
if (pos <= x && x <= pos + size)
{
gtk_column_view_insert_column (self, i, column);
g_object_unref (col);
break;
}
}
g_object_unref (col);
}
g_object_unref (column);
}
}
static void
update_column_resize (GtkColumnView *self,
double x)
{
GtkColumnViewColumn *column;
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
gtk_column_view_column_set_fixed_width (column, MAX (x - self->drag_x, 0));
g_object_unref (column);
}
static void
update_column_reorder (GtkColumnView *self,
double x)
{
GtkColumnViewColumn *column;
int width;
int size;
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
width = gtk_widget_get_allocated_width (GTK_WIDGET (self->header));
gtk_column_view_column_get_allocation (column, NULL, &size);
self->drag_x = CLAMP (x - self->drag_offset, 0, width - size);
gtk_widget_queue_allocate (GTK_WIDGET (self));
gtk_column_view_column_queue_resize (column);
g_object_unref (column);
}
#define SCROLL_EDGE_SIZE 15
static void
header_drag_update (GtkGestureDrag *gesture,
double offset_x,
double offset_y,
GtkColumnView *self)
{
GdkEventSequence *sequence;
double start_x, x;
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
return;
if (self->drag_pos == -1)
return;
if (!self->in_column_resize && !self->in_column_reorder)
{
if (gtk_drag_check_threshold (GTK_WIDGET (self), 0, 0, offset_x, 0))
{
GtkColumnViewColumn *column;
GtkWidget *header;
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
header = gtk_column_view_column_get_header (column);
gtk_widget_insert_after (header, self->header, gtk_widget_get_last_child (self->header));
gtk_style_context_add_class (gtk_widget_get_style_context (header), "dnd");
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
if (!gtk_widget_has_focus (GTK_WIDGET (self)))
gtk_widget_grab_focus (GTK_WIDGET (self));
self->in_column_reorder = TRUE;
g_object_unref (column);
}
}
gtk_gesture_drag_get_start_point (gesture, &start_x, NULL);
x = start_x + offset_x;
if (self->in_column_resize)
update_column_resize (self, x);
else if (self->in_column_reorder)
update_column_reorder (self, x);
if (self->in_column_resize || self->in_column_reorder)
{
double value, page_size, upper;
value = gtk_adjustment_get_value (self->hadjustment);
page_size = gtk_adjustment_get_page_size (self->hadjustment);
upper = gtk_adjustment_get_upper (self->hadjustment);
if (x - value < SCROLL_EDGE_SIZE && value > 0)
add_autoscroll (self, x, - (SCROLL_EDGE_SIZE - (x - value))/3.0);
else if (value + page_size - x < SCROLL_EDGE_SIZE && value + page_size < upper)
add_autoscroll (self, x, (SCROLL_EDGE_SIZE - (value + page_size - x))/3.0);
else
remove_autoscroll (self);
}
}
static void
header_motion (GtkEventControllerMotion *controller,
double x,
double y,
GtkColumnView *self)
{
gboolean cursor_set = FALSE;
int i, n;
n = g_list_model_get_n_items (G_LIST_MODEL (self->columns));
for (i = 0; i < n; i++)
{
GtkColumnViewColumn *column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
if (!gtk_column_view_column_get_visible (column))
{
g_object_unref (column);
continue;
}
if (i + 1 < n &&
gtk_column_view_column_get_resizable (column) &&
gtk_column_view_in_resize_rect (self, column, x, y))
{
gtk_widget_set_cursor_from_name (self->header, "col-resize");
cursor_set = TRUE;
}
g_object_unref (column);
}
if (!cursor_set)
gtk_widget_set_cursor (self->header, NULL);
}
static gboolean
header_key_pressed (GtkEventControllerKey *controller,
guint keyval,
guint keycode,
GdkModifierType modifiers,
GtkColumnView *self)
{
if (self->in_column_reorder)
{
if (keyval == GDK_KEY_Escape)
gtk_gesture_set_state (self->drag_gesture, GTK_EVENT_SEQUENCE_DENIED);
return TRUE;
}
return FALSE;
}
static void
gtk_column_view_init (GtkColumnView *self)
{
GtkEventController *controller;
self->columns = g_list_store_new (GTK_TYPE_COLUMN_VIEW_COLUMN);
self->header = gtk_list_item_widget_new (NULL, "header");
@@ -517,6 +1000,22 @@ gtk_column_view_init (GtkColumnView *self)
gtk_widget_set_layout_manager (self->header, gtk_column_view_layout_new (self));
gtk_widget_set_parent (self->header, GTK_WIDGET (self));
controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
g_signal_connect (controller, "drag-begin", G_CALLBACK (header_drag_begin), self);
g_signal_connect (controller, "drag-update", G_CALLBACK (header_drag_update), self);
g_signal_connect (controller, "drag-end", G_CALLBACK (header_drag_end), self);
gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
gtk_widget_add_controller (self->header, controller);
self->drag_gesture = GTK_GESTURE (controller);
controller = gtk_event_controller_motion_new ();
g_signal_connect (controller, "motion", G_CALLBACK (header_motion), self);
gtk_widget_add_controller (self->header, controller);
controller = gtk_event_controller_key_new ();
g_signal_connect (controller, "key-pressed", G_CALLBACK (header_key_pressed), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
self->sorter = gtk_column_view_sorter_new ();
self->factory = gtk_column_list_item_factory_new (self);
self->listview = GTK_LIST_VIEW (gtk_list_view_new_with_factory (
@@ -530,6 +1029,7 @@ gtk_column_view_init (GtkColumnView *self)
g_quark_from_static_string (I_("view")));
gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
}
/**
@@ -691,6 +1191,7 @@ gtk_column_view_remove_column (GtkColumnView *self,
g_object_unref (item);
if (item == column)
break;
}
gtk_column_view_sorter_remove_column (GTK_COLUMN_VIEW_SORTER (self->sorter), column);
@@ -698,6 +1199,55 @@ gtk_column_view_remove_column (GtkColumnView *self,
g_list_store_remove (self->columns, i);
}
/**
* gtk_column_view_insert_column:
* @self: a #GtkColumnView
* @position: the position to insert @column at
* @column: the #GtkColumnViewColumn to insert
*
* Inserts a column at the given position in the columns of @self.
*
* If @column is already a column of @self, it will be repositioned.
*/
void
gtk_column_view_insert_column (GtkColumnView *self,
guint position,
GtkColumnViewColumn *column)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column));
g_return_if_fail (gtk_column_view_column_get_column_view (column) == NULL ||
gtk_column_view_column_get_column_view (column) == self);
g_return_if_fail (position <= g_list_model_get_n_items (G_LIST_MODEL (self->columns)));
g_object_ref (column);
if (gtk_column_view_column_get_column_view (column) == self)
{
guint i;
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
{
GtkColumnViewColumn *item = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
g_object_unref (item);
if (item == column)
{
g_list_store_remove (self->columns, i);
break;
}
}
}
else
gtk_column_view_column_set_column_view (column, self);
g_list_store_insert (self->columns, position, column);
gtk_column_view_column_queue_resize (column);
g_object_unref (column);
}
void
gtk_column_view_measure_across (GtkColumnView *self,
int *minimum,
@@ -732,6 +1282,12 @@ gtk_column_view_get_header_widget (GtkColumnView *self)
return GTK_LIST_ITEM_WIDGET (self->header);
}
GtkListView *
gtk_column_view_get_list_view (GtkColumnView *self)
{
return GTK_LIST_VIEW (self->listview);
}
/**
* gtk_column_view_get_sorter:
* @self: a #GtkColumnView
@@ -829,3 +1385,102 @@ gtk_column_view_get_single_click_activate (GtkColumnView *self)
return gtk_list_view_get_single_click_activate (self->listview);
}
/**
* gtk_column_view_set_enable_rubberband:
* @self: a #GtkColumnView
* @enable_rubberband: %TRUE to enable rubberband selection
*
* Sets whether selections can be changed by dragging with the mouse.
*/
void
gtk_column_view_set_enable_rubberband (GtkColumnView *self,
gboolean enable_rubberband)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
if (enable_rubberband == gtk_list_view_get_enable_rubberband (self->listview))
return;
gtk_list_view_set_enable_rubberband (self->listview, enable_rubberband);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLE_RUBBERBAND]);
}
/**
* gtk_column_view_get_enable_rubberband:
* @self: a #GtkColumnView
*
* Returns whether rows can be selected by dragging with the mouse.
*
* Returns: %TRUE if rubberband selection is enabled
*/
gboolean
gtk_column_view_get_enable_rubberband (GtkColumnView *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), FALSE);
return gtk_list_view_get_enable_rubberband (self->listview);
}
/**
* gtk_column_view_set_search_filter:
* @self: a #GtkColumnView
* @filter: (nullable): the filter to use for search, or %NULL
*
* Sets a search filter.
*
* The selection will be moved to first item matching the
* filter whenever the filter changes.
*
* This can be used with single selection and a string
* filter that is connected to a search entry.
*/
void
gtk_column_view_set_search_filter (GtkColumnView *self,
GtkFilter *filter)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
if (filter == gtk_list_view_get_search_filter (GTK_LIST_VIEW (self->listview)))
return;
gtk_list_view_set_search_filter (GTK_LIST_VIEW (self->listview), filter);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]);
}
/**
* gtk_column_view_get_search_filter:
* @self: a #GtkColumnView
*
* Gets the search filter that was set with
* gtk_column_view_set_search_filter().
*
* Returns: (transfer none): The search filter of @self
*/
GtkFilter *
gtk_column_view_get_search_filter (GtkColumnView *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
return gtk_list_view_get_search_filter (GTK_LIST_VIEW (self->listview));
}
/**
* gtk_column_view_select_next_match:
* @self: a #GtkColumnView
* @forward: whether to move forward or back
*
* Moves the selection to the next item matching the
* search filter set with gtk_column_view_set_search_filter().
*/
void
gtk_column_view_select_next_match (GtkColumnView *self,
gboolean forward)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
gtk_list_view_select_next_match (GTK_LIST_VIEW (self->listview), forward);
}
+22
View File
@@ -27,6 +27,7 @@
#include <gtk/gtktypes.h>
#include <gtk/gtksortlistmodel.h>
#include <gtk/gtksorter.h>
#include <gtk/gtkfilter.h>
G_BEGIN_DECLS
@@ -61,6 +62,10 @@ void gtk_column_view_append_column (GtkColumnView
GDK_AVAILABLE_IN_ALL
void gtk_column_view_remove_column (GtkColumnView *self,
GtkColumnViewColumn *column);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_insert_column (GtkColumnView *self,
guint position,
GtkColumnViewColumn *column);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_column_view_get_model (GtkColumnView *self);
@@ -77,6 +82,7 @@ void gtk_column_view_set_show_separators (GtkColumnView
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_column_view_get_sorter (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_sort_by_column (GtkColumnView *self,
GtkColumnViewColumn *column,
GtkSortType direction);
@@ -87,6 +93,22 @@ void gtk_column_view_set_single_click_activate (GtkColumnView
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_get_single_click_activate (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_set_enable_rubberband (GtkColumnView *self,
gboolean enable_rubberband);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_get_enable_rubberband (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_set_search_filter (GtkColumnView *self,
GtkFilter *filter);
GDK_AVAILABLE_IN_ALL
GtkFilter * gtk_column_view_get_search_filter (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_select_next_match (GtkColumnView *self,
gboolean forward);
G_END_DECLS
#endif /* __GTK_COLUMN_VIEW_H__ */
+9
View File
@@ -53,10 +53,18 @@ gtk_column_view_cell_measure (GtkWidget *widget,
int *minimum_baseline,
int *natural_baseline)
{
GtkColumnViewCell *cell = GTK_COLUMN_VIEW_CELL (widget);
GtkWidget *child = gtk_widget_get_first_child (widget);
if (child)
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
int fixed_width = gtk_column_view_column_get_fixed_width (cell->column);
if (fixed_width > -1)
*minimum = *natural = fixed_width;
}
}
static void
@@ -156,6 +164,7 @@ gtk_column_view_cell_new (GtkColumnViewColumn *column)
cell = g_object_new (GTK_TYPE_COLUMN_VIEW_CELL,
"factory", gtk_column_view_column_get_factory (column),
"visible", gtk_column_view_column_get_visible (column),
NULL);
cell->column = g_object_ref (column);
+381 -1
View File
@@ -60,6 +60,14 @@ struct _GtkColumnViewColumn
int natural_size_request;
int allocation_offset;
int allocation_size;
int header_position;
int fixed_width;
guint visible : 1;
guint resizable : 1;
guint expand : 1;
guint reorderable : 1;
/* This list isn't sorted - this is just caching for performance */
GtkColumnViewCell *first_cell; /* no reference, just caching */
@@ -77,6 +85,11 @@ enum
PROP_FACTORY,
PROP_TITLE,
PROP_SORTER,
PROP_VISIBLE,
PROP_RESIZABLE,
PROP_EXPAND,
PROP_REORDERABLE,
PROP_FIXED_WIDTH,
N_PROPS
};
@@ -126,6 +139,26 @@ gtk_column_view_column_get_property (GObject *object,
g_value_set_object (value, self->sorter);
break;
case PROP_VISIBLE:
g_value_set_boolean (value, self->visible);
break;
case PROP_RESIZABLE:
g_value_set_boolean (value, self->resizable);
break;
case PROP_EXPAND:
g_value_set_boolean (value, self->expand);
break;
case PROP_REORDERABLE:
g_value_set_boolean (value, self->reorderable);
break;
case PROP_FIXED_WIDTH:
g_value_set_int (value, self->fixed_width);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -154,6 +187,26 @@ gtk_column_view_column_set_property (GObject *object,
gtk_column_view_column_set_sorter (self, g_value_get_object (value));
break;
case PROP_VISIBLE:
gtk_column_view_column_set_visible (self, g_value_get_boolean (value));
break;
case PROP_RESIZABLE:
gtk_column_view_column_set_resizable (self, g_value_get_boolean (value));
break;
case PROP_EXPAND:
gtk_column_view_column_set_expand (self, g_value_get_boolean (value));
break;
case PROP_REORDERABLE:
gtk_column_view_column_set_reorderable (self, g_value_get_boolean (value));
break;
case PROP_FIXED_WIDTH:
gtk_column_view_column_set_fixed_width (self, g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -217,6 +270,67 @@ gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass)
GTK_TYPE_SORTER,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnViewColumn:visible:
*
* Whether this column is visible
*/
properties[PROP_VISIBLE] =
g_param_spec_boolean ("visible",
P_("Visible"),
P_("Whether this column is visible"),
TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnViewColumn:resizable:
*
* Whether this column is resizable
*/
properties[PROP_RESIZABLE] =
g_param_spec_boolean ("resizable",
P_("Resizable"),
P_("Whether this column is resizable"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnViewColumn:expand:
*
* Column gets share of extra width allocated to the view
*/
properties[PROP_EXPAND] =
g_param_spec_boolean ("expand",
P_("Expand"),
P_("column gets share of extra width"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnViewColumn:reorderable:
*
* Whether this column is reorderable
*/
properties[PROP_REORDERABLE] =
g_param_spec_boolean ("reorderable",
P_("Reorderable"),
P_("Whether this column is reorderable"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnViewColumn:fixed-width:
*
* If not -1, this is the width that the column is allocated,
* regardless of the size of its content.
*/
properties[PROP_FIXED_WIDTH] =
g_param_spec_int ("fixed-width",
P_("Fixed width"),
P_("Fixed width of this column"),
-1, G_MAXINT, -1,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
@@ -225,6 +339,11 @@ gtk_column_view_column_init (GtkColumnViewColumn *self)
{
self->minimum_size_request = -1;
self->natural_size_request = -1;
self->visible = TRUE;
self->resizable = FALSE;
self->expand = FALSE;
self->reorderable = FALSE;
self->fixed_width = -1;
}
/**
@@ -333,6 +452,12 @@ gtk_column_view_column_measure (GtkColumnViewColumn *self,
int *minimum,
int *natural)
{
if (self->fixed_width > -1)
{
self->minimum_size_request = self->fixed_width;
self->natural_size_request = self->fixed_width;
}
if (self->minimum_size_request < 0)
{
GtkColumnViewCell *cell;
@@ -375,6 +500,7 @@ gtk_column_view_column_allocate (GtkColumnViewColumn *self,
{
self->allocation_offset = offset;
self->allocation_size = size;
self->header_position = offset;
}
void
@@ -567,7 +693,7 @@ gtk_column_view_column_set_title (GtkColumnViewColumn *self,
*
* Returns the title set with gtk_column_view_column_set_title().
*
* Returns: (nullable) The column's title
* Returns: (nullable): The column's title
*/
const char *
gtk_column_view_column_get_title (GtkColumnViewColumn *self)
@@ -647,3 +773,257 @@ gtk_column_view_column_notify_sort (GtkColumnViewColumn *self)
if (self->header)
gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header));
}
/**
* gtk_column_view_column_set_visible:
* @self: a #GtkColumnViewColumn
* @visible: whether this column should be visible
*
* Sets whether this column should be visible in views.
*/
void
gtk_column_view_column_set_visible (GtkColumnViewColumn *self,
gboolean visible)
{
GtkColumnViewCell *cell;
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
if (self->visible == visible)
return;
self->visible = visible;
self->minimum_size_request = -1;
self->natural_size_request = -1;
if (self->header)
gtk_widget_set_visible (GTK_WIDGET (self->header), visible);
for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell))
{
gtk_widget_set_visible (GTK_WIDGET (cell), visible);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VISIBLE]);
}
/**
* gtk_column_view_get_visible:
* @self: a #GtkColumnViewColumn
*
* Returns whether this column is visible.
*
* Returns: %TRUE if this column is visible
*/
gboolean
gtk_column_view_column_get_visible (GtkColumnViewColumn *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), TRUE);
return self->visible;
}
/**
* gtk_column_view_column_set_resizable:
* @self: a #GtkColumnViewColumn
* @resizable: whether this column should be resizable
*
* Sets whether this column should be resizable by dragging.
*/
void
gtk_column_view_column_set_resizable (GtkColumnViewColumn *self,
gboolean resizable)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
if (self->resizable == resizable)
return;
self->resizable = resizable;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RESIZABLE]);
}
/**
* gtk_column_view_get_resizable:
* @self: a #GtkColumnView
*
* Returns whether this column is resizable.
*
* Returns: %TRUE if this column is resizable
*/
gboolean
gtk_column_view_column_get_resizable (GtkColumnViewColumn *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), TRUE);
return self->resizable;
}
/**
* gtk_column_view_column_set_expand:
* @self: a #GtkColumnViewColumn
* @expand: %TRUE if this column should expand to fill available sace
*
* Sets the column to take available extra space.
*
* The extra space is shared equally amongst all columns that
* have the expand set to %TRUE.
*/
void
gtk_column_view_column_set_expand (GtkColumnViewColumn *self,
gboolean expand)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
if (self->expand == expand)
return;
self->expand = expand;
if (self->visible && self->view)
gtk_widget_queue_resize (GTK_WIDGET (self->view));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EXPAND]);
}
/**
* gtk_column_view_get_expand:
* @self: a #GtkColumnViewColumn
*
* Returns whether this column should expand.
*
* Returns: %TRUE if this column expands
*/
gboolean
gtk_column_view_column_get_expand (GtkColumnViewColumn *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), TRUE);
return self->expand;
}
/**
* gtk_column_view_column_set_reorderable:
* @self: a #GtkColumnViewColumn
* @reorderable: whether this column should be reorderable
*
* Sets whether this column should be reorderable by dragging.
*/
void
gtk_column_view_column_set_reorderable (GtkColumnViewColumn *self,
gboolean reorderable)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
if (self->reorderable == reorderable)
return;
self->reorderable = reorderable;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_REORDERABLE]);
}
/**
* gtk_column_view_get_reorderable:
* @self: a #GtkColumnView
*
* Returns whether this column is reorderable.
*
* Returns: %TRUE if this column is reorderable
*/
gboolean
gtk_column_view_column_get_reorderable (GtkColumnViewColumn *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), TRUE);
return self->reorderable;
}
/**
* gtk_column_view_column_set_fixed_width:
* @self: a #GtkColumnViewColumn
* @fixed_width: the new fixed width, or -1
*
* If @fixed_width is not -1, sets the fixed width of @column;
* otherwise unsets it.
*
* Setting a fixed width overrides the automatically calculated
* width. Interactive resizing also sets the fixed-width property.
*/
void
gtk_column_view_column_set_fixed_width (GtkColumnViewColumn *self,
int fixed_width)
{
GtkOverflow overflow;
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
g_return_if_fail (fixed_width >= -1);
if (self->fixed_width == fixed_width)
return;
self->fixed_width = fixed_width;
if (fixed_width > -1)
overflow = GTK_OVERFLOW_HIDDEN;
else
overflow = GTK_OVERFLOW_VISIBLE;
if (self->header &&
overflow != gtk_widget_get_overflow (GTK_WIDGET (self->header)))
{
GtkColumnViewCell *cell;
gtk_widget_set_overflow (GTK_WIDGET (self->header), overflow);
for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell))
gtk_widget_set_overflow (GTK_WIDGET (cell), overflow);
}
gtk_column_view_column_queue_resize (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FIXED_WIDTH]);
}
/**
* gtk_column_view_column_get_fixed_width:
* @self: a #GtkColumnViewColumn
*
* Gets the fixed width of the column.
*
* Returns: the fixed with of the column
*/
int
gtk_column_view_column_get_fixed_width (GtkColumnViewColumn *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), -1);
return self->fixed_width;
}
GtkWidget *
gtk_column_view_column_get_header (GtkColumnViewColumn *self)
{
return self->header;
}
void
gtk_column_view_column_set_header_position (GtkColumnViewColumn *self,
int offset)
{
self->header_position = offset;
}
void
gtk_column_view_column_get_header_allocation (GtkColumnViewColumn *self,
int *offset,
int *size)
{
if (offset)
*offset = self->header_position;
if (size)
*size = self->allocation_size;
}
+30
View File
@@ -72,6 +72,36 @@ void gtk_column_view_column_set_sorter (GtkColu
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_column_view_column_get_sorter (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_visible (GtkColumnViewColumn *self,
gboolean visible);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_column_get_visible (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_fixed_width (GtkColumnViewColumn *self,
int fixed_width);
GDK_AVAILABLE_IN_ALL
int gtk_column_view_column_get_fixed_width (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_resizable (GtkColumnViewColumn *self,
gboolean resizable);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_column_get_resizable (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_expand (GtkColumnViewColumn *self,
gboolean expand);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_column_get_expand (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_reorderable (GtkColumnViewColumn *self,
gboolean reorderable);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_column_get_reorderable (GtkColumnViewColumn *self);
G_END_DECLS
#endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */
+7
View File
@@ -33,6 +33,7 @@ void gtk_column_view_column_add_cell (GtkColu
void gtk_column_view_column_remove_cell (GtkColumnViewColumn *self,
GtkColumnViewCell *cell);
GtkColumnViewCell * gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self);
GtkWidget * gtk_column_view_column_get_header (GtkColumnViewColumn *self);
void gtk_column_view_column_queue_resize (GtkColumnViewColumn *self);
void gtk_column_view_column_measure (GtkColumnViewColumn *self,
@@ -47,4 +48,10 @@ void gtk_column_view_column_get_allocation (GtkColu
void gtk_column_view_column_notify_sort (GtkColumnViewColumn *self);
void gtk_column_view_column_set_header_position (GtkColumnViewColumn *self,
int offset);
void gtk_column_view_column_get_header_allocation (GtkColumnViewColumn *self,
int *offset,
int *size);
#endif /* __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__ */
+8 -3
View File
@@ -118,11 +118,16 @@ gtk_column_view_layout_allocate (GtkLayoutManager *layout_manager,
int col_x, col_width;
if (GTK_IS_COLUMN_VIEW_CELL (child))
column = gtk_column_view_cell_get_column (GTK_COLUMN_VIEW_CELL (child));
{
column = gtk_column_view_cell_get_column (GTK_COLUMN_VIEW_CELL (child));
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
}
else
column = gtk_column_view_title_get_column (GTK_COLUMN_VIEW_TITLE (child));
{
column = gtk_column_view_title_get_column (GTK_COLUMN_VIEW_TITLE (child));
gtk_column_view_column_get_header_allocation (column, &col_x, &col_width);
}
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
gtk_widget_size_allocate (child, &(GtkAllocation) { col_x, 0, col_width, height }, baseline);
}
}
+2
View File
@@ -21,11 +21,13 @@
#define __GTK_COLUMN_VIEW_PRIVATE_H__
#include "gtk/gtkcolumnview.h"
#include "gtk/gtklistview.h"
#include "gtk/gtkcolumnviewsorterprivate.h"
#include "gtk/gtklistitemwidgetprivate.h"
GtkListItemWidget * gtk_column_view_get_header_widget (GtkColumnView *self);
GtkListView * gtk_column_view_get_list_view (GtkColumnView *self);
void gtk_column_view_measure_across (GtkColumnView *self,
int *minimum,
+14 -6
View File
@@ -58,10 +58,18 @@ gtk_column_view_title_measure (GtkWidget *widget,
int *minimum_baseline,
int *natural_baseline)
{
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
GtkWidget *child = gtk_widget_get_first_child (widget);
if (child)
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
int fixed_width = gtk_column_view_column_get_fixed_width (self->column);
if (fixed_width > -1)
*minimum = *natural = fixed_width;
}
}
static void
@@ -112,11 +120,11 @@ gtk_column_view_title_resize_func (GtkWidget *widget)
}
static void
click_pressed_cb (GtkGestureClick *gesture,
guint n_press,
gdouble x,
gdouble y,
GtkWidget *widget)
click_released_cb (GtkGestureClick *gesture,
guint n_press,
gdouble x,
gdouble y,
GtkWidget *widget)
{
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
GtkSorter *sorter;
@@ -150,7 +158,7 @@ gtk_column_view_title_init (GtkColumnViewTitle *self)
gtk_box_append (GTK_BOX (self->box), self->sort);
gesture = gtk_gesture_click_new ();
g_signal_connect (gesture, "pressed", G_CALLBACK (click_pressed_cb), self);
g_signal_connect (gesture, "released", G_CALLBACK (click_released_cb), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
}
-704
View File
@@ -1,704 +0,0 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcoverflow.h"
#include "gtkintl.h"
#include "gtklistbaseprivate.h"
#include "gtkmain.h"
#include "gtkorientable.h"
#include "gtkprivate.h"
#include "gtkrbtreeprivate.h"
#include "gtkwidgetprivate.h"
/* Extra items to display at most above + below the central item */
#define GTK_COVER_FLOW_DISPLAY_ITEMS 5
/* Number of extra space we leave around the covers */
#define GTK_COVER_FLOW_SCALE_ALONG 3
#define GTK_COVER_FLOW_SCALE_ACROSS 2
/**
* SECTION:gtkcoverflow
* @title: GtkCoverFlow
* @short_description: A coverflow list widget
* @see_also: #GListModel
*
* GtkCoverFlow is a widget to present a coverflow list
*/
struct _GtkCoverFlow
{
GtkListBase parent_instance;
int size_across;
int size_along;
};
struct _GtkCoverFlowClass
{
GtkListBaseClass parent_class;
};
enum
{
PROP_0,
PROP_FACTORY,
PROP_MODEL,
N_PROPS
};
enum {
ACTIVATE,
LAST_SIGNAL
};
G_DEFINE_TYPE (GtkCoverFlow, gtk_cover_flow, GTK_TYPE_LIST_BASE)
static GParamSpec *properties[N_PROPS] = { NULL, };
static guint signals[LAST_SIGNAL] = { 0 };
static gboolean
gtk_cover_flow_get_allocation_along (GtkListBase *base,
guint pos,
int *offset,
int *size)
{
GtkCoverFlow *self = GTK_COVER_FLOW (base);
if (offset)
*offset = pos * self->size_along;
if (size)
*size = self->size_along;
return TRUE;
}
static gboolean
gtk_cover_flow_get_allocation_across (GtkListBase *base,
guint pos,
int *offset,
int *size)
{
GtkCoverFlow *self = GTK_COVER_FLOW (base);
if (offset)
*offset = pos * self->size_across;
if (size)
*size = self->size_across;
return TRUE;
}
static guint
gtk_cover_flow_move_focus_along (GtkListBase *base,
guint pos,
int steps)
{
if (steps < 0)
return pos - MIN (pos, -steps);
else
{
pos += MIN (gtk_list_base_get_n_items (base) - pos - 1, steps);
}
return pos;
}
static guint
gtk_cover_flow_move_focus_across (GtkListBase *base,
guint pos,
int steps)
{
return pos;
}
static gboolean
gtk_cover_flow_get_position_from_allocation (GtkListBase *base,
int across,
int along,
guint *pos,
cairo_rectangle_int_t *area)
{
GtkCoverFlow *self = GTK_COVER_FLOW (base);
if (across >= self->size_across ||
along >= self->size_along * gtk_list_base_get_n_items (base))
return FALSE;
*pos = along / self->size_along;
if (area)
{
area->x = 0;
area->width = self->size_across;
area->y = *pos * self->size_along;
area->height = self->size_along;
}
return TRUE;
}
static void
gtk_cover_flow_measure_children (GtkCoverFlow *self,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural)
{
GtkListItemManagerItem *item;
int min, nat, child_min, child_nat;
min = 0;
nat = 0;
for (item = gtk_list_item_manager_get_first (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
item != NULL;
item = gtk_rb_tree_node_get_next (item))
{
/* ignore unavailable items */
if (item->widget == NULL)
continue;
gtk_widget_measure (item->widget,
orientation, for_size,
&child_min, &child_nat, NULL, NULL);
min = MAX (min, child_min);
nat = MAX (nat, child_nat);
}
*minimum = min;
*natural = nat;
}
static void
gtk_cover_flow_measure_across (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
if (for_size > 0)
for_size /= GTK_COVER_FLOW_SCALE_ALONG;
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
*minimum *= GTK_COVER_FLOW_SCALE_ACROSS;
*natural *= GTK_COVER_FLOW_SCALE_ACROSS;
}
static void
gtk_cover_flow_measure_along (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
if (for_size > 0)
for_size /= GTK_COVER_FLOW_SCALE_ACROSS;
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
*minimum *= GTK_COVER_FLOW_SCALE_ALONG;
*natural *= GTK_COVER_FLOW_SCALE_ALONG;
}
static void
gtk_cover_flow_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
if (orientation == gtk_list_base_get_orientation (GTK_LIST_BASE (self)))
gtk_cover_flow_measure_along (widget, orientation, for_size, minimum, natural);
else
gtk_cover_flow_measure_across (widget, orientation, for_size, minimum, natural);
}
static GskTransform *
transform_translate_oriented (GskTransform *transform,
GtkOrientation orientation,
GtkTextDirection dir,
float across,
float along)
{
if (orientation == GTK_ORIENTATION_VERTICAL)
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (across, along));
else if (dir == GTK_TEXT_DIR_LTR)
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (along, across));
else
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-along, across));
}
static void
gtk_cover_flow_size_allocate_child (GtkWidget *child,
GtkOrientation orientation,
GtkTextDirection dir,
GskTransform *transform,
int width,
int height)
{
if (orientation == GTK_ORIENTATION_VERTICAL)
{
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-width / 2, -height / 2));
gtk_widget_allocate (child, width, height, -1, transform);
}
else
{
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-height / 2, -width / 2));
gtk_widget_allocate (child, height, width, -1, transform);
}
}
static void
gtk_cover_flow_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
GtkOrientation orientation, opposite_orientation;
GtkTextDirection dir;
GtkListItemManagerItem *item;
GtkListItemManager *manager;
guint i, pos;
int x, y, along, across;
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
/* step 0: exit early if list is empty */
if (gtk_list_item_manager_get_root (manager) == NULL)
return;
/* step 1: determine size of children */
along = orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
across = opposite_orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
self->size_along = along / GTK_COVER_FLOW_SCALE_ALONG;
self->size_across = across / GTK_COVER_FLOW_SCALE_ACROSS;
/* step 2: update the adjustments */
gtk_list_base_update_adjustments (GTK_LIST_BASE (self),
self->size_across,
self->size_along * gtk_list_base_get_n_items (GTK_LIST_BASE (self)),
self->size_across,
self->size_along,
&x, &y);
pos = gtk_list_base_get_anchor (GTK_LIST_BASE (self));
/* step 4: actually allocate the widgets */
dir = _gtk_widget_get_direction (widget);
i = 0;
for (item = gtk_list_item_manager_get_first (manager);
item != NULL;
item = gtk_rb_tree_node_get_next (item))
{
if (item->widget)
{
/* start at the center */
GskTransform *transform = transform_translate_oriented (NULL, orientation, dir, across / 2, along / 2);
if (i == pos)
{
/* nothing to do, we're already centered */
}
else if (MAX (pos, i) - MIN (pos, i) < GTK_COVER_FLOW_DISPLAY_ITEMS) /* ABS() doesn't work with guint */
{
int diff = i - pos;
transform = gsk_transform_perspective (transform, MAX (width, height) * 2);
transform = transform_translate_oriented (transform,
orientation, dir,
0,
diff * self->size_along / 4);
transform = transform_translate_oriented (transform,
orientation, dir,
0,
(diff < 0 ? -1 : 1) * self->size_along / 2);
if (orientation == GTK_ORIENTATION_VERTICAL)
transform = gsk_transform_rotate_3d (transform,
diff > 0 ? 60 : -60,
graphene_vec3_x_axis ());
else
transform = gsk_transform_rotate_3d (transform,
diff < 0 ? 60 : -60,
graphene_vec3_y_axis ());
transform = transform_translate_oriented (transform,
orientation, dir,
0,
(diff < 0 ? 1 : -1) * self->size_along / 4);
}
else
{
transform = transform_translate_oriented (transform,
orientation, dir,
- 2 * self->size_across,
- 2 * self->size_along);
}
gtk_cover_flow_size_allocate_child (item->widget,
orientation, dir,
transform,
self->size_across,
self->size_along);
}
i += item->n_items;
}
}
static void
gtk_cover_flow_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkListBase *base = GTK_LIST_BASE (object);
switch (property_id)
{
case PROP_FACTORY:
g_value_set_object (value, gtk_list_item_manager_get_factory (gtk_list_base_get_manager (base)));
break;
case PROP_MODEL:
g_value_set_object (value, gtk_list_base_get_model (base));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_cover_flow_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkWidget *child;
GtkListItemManagerItem *item;
item = gtk_list_item_manager_get_nth (gtk_list_base_get_manager (GTK_LIST_BASE (widget)),
gtk_list_base_get_anchor (GTK_LIST_BASE (widget)),
NULL);
if (item == NULL || item->widget == NULL)
{
GTK_WIDGET_CLASS (gtk_cover_flow_parent_class)->snapshot (widget, snapshot);
return;
}
for (child = _gtk_widget_get_first_child (widget);
child != item->widget;
child = _gtk_widget_get_next_sibling (child))
gtk_widget_snapshot_child (widget, child, snapshot);
for (child = _gtk_widget_get_last_child (widget);
child != item->widget;
child = _gtk_widget_get_prev_sibling (child))
gtk_widget_snapshot_child (widget, child, snapshot);
gtk_widget_snapshot_child (widget, item->widget, snapshot);
}
static void
gtk_cover_flow_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkCoverFlow *self = GTK_COVER_FLOW (object);
switch (property_id)
{
case PROP_FACTORY:
gtk_cover_flow_set_factory (self, g_value_get_object (value));
break;
case PROP_MODEL:
gtk_cover_flow_set_model (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_cover_flow_activate_item (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
guint pos;
if (!g_variant_check_format_string (parameter, "u", FALSE))
return;
g_variant_get (parameter, "u", &pos);
if (pos >= gtk_list_base_get_n_items (GTK_LIST_BASE (self)))
return;
g_signal_emit (widget, signals[ACTIVATE], 0, pos);
}
static void
gtk_cover_flow_class_init (GtkCoverFlowClass *klass)
{
GtkListBaseClass *list_base_class = GTK_LIST_BASE_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
list_base_class->list_item_name = "cover";
list_base_class->list_item_size = sizeof (GtkListItemManagerItem);
list_base_class->list_item_augment_size = sizeof (GtkListItemManagerItemAugment);
list_base_class->list_item_augment_func = gtk_list_item_manager_augment_node;
list_base_class->get_allocation_along = gtk_cover_flow_get_allocation_along;
list_base_class->get_allocation_across = gtk_cover_flow_get_allocation_across;
list_base_class->get_position_from_allocation = gtk_cover_flow_get_position_from_allocation;
list_base_class->move_focus_along = gtk_cover_flow_move_focus_along;
list_base_class->move_focus_across = gtk_cover_flow_move_focus_across;
widget_class->measure = gtk_cover_flow_measure;
widget_class->size_allocate = gtk_cover_flow_size_allocate;
widget_class->snapshot = gtk_cover_flow_snapshot;
gobject_class->get_property = gtk_cover_flow_get_property;
gobject_class->set_property = gtk_cover_flow_set_property;
/**
* GtkCoverFlow:factory:
*
* Factory for populating list items
*/
properties[PROP_FACTORY] =
g_param_spec_object ("factory",
P_("Factory"),
P_("Factory for populating list items"),
GTK_TYPE_LIST_ITEM_FACTORY,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkCoverFlow:model:
*
* Model for the items displayed
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("Model for the items displayed"),
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
/**
* GtkCoverFlow::activate:
* @self: The #GtkCoverFlow
* @position: position of item to activate
*
* The ::activate signal is emitted when an item has been activated by the user,
* usually via activating the GtkCoverFlow|list.activate-item action.
*
* This allows for a convenient way to handle activation in a listview.
* See gtk_list_item_set_activatable() for details on how to use this signal.
*/
signals[ACTIVATE] =
g_signal_new (I_("activate"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
g_signal_set_va_marshaller (signals[ACTIVATE],
G_TYPE_FROM_CLASS (gobject_class),
g_cclosure_marshal_VOID__UINTv);
/**
* GtkCoverFlow|list.activate-item:
* @position: position of item to activate
*
* Activates the item given in @position by emitting the GtkCoverFlow::activate
* signal.
*/
gtk_widget_class_install_action (widget_class,
"list.activate-item",
"u",
gtk_cover_flow_activate_item);
gtk_widget_class_set_css_name (widget_class, I_("coverflow"));
}
static void
gtk_cover_flow_init (GtkCoverFlow *self)
{
gtk_list_base_set_anchor_max_widgets (GTK_LIST_BASE (self),
0,
GTK_COVER_FLOW_DISPLAY_ITEMS);
/* FIXME: this should overwrite the property default, but gobject properties... */
gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_HORIZONTAL);
}
/**
* gtk_cover_flow_new:
*
* Creates a new empty #GtkCoverFlow.
*
* You most likely want to call gtk_cover_flow_set_factory() to
* set up a way to map its items to widgets and gtk_cover_flow_set_model()
* to set a model to provide items next.
*
* Returns: a new #GtkCoverFlow
**/
GtkWidget *
gtk_cover_flow_new (void)
{
return g_object_new (GTK_TYPE_COVER_FLOW, NULL);
}
/**
* gtk_cover_flow_new_with_factory:
* @factory: (transfer full): The factory to populate items with
*
* Creates a new #GtkCoverFlow that uses the given @factory for
* mapping items to widgets.
*
* You most likely want to call gtk_cover_flow_set_model() to set
* a model next.
*
* The function takes ownership of the
* argument, so you can write code like
* ```
* cover_flow = gtk_cover_flow_new_with_factory (
* gtk_builder_list_item_factory_newfrom_resource ("/resource.ui"));
* ```
*
* Returns: a new #GtkCoverFlow using the given @factory
**/
GtkWidget *
gtk_cover_flow_new_with_factory (GtkListItemFactory *factory)
{
GtkWidget *result;
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
result = g_object_new (GTK_TYPE_COVER_FLOW,
"factory", factory,
NULL);
g_object_unref (factory);
return result;
}
/**
* gtk_cover_flow_get_model:
* @self: a #GtkCoverFlow
*
* Gets the model that's currently used to read the items displayed.
*
* Returns: (nullable) (transfer none): The model in use
**/
GListModel *
gtk_cover_flow_get_model (GtkCoverFlow *self)
{
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
return gtk_list_base_get_model (GTK_LIST_BASE (self));
}
/**
* gtk_cover_flow_set_model:
* @self: a #GtkCoverFlow
* @model: (allow-none) (transfer none): the model to use or %NULL for none
*
* Sets the #GListModel to use.
*
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
* Otherwise, @self creates a #GtkSingleSelection for the selection.
**/
void
gtk_cover_flow_set_model (GtkCoverFlow *self,
GListModel *model)
{
g_return_if_fail (GTK_IS_COVER_FLOW (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (!gtk_list_base_set_model (GTK_LIST_BASE (self), model))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_cover_flow_get_factory:
* @self: a #GtkCoverFlow
*
* Gets the factory that's currently used to populate list items.
*
* Returns: (nullable) (transfer none): The factory in use
**/
GtkListItemFactory *
gtk_cover_flow_get_factory (GtkCoverFlow *self)
{
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
return gtk_list_item_manager_get_factory (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
}
/**
* gtk_cover_flow_set_factory:
* @self: a #GtkCoverFlow
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
*
* Sets the #GtkListItemFactory to use for populating list items.
**/
void
gtk_cover_flow_set_factory (GtkCoverFlow *self,
GtkListItemFactory *factory)
{
GtkListItemManager *manager;
g_return_if_fail (GTK_IS_COVER_FLOW (self));
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
if (factory == gtk_list_item_manager_get_factory (manager))
return;
gtk_list_item_manager_set_factory (manager, factory);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
}
-68
View File
@@ -1,68 +0,0 @@
/*
* 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__ */
-5
View File
@@ -40,11 +40,6 @@
#include "gtkcssvalueprivate.h"
#include "gtktypebuiltins.h"
/* this is in case round() is not provided by the compiler,
* such as in the case of C89 compilers, like MSVC
*/
#include "fallback-c89.c"
/*** PARSING ***/
static gboolean
-5
View File
@@ -30,11 +30,6 @@
#include "gtkprivatetypebuiltins.h"
#include "gtkprivate.h"
/* this is in case round() is not provided by the compiler,
* such as in the case of C89 compilers, like MSVC
*/
#include "fallback-c89.c"
enum {
PROP_0,
PROP_ANIMATED,
-5
View File
@@ -29,11 +29,6 @@
#include "gtkintl.h"
#include "gtkprivatetypebuiltins.h"
/* this is in case round() is not provided by the compiler,
* such as in the case of C89 compilers, like MSVC
*/
#include "fallback-c89.c"
/* the actual parsers we have */
#include "gtkcssarrayvalueprivate.h"
#include "gtkcssbgsizevalueprivate.h"
-2
View File
@@ -75,8 +75,6 @@
#include <cairo-gobject.h>
#include <string.h>
#include "fallback-c89.c"
/**
* SECTION:gtkentry
* @Short_description: A single line text entry field
+2 -2
View File
@@ -980,10 +980,10 @@ gtk_closure_expression_new (GType value_type,
/**
* gtk_cclosure_expression_new:
* @value_type: the type of the value that this expression evaluates to
* @marshal: marshaller used for creating a closure
* @marshal: (scope call): marshaller used for creating a closure
* @n_params: the number of params needed for evaluating @closure
* @params: (array length=n_params) (transfer full): expressions for each parameter
* @callback_func: callback used for creating a closure
* @callback_func: (scope notified) (closure user_data) (destroy user_destroy): callback used for creating a closure
* @user_data: user data used for creating a closure
* @user_destroy: destroy notify for @user_data
*
+11
View File
@@ -101,6 +101,17 @@ gtk_functions_list_item_factory_init (GtkFunctionsListItemFactory *self)
{
}
/**
* gtk_functions_list_item_factory_new: (skip)
* @setup_func: (scope call) (destroy user_destroy): the setup function for the list item factory
* @bind_func: (scope call) (destroy user_destroy): the bind function for the list item factory
* @user_data: user data for the @setup_func and @bind_func functions
* @user_destroy: a function called to destroy @user_data
*
* Creates a new #GtkListItemFactory with the given functions.
*
* Returns: (transfer full): the newly created list item factory object
*/
GtkListItemFactory *
gtk_functions_list_item_factory_new (GtkListItemSetupFunc setup_func,
GtkListItemBindFunc bind_func,
+141
View File
@@ -91,6 +91,8 @@ enum
PROP_MIN_COLUMNS,
PROP_MODEL,
PROP_SINGLE_CLICK_ACTIVATE,
PROP_ENABLE_RUBBERBAND,
PROP_SEARCH_FILTER,
N_PROPS
};
@@ -914,6 +916,14 @@ gtk_grid_view_get_property (GObject *object,
g_value_set_boolean (value, gtk_list_item_manager_get_single_click_activate (self->item_manager));
break;
case PROP_ENABLE_RUBBERBAND:
g_value_set_boolean (value, gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)));
break;
case PROP_SEARCH_FILTER:
g_value_set_object (value, gtk_grid_view_get_search_filter (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -950,6 +960,14 @@ gtk_grid_view_set_property (GObject *object,
gtk_grid_view_set_single_click_activate (self, g_value_get_boolean (value));
break;
case PROP_ENABLE_RUBBERBAND:
gtk_grid_view_set_enable_rubberband (self, g_value_get_boolean (value));
break;
case PROP_SEARCH_FILTER:
gtk_grid_view_set_search_filter (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -1062,6 +1080,30 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkGridView:enable-rubberband:
*
* Allow rubberband selection
*/
properties[PROP_ENABLE_RUBBERBAND] =
g_param_spec_boolean ("enable-rubberband",
P_("Enable rubberband selection"),
P_("Allow selecting items by dragging with the mouse"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkGridView:search-filter:
*
* Filter used for search
*/
properties[PROP_SEARCH_FILTER] =
g_param_spec_object ("search-filter",
P_("Search filter"),
P_("Filter used for searching"),
GTK_TYPE_FILTER,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
/**
@@ -1370,3 +1412,102 @@ gtk_grid_view_get_single_click_activate (GtkGridView *self)
return gtk_list_item_manager_get_single_click_activate (self->item_manager);
}
/**
* gtk_grid_view_set_enable_rubberband:
* @self: a #GtkGridView
* @enable_rubberband: %TRUE to enable rubberband selection
*
* Sets whether selections can be changed by dragging with the mouse.
*/
void
gtk_grid_view_set_enable_rubberband (GtkGridView *self,
gboolean enable_rubberband)
{
g_return_if_fail (GTK_IS_GRID_VIEW (self));
if (enable_rubberband == gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)))
return;
gtk_list_base_set_enable_rubberband (GTK_LIST_BASE (self), enable_rubberband);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLE_RUBBERBAND]);
}
/**
* gtk_grid_view_get_enable_rubberband:
* @self: a #GtkGridView
*
* Returns whether rows can be selected by dragging with the mouse.
*
* Returns: %TRUE if rubberband selection is enabled
*/
gboolean
gtk_grid_view_get_enable_rubberband (GtkGridView *self)
{
g_return_val_if_fail (GTK_IS_GRID_VIEW (self), FALSE);
return gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self));
}
/**
* gtk_grid_view_set_search_filter:
* @self: a #GtkGridView
* @filter: (nullable): the filter to use for search, or %NULL
*
* Sets a search filter.
*
* The selection will be moved to first item matching the
* filter whenever the filter changes.
*
* This can be used with single selection and a string
* filter that is connected to a search entry.
*/
void
gtk_grid_view_set_search_filter (GtkGridView *self,
GtkFilter *filter)
{
g_return_if_fail (GTK_IS_GRID_VIEW (self));
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
if (filter == gtk_list_base_get_search_filter (GTK_LIST_BASE (self)))
return;
gtk_list_base_set_search_filter (GTK_LIST_BASE (self), filter);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]);
}
/**
* gtk_grid_view_get_search_filter:
* @self: a #GtkGridView
*
* Gets the search filter that was set with
* gtk_grid_view_set_search_filter().
*
* Returns: (transfer none): The search filter of @self
*/
GtkFilter *
gtk_grid_view_get_search_filter (GtkGridView *self)
{
g_return_val_if_fail (GTK_IS_GRID_VIEW (self), NULL);
return gtk_list_base_get_search_filter (GTK_LIST_BASE (self));
}
/**
* gtk_grid_view_select_next_match:
* @self: a #GtkGridView
* @forward: whether to move forward or back
*
* Moves the selection to the next item matching the
* search filter set with gtk_grid_view_set_search_filter().
*/
void
gtk_grid_view_select_next_match (GtkGridView *self,
gboolean forward)
{
g_return_if_fail (GTK_IS_GRID_VIEW (self));
gtk_list_base_select_next_match (GTK_LIST_BASE (self), forward);
}
+16
View File
@@ -25,6 +25,7 @@
#endif
#include <gtk/gtklistbase.h>
#include <gtk/gtkfilter.h>
G_BEGIN_DECLS
@@ -73,6 +74,11 @@ guint gtk_grid_view_get_max_columns (GtkGridView
GDK_AVAILABLE_IN_ALL
void gtk_grid_view_set_max_columns (GtkGridView *self,
guint max_columns);
GDK_AVAILABLE_IN_ALL
void gtk_grid_view_set_enable_rubberband (GtkGridView *self,
gboolean enable_rubberband);
GDK_AVAILABLE_IN_ALL
gboolean gtk_grid_view_get_enable_rubberband (GtkGridView *self);
GDK_AVAILABLE_IN_ALL
void gtk_grid_view_set_single_click_activate (GtkGridView *self,
@@ -80,6 +86,16 @@ void gtk_grid_view_set_single_click_activate (GtkGridView
GDK_AVAILABLE_IN_ALL
gboolean gtk_grid_view_get_single_click_activate (GtkGridView *self);
GDK_AVAILABLE_IN_ALL
void gtk_grid_view_set_search_filter (GtkGridView *self,
GtkFilter *filter);
GDK_AVAILABLE_IN_ALL
GtkFilter * gtk_grid_view_get_search_filter (GtkGridView *self);
GDK_AVAILABLE_IN_ALL
void gtk_grid_view_select_next_match (GtkGridView *self,
gboolean forward);
G_END_DECLS
-5
View File
@@ -55,11 +55,6 @@
#include "gdk/gdktextureprivate.h"
#include "gdk/gdkprofilerprivate.h"
/* this is in case round() is not provided by the compiler,
* such as in the case of C89 compilers, like MSVC
*/
#include "fallback-c89.c"
/**
* SECTION:gtkicontheme
* @Short_description: Looking up icons by name
+1 -2
View File
@@ -18,10 +18,9 @@
#include "config.h"
#include "gtkkineticscrollingprivate.h"
#include <math.h>
#include <stdio.h>
#include "fallback-c89.c"
/*
* All our curves are second degree linear differential equations, and
* so they can always be written as linear combinations of 2 base
+1 -6
View File
@@ -63,11 +63,6 @@
#include <math.h>
#include <string.h>
/* this is in case rint() is not provided by the compiler,
* such as in the case of C89 compilers, like MSVC
*/
#include "fallback-c89.c"
/**
* SECTION:gtklabel
* @Short_description: A widget that displays a small to medium amount of text
@@ -2238,7 +2233,7 @@ extract_mnemonic_keyval (const char *text,
p = g_utf8_next_char (p);
c = g_utf8_get_char (p);
if (c != '_' && c != '0')
if (c != '_' && c != '\0')
{
const gsize byte_index = p - text - 1; /* Of the _ */
-2
View File
@@ -140,8 +140,6 @@
#include "a11y/gtklevelbaraccessible.h"
#include "fallback-c89.c"
enum {
PROP_VALUE = 1,
PROP_MIN_VALUE,
+605 -20
View File
@@ -28,6 +28,12 @@
#include "gtkscrollable.h"
#include "gtksingleselection.h"
#include "gtktypebuiltins.h"
#include "gtkgesturedrag.h"
#include "gtkwidgetprivate.h"
#include "gtkcssnodeprivate.h"
#include "gtkstylecontextprivate.h"
#include "gtksnapshot.h"
#include "gtkmultiselection.h"
typedef struct _GtkListBasePrivate GtkListBasePrivate;
@@ -50,6 +56,24 @@ struct _GtkListBasePrivate
GtkListItemTracker *selected;
/* the item that has input focus */
GtkListItemTracker *focus;
gboolean enable_rubberband;
gboolean doing_rubberband;
double rb_x1;
double rb_y1;
double rb_x2;
double rb_y2;
GtkGesture *drag_gesture;
GtkCssNode *rubberband_node;
GtkSelectionModel *old_selection;
gboolean modify;
gboolean extend;
guint autoscroll_id;
double autoscroll_delta_x;
double autoscroll_delta_y;
GtkFilter *search_filter;
};
enum
@@ -523,6 +547,8 @@ gtk_list_base_focus (GtkWidget *widget,
}
}
static void gtk_list_base_clear_search_filter (GtkListBase *self);
static void
gtk_list_base_dispose (GObject *object)
{
@@ -551,6 +577,8 @@ gtk_list_base_dispose (GObject *object)
g_clear_object (&priv->model);
gtk_list_base_clear_search_filter (self);
G_OBJECT_CLASS (gtk_list_base_parent_class)->dispose (object);
}
@@ -764,26 +792,18 @@ gtk_list_base_update_focus_tracker (GtkListBase *self)
}
}
static void
gtk_list_base_scroll_to_item (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
static gboolean
gtk_list_base_scroll_to_position (GtkListBase *self,
guint pos)
{
GtkListBase *self = GTK_LIST_BASE (widget);
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
int start, end;
double align_along, align_across;
GtkPackType side_along, side_across;
guint pos;
if (!g_variant_check_format_string (parameter, "u", FALSE))
return;
g_variant_get (parameter, "u", &pos);
/* figure out primary orientation and if position is valid */
if (!gtk_list_base_get_allocation_along (GTK_LIST_BASE (self), pos, &start, &end))
return;
return FALSE;
end += start;
gtk_list_base_compute_scroll_align (self,
@@ -794,7 +814,7 @@ gtk_list_base_scroll_to_item (GtkWidget *widget,
/* now do the same thing with the other orientation */
if (!gtk_list_base_get_allocation_across (GTK_LIST_BASE (self), pos, &start, &end))
return;
return FALSE;
end += start;
gtk_list_base_compute_scroll_align (self,
@@ -808,13 +828,32 @@ gtk_list_base_scroll_to_item (GtkWidget *widget,
align_across, side_across,
align_along, side_along);
/* HACK HACK HACK
*
* GTK has no way to track the focused child. But we now that when a listitem
* gets focus, it calls this action. So we update our focus tracker from here
* because it's the closest we can get to accurate tracking.
*/
gtk_list_base_update_focus_tracker (self);
return TRUE;
}
static void
gtk_list_base_scroll_to_item (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
{
GtkListBase *self = GTK_LIST_BASE (widget);
guint pos;
if (!g_variant_check_format_string (parameter, "u", FALSE))
return;
g_variant_get (parameter, "u", &pos);
if (gtk_list_base_scroll_to_position (self, pos))
{
/* HACK HACK HACK
*
* GTK has no way to track the focused child. But we know that when a listitem
* gets focus, it calls this action. So we update our focus tracker from here
* because it's the closest we can get to accurate tracking.
*/
gtk_list_base_update_focus_tracker (self);
}
}
static void
@@ -863,6 +902,17 @@ gtk_list_base_unselect_all (GtkWidget *widget,
gtk_selection_model_unselect_all (selection_model);
}
static void
gtk_list_base_next_match_action (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
{
gboolean forward;
g_variant_get (parameter, "(b)", &forward);
gtk_list_base_select_next_match (GTK_LIST_BASE (widget), forward);
}
static gboolean
gtk_list_base_move_cursor_to_start (GtkWidget *widget,
GVariant *args,
@@ -1061,6 +1111,20 @@ gtk_list_base_add_custom_move_binding (GtkWidgetClass *widget_class,
"(bbb)", TRUE, TRUE, TRUE);
}
static void gtk_list_base_snapshot_rubberband (GtkListBase *self,
GtkSnapshot *snapshot);
static void
gtk_list_base_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkListBase *self = GTK_LIST_BASE (widget);
GTK_WIDGET_CLASS (gtk_list_base_parent_class)->snapshot (widget, snapshot);
gtk_list_base_snapshot_rubberband (self, snapshot);
}
static void
gtk_list_base_class_init (GtkListBaseClass *klass)
{
@@ -1069,6 +1133,7 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
gpointer iface;
widget_class->focus = gtk_list_base_focus;
widget_class->snapshot = gtk_list_base_snapshot;
gobject_class->dispose = gtk_list_base_dispose;
gobject_class->get_property = gtk_list_base_get_property;
@@ -1162,6 +1227,11 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
NULL,
gtk_list_base_unselect_all);
gtk_widget_class_install_action (widget_class,
"list.next-match",
"(b)",
gtk_list_base_next_match_action);
gtk_list_base_add_move_binding (widget_class, GDK_KEY_Up, GTK_ORIENTATION_VERTICAL, -1);
gtk_list_base_add_move_binding (widget_class, GDK_KEY_KP_Up, GTK_ORIENTATION_VERTICAL, -1);
gtk_list_base_add_move_binding (widget_class, GDK_KEY_Down, GTK_ORIENTATION_VERTICAL, 1);
@@ -1184,6 +1254,341 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL);
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.unselect-all", NULL);
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL);
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_g, GDK_CONTROL_MASK, "list.next-match", "(b)", TRUE);
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_g, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.next-match", "(b)", FALSE);
}
static void gtk_list_base_update_rubberband_selection (GtkListBase *self);
static gboolean
autoscroll_cb (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer data)
{
GtkListBase *self = data;
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
double value;
value = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
gtk_adjustment_set_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL], value + priv->autoscroll_delta_x);
value = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
gtk_adjustment_set_value (priv->adjustment[GTK_ORIENTATION_VERTICAL], value + priv->autoscroll_delta_y);
if (priv->doing_rubberband)
{
priv->rb_x2 += priv->autoscroll_delta_x;
priv->rb_y2 += priv->autoscroll_delta_y;
gtk_list_base_update_rubberband_selection (self);
}
gtk_widget_queue_draw (GTK_WIDGET (self));
return G_SOURCE_CONTINUE;
}
static void
add_autoscroll (GtkListBase *self,
double delta_x,
double delta_y)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
priv->autoscroll_delta_x = delta_x;
priv->autoscroll_delta_y = delta_y;
if (priv->autoscroll_id == 0)
priv->autoscroll_id = gtk_widget_add_tick_callback (GTK_WIDGET (self), autoscroll_cb, self, NULL);
}
static void
remove_autoscroll (GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
if (priv->autoscroll_id != 0)
{
gtk_widget_remove_tick_callback (GTK_WIDGET (self), priv->autoscroll_id);
priv->autoscroll_id = 0;
}
}
static void
gtk_list_base_start_rubberband (GtkListBase *self,
double x,
double y,
gboolean modify,
gboolean extend)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
GtkCssNode *widget_node;
double value_x, value_y;
GtkSelectionModel *selection;
if (priv->doing_rubberband)
return;
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
priv->rb_x1 = priv->rb_x2 = x + value_x;
priv->rb_y1 = priv->rb_y2 = y + value_y;
priv->modify = modify;
priv->extend = extend;
widget_node = gtk_widget_get_css_node (GTK_WIDGET (self));
priv->rubberband_node = gtk_css_node_new ();
gtk_css_node_set_name (priv->rubberband_node,
g_quark_from_static_string ("rubberband"));
gtk_css_node_set_parent (priv->rubberband_node, widget_node);
gtk_css_node_set_state (priv->rubberband_node, gtk_css_node_get_state (widget_node));
g_object_unref (priv->rubberband_node);
selection = gtk_list_item_manager_get_model (priv->item_manager);
if (modify)
priv->old_selection = GTK_SELECTION_MODEL (gtk_multi_selection_copy (selection));
priv->doing_rubberband = TRUE;
}
static void
gtk_list_base_stop_rubberband (GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
if (!priv->doing_rubberband)
return;
priv->doing_rubberband = FALSE;
gtk_css_node_set_parent (priv->rubberband_node, NULL);
priv->rubberband_node = NULL;
g_clear_object (&priv->old_selection);
remove_autoscroll (self);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
#define SCROLL_EDGE_SIZE 15
static void
gtk_list_base_update_rubberband (GtkListBase *self,
double x,
double y)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
double value_x, value_y, page_size, upper;
double delta_x, delta_y;
if (!priv->doing_rubberband)
return;
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
priv->rb_x2 = x + value_x;
priv->rb_y2 = y + value_y;
gtk_list_base_update_rubberband_selection (self);
page_size = gtk_adjustment_get_page_size (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
upper = gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
if (x < SCROLL_EDGE_SIZE && value_x > 0)
delta_x = - (SCROLL_EDGE_SIZE - x)/3.0;
else if (page_size - x < SCROLL_EDGE_SIZE && value_x + page_size < upper)
delta_x = (SCROLL_EDGE_SIZE - (page_size - x))/3.0;
else
delta_x = 0;
page_size = gtk_adjustment_get_page_size (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
upper = gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
if (y < SCROLL_EDGE_SIZE && value_y > 0)
delta_y = - (SCROLL_EDGE_SIZE - y)/3.0;
else if (page_size - y < SCROLL_EDGE_SIZE && value_y + page_size < upper)
delta_y = (SCROLL_EDGE_SIZE - (page_size - y))/3.0;
else
delta_y = 0;
if (delta_x != 0 || delta_y != 0)
add_autoscroll (self, delta_x, delta_y);
else
remove_autoscroll (self);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
gtk_list_base_update_rubberband_selection (GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
GdkRectangle rect;
GdkRectangle alloc;
double value_x, value_y;
GtkSelectionModel *model;
GtkListItemManagerItem *item;
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
rect.x = MIN (priv->rb_x1, priv->rb_x2) - value_x;
rect.y = MIN (priv->rb_y1, priv->rb_y2) - value_y;
rect.width = ABS (priv->rb_x1 - priv->rb_x2) + 1;
rect.height = ABS (priv->rb_y1 - priv->rb_y2) + 1;
model = gtk_list_item_manager_get_model (priv->item_manager);
for (item = gtk_list_item_manager_get_first (priv->item_manager);
item != NULL;
item = gtk_rb_tree_node_get_next (item))
{
guint pos;
gboolean was_selected, selected;
if (!item->widget)
continue;
pos = gtk_list_item_manager_get_item_position (priv->item_manager, item);
gtk_widget_get_allocation (item->widget, &alloc);
selected = gdk_rectangle_intersect (&rect, &alloc, &alloc);
if (priv->modify)
{
was_selected = gtk_selection_model_is_selected (priv->old_selection, pos);
selected = selected ^ was_selected;
}
if (selected)
gtk_selection_model_select_item (model, pos, FALSE);
else if (!priv->extend)
gtk_selection_model_unselect_item (model, pos);
}
}
static void
gtk_list_base_snapshot_rubberband (GtkListBase *self,
GtkSnapshot *snapshot)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
GtkStyleContext *context;
GdkRectangle rect;
double value_x, value_y;
if (!priv->doing_rubberband)
return;
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
rect.x = MIN (priv->rb_x1, priv->rb_x2) - value_x;
rect.y = MIN (priv->rb_y1, priv->rb_y2) - value_y;
rect.width = ABS (priv->rb_x1 - priv->rb_x2) + 1;
rect.height = ABS (priv->rb_y1 - priv->rb_y2) + 1;
context = gtk_widget_get_style_context (GTK_WIDGET (self));
gtk_style_context_save_to_node (context, priv->rubberband_node);
gtk_snapshot_render_background (snapshot, context, rect.x, rect.y, rect.width, rect.height);
gtk_snapshot_render_frame (snapshot, context, rect.x, rect.y, rect.width, rect.height);
gtk_style_context_restore (context);
}
static void
get_selection_modifiers (GtkGesture *gesture,
gboolean *modify,
gboolean *extend)
{
GdkEventSequence *sequence;
GdkEvent *event;
GdkModifierType state;
*modify = FALSE;
*extend = FALSE;
sequence = gtk_gesture_get_last_updated_sequence (gesture);
event = gtk_gesture_get_last_event (gesture, sequence);
state = gdk_event_get_modifier_state (event);
if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
*modify = TRUE;
if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
*extend = TRUE;
}
static void
gtk_list_base_drag_begin (GtkGestureDrag *gesture,
double start_x,
double start_y,
GtkListBase *self)
{
gboolean modify;
gboolean extend;
get_selection_modifiers (GTK_GESTURE (gesture), &modify, &extend);
gtk_list_base_start_rubberband (self, start_x, start_y, modify, extend);
}
static void
gtk_list_base_drag_update (GtkGestureDrag *gesture,
double offset_x,
double offset_y,
GtkListBase *self)
{
double start_x, start_y;
gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
gtk_list_base_update_rubberband (self, start_x + offset_x, start_y + offset_y);
}
static void
gtk_list_base_drag_end (GtkGestureDrag *gesture,
double offset_x,
double offset_y,
GtkListBase *self)
{
gtk_list_base_drag_update (gesture, offset_x, offset_y, self);
gtk_list_base_stop_rubberband (self);
}
void
gtk_list_base_set_enable_rubberband (GtkListBase *self,
gboolean enable)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
if (priv->enable_rubberband == enable)
return;
priv->enable_rubberband = enable;
if (enable)
{
priv->drag_gesture = gtk_gesture_drag_new ();
g_signal_connect (priv->drag_gesture, "drag-begin", G_CALLBACK (gtk_list_base_drag_begin), self);
g_signal_connect (priv->drag_gesture, "drag-update", G_CALLBACK (gtk_list_base_drag_update), self);
g_signal_connect (priv->drag_gesture, "drag-end", G_CALLBACK (gtk_list_base_drag_end), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (priv->drag_gesture));
}
else
{
gtk_widget_remove_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (priv->drag_gesture));
priv->drag_gesture = NULL;
}
}
gboolean
gtk_list_base_get_enable_rubberband (GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
return priv->enable_rubberband;
}
static void
@@ -1538,3 +1943,183 @@ gtk_list_base_set_model (GtkListBase *self,
return TRUE;
}
static guint
find_first_selected (GtkSelectionModel *model)
{
guint i, start, n_items;
gboolean selected;
n_items = 0;
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model)); i += n_items)
{
gtk_selection_model_query_range (model, i, &start, &n_items, &selected);
if (selected)
return i;
}
return GTK_INVALID_LIST_POSITION;
}
static gboolean
match_item (GListModel *model,
GtkFilter *filter,
guint position)
{
gpointer item;
gboolean result;
item = g_list_model_get_item (model, position);
result = gtk_filter_match (filter, item);
g_object_unref (item);
return result;
}
static guint
find_next_match (GListModel *model,
GtkFilter *filter,
guint start,
gboolean forward)
{
guint i;
if (start == GTK_INVALID_LIST_POSITION)
start = 0;
if (forward)
for (i = start; i < g_list_model_get_n_items (model); i++)
{
if (match_item (model, filter, i))
return i;
}
else
for (i = start; ; i--)
{
if (match_item (model, filter, i))
return i;
if (i == 0)
break;
}
return GTK_INVALID_LIST_POSITION;
}
static void
gtk_list_base_search_filter_changed_cb (GtkFilter *filter,
GtkFilterChange change,
GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
GtkSelectionModel *model = gtk_list_item_manager_get_model (priv->item_manager);
if (model == NULL)
return;
if (gtk_filter_get_strictness (priv->search_filter) == GTK_FILTER_MATCH_NONE)
gtk_selection_model_unselect_all (model);
else
{
guint position;
switch (change)
{
case GTK_FILTER_CHANGE_DIFFERENT:
case GTK_FILTER_CHANGE_LESS_STRICT:
position = find_next_match (G_LIST_MODEL (model), priv->search_filter, 0, TRUE);
break;
case GTK_FILTER_CHANGE_MORE_STRICT:
position = find_first_selected (model);
if (position == GTK_INVALID_LIST_POSITION)
position = 0;
position = find_next_match (G_LIST_MODEL (model), priv->search_filter, position, TRUE);
break;
default:
g_assert_not_reached ();
}
if (position == GTK_INVALID_LIST_POSITION)
gtk_selection_model_unselect_all (model);
else
gtk_list_base_grab_focus_on_item (self, position, TRUE, FALSE, FALSE);
}
}
static void
gtk_list_base_clear_search_filter (GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
if (priv->search_filter == NULL)
return;
g_signal_handlers_disconnect_by_func (priv->search_filter,
gtk_list_base_search_filter_changed_cb,
self);
g_clear_object (&priv->search_filter);
}
void
gtk_list_base_set_search_filter (GtkListBase *self,
GtkFilter *filter)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
if (priv->search_filter == filter)
return;
gtk_list_base_clear_search_filter (self);
if (filter)
{
priv->search_filter = g_object_ref (filter);
g_signal_connect (priv->search_filter, "changed",
G_CALLBACK (gtk_list_base_search_filter_changed_cb), self);
gtk_list_base_search_filter_changed_cb (priv->search_filter, GTK_FILTER_CHANGE_DIFFERENT, self);
}
gtk_widget_action_set_enabled (GTK_WIDGET (self), "list.next-match", filter != NULL);
}
GtkFilter *
gtk_list_base_get_search_filter (GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
return priv->search_filter;
}
gboolean
gtk_list_base_select_next_match (GtkListBase *self,
gboolean forward)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
GtkSelectionModel *model = gtk_list_item_manager_get_model (priv->item_manager);
guint position;
if (!priv->search_filter)
return FALSE;
position = find_first_selected (model);
if (position == GTK_INVALID_LIST_POSITION)
return FALSE;
if (forward)
position = position + 1;
else if (position > 0)
position = position - 1;
else
return FALSE;
position = find_next_match (G_LIST_MODEL (model), priv->search_filter, position, forward);
if (position == GTK_INVALID_LIST_POSITION)
{
gtk_widget_error_bell (GTK_WIDGET (self));
return FALSE;
}
gtk_list_base_grab_focus_on_item (self, position, TRUE, FALSE, FALSE);
return TRUE;
}
+9
View File
@@ -23,6 +23,7 @@
#include "gtklistbase.h"
#include "gtklistitemmanagerprivate.h"
#include "gtkfilter.h"
#include "gtkprivate.h"
struct _GtkListBase
@@ -99,5 +100,13 @@ gboolean gtk_list_base_grab_focus_on_item (GtkListBase
gboolean select,
gboolean modify,
gboolean extend);
void gtk_list_base_set_enable_rubberband (GtkListBase *self,
gboolean enable);
gboolean gtk_list_base_get_enable_rubberband (GtkListBase *self);
void gtk_list_base_set_search_filter (GtkListBase *self,
GtkFilter *filter);
GtkFilter * gtk_list_base_get_search_filter (GtkListBase *self);
gboolean gtk_list_base_select_next_match (GtkListBase *self,
gboolean forward);
#endif /* __GTK_LIST_BASE_PRIVATE_H__ */
+9 -1
View File
@@ -29,6 +29,7 @@
#include "gtkintl.h"
#include "gtklistitemfactoryprivate.h"
#include "gtklistitemprivate.h"
#include "gtklistbaseprivate.h"
#include "gtkmain.h"
#include "gtkselectionmodel.h"
#include "gtkwidget.h"
@@ -309,6 +310,8 @@ gtk_list_item_widget_click_gesture_pressed (GtkGestureClick *gesture,
{
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
GtkWidget *widget = GTK_WIDGET (self);
GtkWidget * parent = gtk_widget_get_parent (widget);
gboolean rubberband;
if (priv->list_item && !priv->list_item->selectable && !priv->list_item->activatable)
{
@@ -316,7 +319,12 @@ gtk_list_item_widget_click_gesture_pressed (GtkGestureClick *gesture,
return;
}
if (!priv->list_item || priv->list_item->selectable)
if (GTK_IS_LIST_BASE (parent))
rubberband = gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (parent));
else
rubberband = FALSE;
if (!rubberband && (!priv->list_item || priv->list_item->selectable))
{
GdkModifierType state;
GdkEvent *event;
+142
View File
@@ -85,6 +85,8 @@ enum
PROP_MODEL,
PROP_SHOW_SEPARATORS,
PROP_SINGLE_CLICK_ACTIVATE,
PROP_ENABLE_RUBBERBAND,
PROP_SEARCH_FILTER,
N_PROPS
};
@@ -643,6 +645,14 @@ gtk_list_view_get_property (GObject *object,
g_value_set_boolean (value, gtk_list_item_manager_get_single_click_activate (self->item_manager));
break;
case PROP_ENABLE_RUBBERBAND:
g_value_set_boolean (value, gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)));
break;
case PROP_SEARCH_FILTER:
g_value_set_object (value, gtk_list_view_get_search_filter (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -675,6 +685,14 @@ gtk_list_view_set_property (GObject *object,
gtk_list_view_set_single_click_activate (self, g_value_get_boolean (value));
break;
case PROP_ENABLE_RUBBERBAND:
gtk_list_view_set_enable_rubberband (self, g_value_get_boolean (value));
break;
case PROP_SEARCH_FILTER:
gtk_list_view_set_search_filter (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -771,6 +789,30 @@ gtk_list_view_class_init (GtkListViewClass *klass)
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkListView:enable-rubberband:
*
* Allow rubberband selection
*/
properties[PROP_ENABLE_RUBBERBAND] =
g_param_spec_boolean ("enable-rubberband",
P_("Enable rubberband selection"),
P_("Allow selecting items by dragging with the mouse"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkListView:search-filter:
*
* Filter used for search
*/
properties[PROP_SEARCH_FILTER] =
g_param_spec_object ("search-filter",
P_("Search filter"),
P_("Filter used for searching"),
GTK_TYPE_FILTER,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
/**
@@ -1033,3 +1075,103 @@ gtk_list_view_get_single_click_activate (GtkListView *self)
return gtk_list_item_manager_get_single_click_activate (self->item_manager);
}
/**
* gtk_list_view_set_enable_rubberband:
* @self: a #GtkListView
* @enable_rubberband: %TRUE to enable rubberband selection
*
* Sets whether selections can be changed by dragging with the mouse.
*/
void
gtk_list_view_set_enable_rubberband (GtkListView *self,
gboolean enable_rubberband)
{
g_return_if_fail (GTK_IS_LIST_VIEW (self));
if (enable_rubberband == gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)))
return;
gtk_list_base_set_enable_rubberband (GTK_LIST_BASE (self), enable_rubberband);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLE_RUBBERBAND]);
}
/**
* gtk_list_view_get_enable_rubberband:
* @self: a #GtkListView
*
* Returns whether rows can be selected by dragging with the mouse.
*
* Returns: %TRUE if rubberband selection is enabled
*/
gboolean
gtk_list_view_get_enable_rubberband (GtkListView *self)
{
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), FALSE);
return gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self));
}
/**
* gtk_list_view_set_search_filter:
* @self: a #GtkListView
* @filter: (nullable): the filter ot use for search, or %NULL
*
* Sets a search filter.
*
* The selection will be moved to first item matching the
* filter whenever the filter changes.
*
* This can be used with single selection and a string
* filter that is connected to a search entry.
*/
void
gtk_list_view_set_search_filter (GtkListView *self,
GtkFilter *filter)
{
g_return_if_fail (GTK_IS_LIST_VIEW (self));
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
if (filter == gtk_list_base_get_search_filter (GTK_LIST_BASE (self)))
return;
gtk_list_base_set_search_filter (GTK_LIST_BASE (self), filter);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]);
}
/**
* gtk_list_view_get_search_filter:
* @self: a #GtkListView
*
* Gets the search filter that was set with
* gtk_list_view_set_search_filter().
*
* Returns: (transfer none): The search filter of @self
*/
GtkFilter *
gtk_list_view_get_search_filter (GtkListView *self)
{
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), NULL);
return gtk_list_base_get_search_filter (GTK_LIST_BASE (self));
}
/**
* gtk_list_view_select_next_match:
* @self: a #GtkListView
* @forward: whether to move forward or back
*
* Moves the selection to the next item matching the
* search filter set with gtk_list_view_set_search_filter().
*/
void
gtk_list_view_select_next_match (GtkListView *self,
gboolean forward)
{
g_return_if_fail (GTK_IS_LIST_VIEW (self));
gtk_list_base_select_next_match (GTK_LIST_BASE (self), forward);
}
+17
View File
@@ -25,6 +25,7 @@
#endif
#include <gtk/gtklistbase.h>
#include <gtk/gtkfilter.h>
G_BEGIN_DECLS
@@ -75,6 +76,22 @@ void gtk_list_view_set_single_click_activate (GtkListView
GDK_AVAILABLE_IN_ALL
gboolean gtk_list_view_get_single_click_activate (GtkListView *self);
GDK_AVAILABLE_IN_ALL
void gtk_list_view_set_enable_rubberband (GtkListView *self,
gboolean enable_rubberband);
GDK_AVAILABLE_IN_ALL
gboolean gtk_list_view_get_enable_rubberband (GtkListView *self);
GDK_AVAILABLE_IN_ALL
void gtk_list_view_set_search_filter (GtkListView *self,
GtkFilter *filter);
GDK_AVAILABLE_IN_ALL
GtkFilter * gtk_list_view_get_search_filter (GtkListView *self);
GDK_AVAILABLE_IN_ALL
void gtk_list_view_select_next_match (GtkListView *self,
gboolean forward);
G_END_DECLS
#endif /* __GTK_LIST_VIEW_H__ */
+380
View File
@@ -0,0 +1,380 @@
/*
* Copyright © 2019 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include "gtkmultiselection.h"
#include "gtkintl.h"
#include "gtkselectionmodel.h"
#include "gtksingleselection.h"
#include "gtkset.h"
/**
* SECTION:gtkmultiselection
* @Short_description: A selection model that allows selecting a multiple items
* @Title: GtkMultiSelection
* @see_also: #GtkSelectionModel
*
* GtkMultiSelection is an implementation of the #GtkSelectionModel interface
* that allows selecting multiple elements.
*/
struct _GtkMultiSelection
{
GObject parent_instance;
GListModel *model;
GtkSet *selected;
guint last_selected;
};
struct _GtkMultiSelectionClass
{
GObjectClass parent_class;
};
enum {
PROP_0,
PROP_MODEL,
N_PROPS,
};
static GParamSpec *properties[N_PROPS] = { NULL, };
static GType
gtk_multi_selection_get_item_type (GListModel *list)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (list);
return g_list_model_get_item_type (self->model);
}
static guint
gtk_multi_selection_get_n_items (GListModel *list)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (list);
return g_list_model_get_n_items (self->model);
}
static gpointer
gtk_multi_selection_get_item (GListModel *list,
guint position)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (list);
return g_list_model_get_item (self->model, position);
}
static void
gtk_multi_selection_list_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_multi_selection_get_item_type;
iface->get_n_items = gtk_multi_selection_get_n_items;
iface->get_item = gtk_multi_selection_get_item;
}
static gboolean
gtk_multi_selection_is_selected (GtkSelectionModel *model,
guint position)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
return gtk_set_contains (self->selected, position);
}
static gboolean
gtk_multi_selection_select_range (GtkSelectionModel *model,
guint position,
guint n_items,
gboolean exclusive)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
if (exclusive)
gtk_set_remove_all (self->selected);
gtk_set_add_range (self->selected, position, n_items);
gtk_selection_model_selection_changed (model, position, n_items);
return TRUE;
}
static gboolean
gtk_multi_selection_unselect_range (GtkSelectionModel *model,
guint position,
guint n_items)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
gtk_set_remove_range (self->selected, position, n_items);
gtk_selection_model_selection_changed (model, position, n_items);
return TRUE;
}
static gboolean
gtk_multi_selection_select_item (GtkSelectionModel *model,
guint position,
gboolean exclusive)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
guint pos, n_items;
pos = position;
n_items = 1;
self->last_selected = position;
return gtk_multi_selection_select_range (model, pos, n_items, exclusive);
}
static gboolean
gtk_multi_selection_unselect_item (GtkSelectionModel *model,
guint position)
{
return gtk_multi_selection_unselect_range (model, position, 1);
}
static gboolean
gtk_multi_selection_select_all (GtkSelectionModel *model)
{
return gtk_multi_selection_select_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)), FALSE);
}
static gboolean
gtk_multi_selection_unselect_all (GtkSelectionModel *model)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
self->last_selected = GTK_INVALID_LIST_POSITION;
return gtk_multi_selection_unselect_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
}
static void
gtk_multi_selection_query_range (GtkSelectionModel *model,
guint position,
guint *start_range,
guint *n_items,
gboolean *selected)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
guint upper_bound = g_list_model_get_n_items (self->model);
gtk_set_find_range (self->selected, position, upper_bound, start_range, n_items, selected);
}
static void
gtk_multi_selection_selection_model_init (GtkSelectionModelInterface *iface)
{
iface->is_selected = gtk_multi_selection_is_selected;
iface->select_item = gtk_multi_selection_select_item;
iface->unselect_item = gtk_multi_selection_unselect_item;
iface->select_range = gtk_multi_selection_select_range;
iface->unselect_range = gtk_multi_selection_unselect_range;
iface->select_all = gtk_multi_selection_select_all;
iface->unselect_all = gtk_multi_selection_unselect_all;
iface->query_range = gtk_multi_selection_query_range;
}
G_DEFINE_TYPE_EXTENDED (GtkMultiSelection, gtk_multi_selection, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
gtk_multi_selection_list_model_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL,
gtk_multi_selection_selection_model_init))
static void
gtk_multi_selection_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkMultiSelection *self)
{
gtk_set_remove_range (self->selected, position, removed);
gtk_set_shift (self->selected, position, (int)added - (int)removed);
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
}
static void
gtk_multi_selection_clear_model (GtkMultiSelection *self)
{
if (self->model == NULL)
return;
g_signal_handlers_disconnect_by_func (self->model,
gtk_multi_selection_items_changed_cb,
self);
g_clear_object (&self->model);
}
static void
gtk_multi_selection_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (object);
switch (prop_id)
{
case PROP_MODEL:
self->model = g_value_dup_object (value);
g_warn_if_fail (self->model != NULL);
g_signal_connect (self->model,
"items-changed",
G_CALLBACK (gtk_multi_selection_items_changed_cb),
self);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_multi_selection_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (object);
switch (prop_id)
{
case PROP_MODEL:
g_value_set_object (value, self->model);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_multi_selection_dispose (GObject *object)
{
GtkMultiSelection *self = GTK_MULTI_SELECTION (object);
gtk_multi_selection_clear_model (self);
g_clear_pointer (&self->selected, gtk_set_free);
self->last_selected = GTK_INVALID_LIST_POSITION;
G_OBJECT_CLASS (gtk_multi_selection_parent_class)->dispose (object);
}
static void
gtk_multi_selection_class_init (GtkMultiSelectionClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = gtk_multi_selection_get_property;
gobject_class->set_property = gtk_multi_selection_set_property;
gobject_class->dispose = gtk_multi_selection_dispose;
/**
* GtkMultiSelection:model
*
* The list managed by this selection
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("List managed by this selection"),
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_multi_selection_init (GtkMultiSelection *self)
{
self->selected = gtk_set_new ();
self->last_selected = GTK_INVALID_LIST_POSITION;
}
/**
* gtk_multi_selection_new:
* @model: (transfer none): the #GListModel to manage
*
* Creates a new selection to handle @model.
*
* Returns: (transfer full) (type GtkMultiSelection): a new #GtkMultiSelection
**/
GListModel *
gtk_multi_selection_new (GListModel *model)
{
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
return g_object_new (GTK_TYPE_MULTI_SELECTION,
"model", model,
NULL);
}
/**
* gtk_multi_selection_copy:
* @selection: the #GtkSelectionModel to copy
*
* Creates a #GtkMultiSelection that has the same underlying
* model and the same selected items as @selection.
*
* Returns: (transfer full): a new #GtkMultiSelection
*/
GtkMultiSelection *
gtk_multi_selection_copy (GtkSelectionModel *selection)
{
GtkMultiSelection *copy;
GListModel *model;
g_object_get (selection, "model", &model, NULL);
copy = GTK_MULTI_SELECTION (gtk_multi_selection_new (model));
if (GTK_IS_MULTI_SELECTION (selection))
{
GtkMultiSelection *multi = GTK_MULTI_SELECTION (selection);
gtk_set_free (copy->selected);
copy->selected = gtk_set_copy (multi->selected);
copy->last_selected = multi->last_selected;
}
else
{
guint pos, n;
guint start, n_items;
gboolean selected;
n = g_list_model_get_n_items (model);
n_items = 0;
for (pos = 0; pos < n; pos += n_items)
{
gtk_selection_model_query_range (selection, pos, &start, &n_items, &selected);
if (selected)
gtk_selection_model_select_range (GTK_SELECTION_MODEL (copy), start, n_items, FALSE);
}
}
g_object_unref (model);
return copy;
}
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright © 2019 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#ifndef __GTK_MULTI_SELECTION_H__
#define __GTK_MULTI_SELECTION_H__
#include <gtk/gtktypes.h>
#include <gtk/gtkselectionmodel.h>
G_BEGIN_DECLS
#define GTK_TYPE_MULTI_SELECTION (gtk_multi_selection_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkMultiSelection, gtk_multi_selection, GTK, MULTI_SELECTION, GObject)
GDK_AVAILABLE_IN_ALL
GListModel * gtk_multi_selection_new (GListModel *model);
GDK_AVAILABLE_IN_ALL
GtkMultiSelection * gtk_multi_selection_copy (GtkSelectionModel *selection);
G_END_DECLS
#endif /* __GTK_MULTI_SELECTION_H__ */
+3 -4
View File
@@ -25,7 +25,6 @@
#include "gtktypebuiltins.h"
#include <math.h>
#include "fallback-c89.c"
/**
* SECTION:gtknumericsorter
@@ -127,11 +126,11 @@ gtk_numeric_sorter_compare (GtkSorter *sorter,
float num1 = g_value_get_float (&value1);
float num2 = g_value_get_float (&value2);
if (isnanf (num1) && isnanf (num2))
if (isnan (num1) && isnan (num2))
result = GTK_ORDERING_EQUAL;
else if (isnanf (num1))
else if (isnan (num1))
result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_LARGER : GTK_ORDERING_SMALLER;
else if (isnanf (num2))
else if (isnan (num2))
result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER;
else if (num1 < num2)
result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER;
+59 -73
View File
@@ -29,6 +29,7 @@
#include "gtkprintbackendprivate.h"
static void gtk_print_backend_finalize (GObject *object);
static void gtk_print_backend_dispose (GObject *object);
static void gtk_print_backend_set_property (GObject *object,
guint prop_id,
@@ -41,7 +42,7 @@ static void gtk_print_backend_get_property (GObject *object,
struct _GtkPrintBackendPrivate
{
GHashTable *printers;
GListStore *printers;
guint printer_list_requested : 1;
guint printer_list_done : 1;
GtkPrintBackendStatus status;
@@ -251,6 +252,7 @@ gtk_print_backend_class_init (GtkPrintBackendClass *class)
backend_parent_class = g_type_class_peek_parent (class);
object_class->finalize = gtk_print_backend_finalize;
object_class->dispose = gtk_print_backend_dispose;
object_class->set_property = gtk_print_backend_set_property;
object_class->get_property = gtk_print_backend_get_property;
@@ -331,9 +333,7 @@ gtk_print_backend_init (GtkPrintBackend *backend)
priv = backend->priv = gtk_print_backend_get_instance_private (backend);
priv->printers = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) g_object_unref);
priv->printers = g_list_store_new (GTK_TYPE_PRINTER);
priv->auth_info_required = NULL;
priv->auth_info = NULL;
}
@@ -350,15 +350,21 @@ gtk_print_backend_dispose (GObject *object)
/* We unref the printers in dispose, not in finalize so that
* we can break refcount cycles with gtk_print_backend_destroy
*/
if (priv->printers)
{
g_hash_table_destroy (priv->printers);
priv->printers = NULL;
}
g_list_store_remove_all (priv->printers);
backend_parent_class->dispose (object);
}
static void
gtk_print_backend_finalize (GObject *object)
{
GtkPrintBackend *backend = GTK_PRINT_BACKEND (object);
GtkPrintBackendPrivate *priv = backend->priv;
g_clear_object (&priv->printers);
backend_parent_class->finalize (object);
}
static void
fallback_printer_request_details (GtkPrinter *printer)
@@ -367,7 +373,7 @@ fallback_printer_request_details (GtkPrinter *printer)
static gboolean
fallback_printer_mark_conflicts (GtkPrinter *printer,
GtkPrinterOptionSet *options)
GtkPrinterOptionSet *options)
{
return FALSE;
}
@@ -411,58 +417,25 @@ fallback_printer_get_capabilities (GtkPrinter *printer)
return 0;
}
static void
printer_hash_to_sorted_active_list (const gchar *key,
gpointer value,
GList **out_list)
{
GtkPrinter *printer;
printer = GTK_PRINTER (value);
if (gtk_printer_get_name (printer) == NULL)
return;
if (!gtk_printer_is_active (printer))
return;
*out_list = g_list_insert_sorted (*out_list, value, (GCompareFunc) gtk_printer_compare);
}
void
gtk_print_backend_add_printer (GtkPrintBackend *backend,
GtkPrinter *printer)
{
GtkPrintBackendPrivate *priv;
g_return_if_fail (GTK_IS_PRINT_BACKEND (backend));
priv = backend->priv;
if (!priv->printers)
return;
g_hash_table_insert (priv->printers,
g_strdup (gtk_printer_get_name (printer)),
g_object_ref (printer));
g_list_store_append (backend->priv->printers, printer);
}
void
gtk_print_backend_remove_printer (GtkPrintBackend *backend,
GtkPrinter *printer)
{
GtkPrintBackendPrivate *priv;
guint position;
g_return_if_fail (GTK_IS_PRINT_BACKEND (backend));
priv = backend->priv;
if (!priv->printers)
return;
g_hash_table_remove (priv->printers,
gtk_printer_get_name (printer));
if (g_list_store_find (backend->priv->printers, printer, &position))
g_list_store_remove (backend->priv->printers, position);
}
void
@@ -488,54 +461,67 @@ gtk_print_backend_set_list_done (GtkPrintBackend *backend)
GList *
gtk_print_backend_get_printer_list (GtkPrintBackend *backend)
{
GtkPrintBackendPrivate *priv;
GList *result;
GList *result = NULL;
guint i;
g_return_val_if_fail (GTK_IS_PRINT_BACKEND (backend), NULL);
priv = backend->priv;
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (backend->priv->printers)); i++)
{
GtkPrinter *printer = g_list_model_get_item (G_LIST_MODEL (backend->priv->printers), i);
result = g_list_prepend (result, printer);
g_object_unref (printer);
}
result = NULL;
if (priv->printers != NULL)
g_hash_table_foreach (priv->printers,
(GHFunc) printer_hash_to_sorted_active_list,
&result);
if (!priv->printer_list_requested && priv->printers != NULL)
if (!backend->priv->printer_list_requested)
{
if (GTK_PRINT_BACKEND_GET_CLASS (backend)->request_printer_list)
GTK_PRINT_BACKEND_GET_CLASS (backend)->request_printer_list (backend);
priv->printer_list_requested = TRUE;
backend->priv->printer_list_requested = TRUE;
}
return result;
}
gboolean
gtk_print_backend_printer_list_is_done (GtkPrintBackend *print_backend)
GListModel *
gtk_print_backend_get_printers (GtkPrintBackend *backend)
{
g_return_val_if_fail (GTK_IS_PRINT_BACKEND (print_backend), TRUE);
if (!backend->priv->printer_list_requested)
{
if (GTK_PRINT_BACKEND_GET_CLASS (backend)->request_printer_list)
GTK_PRINT_BACKEND_GET_CLASS (backend)->request_printer_list (backend);
backend->priv->printer_list_requested = TRUE;
}
return print_backend->priv->printer_list_done;
return G_LIST_MODEL (backend->priv->printers);
}
gboolean
gtk_print_backend_printer_list_is_done (GtkPrintBackend *backend)
{
g_return_val_if_fail (GTK_IS_PRINT_BACKEND (backend), TRUE);
return backend->priv->printer_list_done;
}
GtkPrinter *
gtk_print_backend_find_printer (GtkPrintBackend *backend,
const gchar *printer_name)
{
GtkPrintBackendPrivate *priv;
GtkPrinter *printer;
GtkPrinter *result = NULL;
guint i;
g_return_val_if_fail (GTK_IS_PRINT_BACKEND (backend), NULL);
priv = backend->priv;
for (i = 0; !result && i < g_list_model_get_n_items (G_LIST_MODEL (backend->priv->printers)); i++)
{
GtkPrinter *printer = g_list_model_get_item (G_LIST_MODEL (backend->priv->printers), i);
if (strcmp (gtk_printer_get_name (printer), printer_name) == 0)
result = printer;
g_object_unref (printer);
}
if (priv->printers)
printer = g_hash_table_lookup (priv->printers, printer_name);
else
printer = NULL;
return printer;
return result;
}
void
@@ -754,7 +740,7 @@ request_password (GtkPrintBackend *backend,
}
void
gtk_print_backend_destroy (GtkPrintBackend *print_backend)
gtk_print_backend_destroy (GtkPrintBackend *backend)
{
/* The lifecycle of print backends and printers are tied, such that
* the backend owns the printers, but the printers also ref the backend.
@@ -762,5 +748,5 @@ gtk_print_backend_destroy (GtkPrintBackend *print_backend)
* will be around. However, this results in a cycle, which we break
* with this call, which causes the print backend to release its printers.
*/
g_object_run_dispose (G_OBJECT (print_backend));
g_object_run_dispose (G_OBJECT (backend));
}
+2
View File
@@ -149,6 +149,8 @@ GType gtk_print_backend_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GList *gtk_print_backend_get_printer_list (GtkPrintBackend *print_backend);
GDK_AVAILABLE_IN_ALL
GListModel *gtk_print_backend_get_printers (GtkPrintBackend *print_backend);
GDK_AVAILABLE_IN_ALL
gboolean gtk_print_backend_printer_list_is_done (GtkPrintBackend *print_backend);
GDK_AVAILABLE_IN_ALL
GtkPrinter *gtk_print_backend_find_printer (GtkPrintBackend *print_backend,
+2 -2
View File
@@ -169,7 +169,7 @@ gtk_printer_class_init (GtkPrinterClass *class)
g_param_spec_string ("icon-name",
P_("Icon Name"),
P_("The icon name to use for the printer"),
"",
"printer",
GTK_PARAM_READABLE));
g_object_class_install_property (G_OBJECT_CLASS (class),
PROP_JOB_COUNT,
@@ -340,7 +340,7 @@ gtk_printer_get_property (GObject *object,
if (priv->icon_name)
g_value_set_string (value, priv->icon_name);
else
g_value_set_static_string (value, "");
g_value_set_static_string (value, "printer");
break;
case PROP_JOB_COUNT:
g_value_set_int (value, priv->job_count);
-2
View File
@@ -43,8 +43,6 @@
#include <string.h>
#include "fallback-c89.c"
/**
* SECTION:gtkprogressbar
* @Short_description: A widget which indicates progress visually
-2
View File
@@ -34,8 +34,6 @@
#include "gsk/gskroundedrectprivate.h"
#include <gdk/gdktextureprivate.h>
#include "fallback-c89.c"
static void
gtk_do_render_icon (GtkStyleContext *context,
cairo_t *cr,
-5
View File
@@ -43,11 +43,6 @@
#include "gsk/gskroundedrectprivate.h"
/* this is in case round() is not provided by the compiler,
* such as in the case of C89 compilers, like MSVC
*/
#include "fallback-c89.c"
static void
gtk_theming_background_snapshot_color (GtkCssBoxes *boxes,
GtkSnapshot *snapshot,
-5
View File
@@ -39,11 +39,6 @@
#include "gsk/gskroundedrectprivate.h"
/* this is in case round() is not provided by the compiler,
* such as in the case of C89 compilers, like MSVC
*/
#include "fallback-c89.c"
typedef struct _GtkBorderImage GtkBorderImage;
struct _GtkBorderImage {
-2
View File
@@ -32,8 +32,6 @@
#include "gtkwidgetprivate.h"
#include "gtkbuildable.h"
#include "fallback-c89.c"
/**
* SECTION:gtkrevealer
* @Short_description: Hide and show with animation
+351
View File
@@ -0,0 +1,351 @@
/*
* Copyright © 2019 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#include "gtkset.h"
/* Store a set of unsigned integers as a sorted array of ranges.
*/
typedef struct
{
guint first;
guint n_items;
} Range;
struct _GtkSet
{
GArray *ranges;
};
typedef struct
{
GtkSet *set;
Range *current;
int idx;
guint pos;
} GtkRealSetIter;
GtkSet *
gtk_set_new (void)
{
GtkSet *set;
set = g_new (GtkSet, 1);
set->ranges = g_array_new (FALSE, FALSE, sizeof (Range));
return set;
}
GtkSet *
gtk_set_copy (GtkSet *set)
{
GtkSet *copy;
copy = g_new (GtkSet, 1);
copy->ranges = g_array_copy (set->ranges);
return copy;
}
void
gtk_set_free (GtkSet *set)
{
g_array_free (set->ranges, TRUE);
g_free (set);
}
gboolean
gtk_set_contains (GtkSet *set,
guint item)
{
int i;
for (i = 0; i < set->ranges->len; i++)
{
Range *r = &g_array_index (set->ranges, Range, i);
if (item < r->first)
return FALSE;
if (item < r->first + r->n_items)
return TRUE;
}
return FALSE;
}
void
gtk_set_remove_all (GtkSet *set)
{
g_array_set_size (set->ranges, 0);
}
static int
range_compare (Range *r, Range *s)
{
int ret = 0;
if (r->first + r->n_items < s->first)
ret = -1;
else if (s->first + s->n_items < r->first)
ret = 1;
return ret;
}
void
gtk_set_add_range (GtkSet *set,
guint first_item,
guint n_items)
{
int i;
Range s;
int first = -1;
int last = -1;
s.first = first_item;
s.n_items = n_items;
for (i = 0; i < set->ranges->len; i++)
{
Range *r = &g_array_index (set->ranges, Range, i);
int cmp = range_compare (&s, r);
if (cmp < 0)
break;
if (cmp == 0)
{
if (first < 0)
first = i;
last = i;
}
}
if (first > -1)
{
Range *r;
guint start;
guint end;
r = &g_array_index (set->ranges, Range, first);
start = MIN (s.first, r->first);
r = &g_array_index (set->ranges, Range, last);
end = MAX (s.first + s.n_items - 1, r->first + r->n_items - 1);
s.first = start;
s.n_items = end - start + 1;
g_array_remove_range (set->ranges, first, last - first + 1);
g_array_insert_val (set->ranges, first, s);
}
else
g_array_insert_val (set->ranges, i, s);
}
void
gtk_set_remove_range (GtkSet *set,
guint first_item,
guint n_items)
{
Range s;
int i;
int first = -1;
int last = -1;
s.first = first_item;
s.n_items = n_items;
for (i = 0; i < set->ranges->len; i++)
{
Range *r = &g_array_index (set->ranges, Range, i);
int cmp = range_compare (&s, r);
if (cmp < 0)
break;
if (cmp == 0)
{
if (first < 0)
first = i;
last = i;
}
}
if (first > -1)
{
Range *r;
Range a[2];
int k = 0;
r = &g_array_index (set->ranges, Range, first);
if (r->first < s.first)
{
a[k].first = r->first;
a[k].n_items = s.first - r->first;
k++;
}
r = &g_array_index (set->ranges, Range, last);
if (r->first + r->n_items > s.first + s.n_items)
{
a[k].first = s.first + s.n_items;
a[k].n_items = r->first + r->n_items - a[k].first;
k++;
}
g_array_remove_range (set->ranges, first, last - first + 1);
if (k > 0)
g_array_insert_vals (set->ranges, first, a, k);
}
}
void
gtk_set_find_range (GtkSet *set,
guint position,
guint upper_bound,
guint *start,
guint *n_items,
gboolean *contained)
{
int i;
int last = 0;
if (position >= upper_bound)
{
*start = 0;
*n_items = 0;
*contained = FALSE;
return;
}
for (i = 0; i < set->ranges->len; i++)
{
Range *r = &g_array_index (set->ranges, Range, i);
if (position < r->first)
{
*start = last;
*n_items = r->first - last;
*contained = FALSE;
return;
}
else if (r->first <= position && position < r->first + r->n_items)
{
*start = r->first;
*n_items = r->n_items;
*contained = TRUE;
return;
}
else
last = r->first + r->n_items;
}
*start = last;
*n_items = upper_bound - last;
*contained = FALSE;
}
void
gtk_set_add_item (GtkSet *set,
guint item)
{
gtk_set_add_range (set, item, 1);
}
void
gtk_set_remove_item (GtkSet *set,
guint item)
{
gtk_set_remove_range (set, item, 1);
}
/* This is peculiar operation: Replace every number n >= first by n + shift
* This is only supported for negative shifts if the shifting does not cause
* any ranges to overlap.
*/
void
gtk_set_shift (GtkSet *set,
guint first,
int shift)
{
int i;
for (i = 0; i < set->ranges->len; i++)
{
Range *r = &g_array_index (set->ranges, Range, i);
if (r->first >= first)
r->first += shift;
}
}
void
gtk_set_iter_init (GtkSetIter *iter,
GtkSet *set)
{
GtkRealSetIter *ri = (GtkRealSetIter *)iter;
ri->set = set;
ri->idx = -1;
ri->current = 0;
}
gboolean
gtk_set_iter_next (GtkSetIter *iter,
guint *item)
{
GtkRealSetIter *ri = (GtkRealSetIter *)iter;
if (ri->idx == -1)
{
next_range:
ri->idx++;
if (ri->idx == ri->set->ranges->len)
return FALSE;
ri->current = &g_array_index (ri->set->ranges, Range, ri->idx);
ri->pos = ri->current->first;
}
else
{
ri->pos++;
if (ri->pos == ri->current->first + ri->current->n_items)
goto next_range;
}
*item = ri->pos;
return TRUE;
}
#if 0
void
gtk_set_dump (GtkSet *set)
{
int i;
for (i = 0; i < set->ranges->len; i++)
{
Range *r = &g_array_index (set->ranges, Range, i);
g_print (" %u:%u", r->first, r->n_items);
}
g_print ("\n");
}
#endif
+70
View File
@@ -0,0 +1,70 @@
/*
* Copyright © 2019 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#ifndef __GTK_SET_H__
#define __GTK_SET_H__
#include <glib.h>
typedef struct _GtkSet GtkSet;
typedef struct _GtkSetIter GtkSetIter;
struct _GtkSetIter
{
gpointer dummy1;
gpointer dummy2;
int dummy3;
int dummy4;
};
GtkSet *gtk_set_new (void);
void gtk_set_free (GtkSet *set);
GtkSet *gtk_set_copy (GtkSet *set);
gboolean gtk_set_contains (GtkSet *set,
guint item);
void gtk_set_remove_all (GtkSet *set);
void gtk_set_add_item (GtkSet *set,
guint item);
void gtk_set_remove_item (GtkSet *set,
guint item);
void gtk_set_add_range (GtkSet *set,
guint first,
guint n);
void gtk_set_remove_range (GtkSet *set,
guint first,
guint n);
void gtk_set_find_range (GtkSet *set,
guint position,
guint upper_bound,
guint *start,
guint *n_items,
gboolean *contained);
void gtk_set_shift (GtkSet *set,
guint first,
int shift);
void gtk_set_iter_init (GtkSetIter *iter,
GtkSet *set);
gboolean gtk_set_iter_next (GtkSetIter *iter,
guint *item);
#endif /* __GTK_SET_H__ */
-2
View File
@@ -71,8 +71,6 @@
#include "a11y/gtkswitchaccessible.h"
#include "fallback-c89.c"
typedef struct _GtkSwitchClass GtkSwitchClass;
/**
-2
View File
@@ -73,8 +73,6 @@
#include <cairo-gobject.h>
#include <string.h>
#include "fallback-c89.c"
/**
* SECTION:gtktext
* @Short_description: A simple single-line text entry field
-3
View File
@@ -90,9 +90,6 @@
#include <stdarg.h>
#include <string.h>
/* for the use of round() */
#include "fallback-c89.c"
/**
* SECTION:gtkwidget
* @Short_description: Base class for all widgets
+7 -3
View File
@@ -96,12 +96,14 @@ variant_editor_new (const GVariantType *type,
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
{
editor = gtk_entry_new ();
gtk_editable_set_width_chars (GTK_EDITABLE (editor), 10);
g_signal_connect (editor, "notify::text", G_CALLBACK (variant_editor_changed_cb), d);
}
else
{
editor = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
entry = gtk_entry_new ();
gtk_editable_set_width_chars (GTK_EDITABLE (entry), 10);
gtk_box_append (GTK_BOX (editor), entry);
label = gtk_label_new (g_variant_type_peek_string (type));
gtk_box_append (GTK_BOX (editor), label);
@@ -284,7 +286,8 @@ constructed (GObject *object)
row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
activate = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
gtk_box_append (GTK_BOX (row), activate);
gtk_size_group_add_widget (r->priv->sg, activate);
if (r->priv->sg)
gtk_size_group_add_widget (r->priv->sg, activate);
r->priv->activate_button = gtk_button_new_with_label (_("Activate"));
g_signal_connect (r->priv->activate_button, "clicked", G_CALLBACK (activate_action), r);
@@ -307,7 +310,8 @@ constructed (GObject *object)
r->priv->state_type = g_variant_type_copy (g_variant_get_type (state));
row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
label = gtk_label_new (_("Set State"));
gtk_size_group_add_widget (r->priv->sg, label);
if (r->priv->sg)
gtk_size_group_add_widget (r->priv->sg, label);
gtk_box_append (GTK_BOX (row), label);
r->priv->state_entry = variant_editor_new (r->priv->state_type, state_changed, r);
variant_editor_set_value (r->priv->state_entry, state);
@@ -327,7 +331,7 @@ finalize (GObject *object)
GtkInspectorActionEditor *r = GTK_INSPECTOR_ACTION_EDITOR (object);
g_free (r->priv->name);
g_object_unref (r->priv->sg);
g_clear_object (&r->priv->sg);
if (r->priv->state_type)
g_variant_type_free (r->priv->state_type);
g_signal_handlers_disconnect_by_func (r->priv->group, action_enabled_changed_cb, r);
+61
View File
@@ -0,0 +1,61 @@
#include "action-holder.h"
struct _ActionHolder {
GObject instance;
GActionGroup *group;
char *name;
};
G_DEFINE_TYPE (ActionHolder, action_holder, G_TYPE_OBJECT)
static void
action_holder_init (ActionHolder *holder)
{
}
static void
action_holder_finalize (GObject *object)
{
ActionHolder *holder = ACTION_HOLDER (object);
g_object_unref (holder->group);
g_free (holder->name);
G_OBJECT_CLASS (action_holder_parent_class)->finalize (object);
}
static void
action_holder_class_init (ActionHolderClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = action_holder_finalize;
}
ActionHolder *
action_holder_new (GActionGroup *group,
const char *name)
{
ActionHolder *holder;
holder = g_object_new (ACTION_TYPE_HOLDER, NULL);
holder->group = g_object_ref (group);
holder->name = g_strdup (name);
return holder;
}
GActionGroup *
action_holder_get_group (ActionHolder *holder)
{
return holder->group;
}
const char *
action_holder_get_name (ActionHolder *holder)
{
return holder->name;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef __ACTION_HOLDER_H__
#define __ACTION_HOLDER_H__
#include <gtk/gtk.h>
#define ACTION_TYPE_HOLDER (action_holder_get_type ())
G_DECLARE_FINAL_TYPE (ActionHolder, action_holder, ACTION, HOLDER, GObject)
ActionHolder * action_holder_new (GActionGroup *group,
const char *name);
GActionGroup *action_holder_get_group (ActionHolder *holder);
const char *action_holder_get_name (ActionHolder *holder);
#endif /* __ACTION_HOLDER_H__ */
+232 -150
View File
@@ -20,6 +20,7 @@
#include "actions.h"
#include "action-editor.h"
#include "action-holder.h"
#include "gtkapplication.h"
#include "gtkapplicationwindow.h"
@@ -37,13 +38,11 @@
struct _GtkInspectorActionsPrivate
{
GtkWidget *list;
GtkSizeGroup *name;
GtkSizeGroup *enabled;
GtkSizeGroup *parameter;
GtkSizeGroup *state;
GtkSizeGroup *activate;
GActionGroup *group;
GtkWidget *button;
GActionGroup *group;
GListModel *actions;
GtkColumnViewColumn *name;
};
enum {
@@ -61,105 +60,170 @@ gtk_inspector_actions_init (GtkInspectorActions *sl)
}
static void
add_action (GtkInspectorActions *sl,
GActionGroup *group,
const gchar *name)
action_added_cb (GActionGroup *group,
const gchar *action_name,
GtkInspectorActions *sl)
{
gboolean enabled;
const gchar *parameter;
GVariant *state;
gchar *state_string;
GtkWidget *row;
GtkWidget *label;
GtkWidget *box;
char *key = g_strdup (name);
GtkWidget *editor;
ActionHolder *holder = action_holder_new (group, action_name);
g_list_store_append (G_LIST_STORE (sl->priv->actions), holder);
g_object_unref (holder);
}
static void
setup_name_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *label;
label = gtk_label_new (NULL);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_list_item_set_child (list_item, label);
}
static void
bind_name_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
gpointer item;
GtkWidget *label;
item = gtk_list_item_get_item (list_item);
label = gtk_list_item_get_child (list_item);
gtk_label_set_label (GTK_LABEL (label), action_holder_get_name (ACTION_HOLDER (item)));
}
static void
setup_enabled_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *label;
label = gtk_label_new (NULL);
gtk_label_set_xalign (GTK_LABEL (label), 0.5);
gtk_list_item_set_child (list_item, label);
}
static void
bind_enabled_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
gpointer item;
GtkWidget *label;
GActionGroup *group;
const char *name;
gboolean enabled;
item = gtk_list_item_get_item (list_item);
label = gtk_list_item_get_child (list_item);
group = action_holder_get_group (ACTION_HOLDER (item));
name = action_holder_get_name (ACTION_HOLDER (item));
enabled = g_action_group_get_action_enabled (group, name);
gtk_label_set_label (GTK_LABEL (label), enabled ? "+" : "-");
}
static void
setup_parameter_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *label;
label = gtk_label_new (NULL);
gtk_label_set_xalign (GTK_LABEL (label), 0.5);
gtk_list_item_set_child (list_item, label);
gtk_style_context_add_class (gtk_widget_get_style_context (label), "cell");
}
static void
bind_parameter_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
gpointer item;
GtkWidget *label;
GActionGroup *group;
const char *name;
const char *parameter;
item = gtk_list_item_get_item (list_item);
label = gtk_list_item_get_child (list_item);
group = action_holder_get_group (ACTION_HOLDER (item));
name = action_holder_get_name (ACTION_HOLDER (item));
parameter = (const gchar *)g_action_group_get_action_parameter_type (group, name);
gtk_label_set_label (GTK_LABEL (label), parameter);
}
static void
setup_state_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *label;
label = gtk_label_new (NULL);
gtk_widget_set_margin_start (label, 5);
gtk_widget_set_margin_end (label, 5);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_list_item_set_child (list_item, label);
gtk_style_context_add_class (gtk_widget_get_style_context (label), "cell");
}
static void
bind_state_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
gpointer item;
GtkWidget *label;
GActionGroup *group;
const char *name;
GVariant *state;
char *state_string;
item = gtk_list_item_get_item (list_item);
label = gtk_list_item_get_child (list_item);
group = action_holder_get_group (ACTION_HOLDER (item));
name = action_holder_get_name (ACTION_HOLDER (item));
state = g_action_group_get_action_state (group, name);
if (state)
state_string = g_variant_print (state, FALSE);
else
state_string = g_strdup ("");
row = gtk_list_box_row_new ();
g_object_set_data_full (G_OBJECT (row), "key", key, g_free);
gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box);
label = gtk_label_new (name);
gtk_widget_add_css_class (label, "cell");
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_size_group_add_widget (sl->priv->name, label);
gtk_box_append (GTK_BOX (box), label);
label = gtk_label_new (enabled ? "+" : "-");
gtk_widget_add_css_class (label, "cell");
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
gtk_size_group_add_widget (sl->priv->enabled, label);
gtk_box_append (GTK_BOX (box), label);
g_object_set_data (G_OBJECT (row), "enabled", label);
label = gtk_label_new (parameter);
gtk_widget_add_css_class (label, "cell");
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_size_group_add_widget (sl->priv->parameter, label);
gtk_box_append (GTK_BOX (box), label);
label = gtk_label_new (state_string);
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_widget_add_css_class (label, "cell");
gtk_size_group_add_widget (sl->priv->state, label);
gtk_box_append (GTK_BOX (box), label);
g_object_set_data (G_OBJECT (row), "state", label);
editor = gtk_inspector_action_editor_new (group, name, sl->priv->activate);
gtk_widget_add_css_class (editor, "cell");
gtk_box_append (GTK_BOX (box), editor);
g_object_set_data (G_OBJECT (row), "editor", editor);
gtk_list_box_insert (GTK_LIST_BOX (sl->priv->list), row, -1);
gtk_label_set_label (GTK_LABEL (label), state_string);
g_free (state_string);
}
static GtkWidget *
find_row (GtkInspectorActions *sl,
const char *action_name)
{
GtkWidget *row = NULL;
GtkWidget *widget;
const char *key;
for (widget = gtk_widget_get_first_child (sl->priv->list);
widget;
widget = gtk_widget_get_next_sibling (widget))
{
if (!GTK_IS_LIST_BOX_ROW (widget))
continue;
key = g_object_get_data (G_OBJECT (widget), "key");
if (g_str_equal (key, action_name))
{
row = widget;
break;
}
}
return row;
if (state)
g_variant_unref (state);
}
static void
action_added_cb (GActionGroup *group,
const gchar *action_name,
GtkInspectorActions *sl)
bind_changes_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
add_action (sl, group, action_name);
gpointer item;
GActionGroup *group;
const char *name;
GtkWidget *editor;
item = gtk_list_item_get_item (list_item);
group = action_holder_get_group (ACTION_HOLDER (item));
name = action_holder_get_name (ACTION_HOLDER (item));
editor = gtk_inspector_action_editor_new (group, name, NULL);
gtk_style_context_add_class (gtk_widget_get_style_context (editor), "cell");
gtk_list_item_set_child (list_item, editor);
}
static void
unbind_changes_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
gtk_list_item_set_child (list_item, NULL);
}
static void
@@ -167,21 +231,37 @@ action_removed_cb (GActionGroup *group,
const gchar *action_name,
GtkInspectorActions *sl)
{
GtkWidget *row;
int i;
row = find_row (sl, action_name);
if (row)
gtk_list_box_remove (GTK_LIST_BOX (sl->priv->list), row);
for (i = 0; i < g_list_model_get_n_items (sl->priv->actions); i++)
{
ActionHolder *holder = g_list_model_get_item (sl->priv->actions, i);
if (group == action_holder_get_group (holder) &&
strcmp (action_name, action_holder_get_name (holder)) == 0)
g_list_store_remove (G_LIST_STORE (sl->priv->actions), i);
g_object_unref (holder);
}
}
static void
set_row_enabled (GtkWidget *row,
gboolean enabled)
notify_action_changed (GtkInspectorActions *sl,
GActionGroup *group,
const char *action_name)
{
GtkWidget *label;
int i;
label = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "enabled"));
gtk_label_set_label (GTK_LABEL (label), enabled ? "+" : "-" );
for (i = 0; i < g_list_model_get_n_items (sl->priv->actions); i++)
{
ActionHolder *holder = g_list_model_get_item (sl->priv->actions, i);
if (group == action_holder_get_group (holder) &&
strcmp (action_name, action_holder_get_name (holder)) == 0)
g_list_model_items_changed (sl->priv->actions, i, 1, 1);
g_object_unref (holder);
}
}
static void
@@ -190,26 +270,7 @@ action_enabled_changed_cb (GActionGroup *group,
gboolean enabled,
GtkInspectorActions *sl)
{
GtkWidget *row;
row = find_row (sl, action_name);
set_row_enabled (row, enabled);
}
static void
set_row_state (GtkWidget *row,
GVariant *state)
{
gchar *state_string;
GtkWidget *label;
if (state)
state_string = g_variant_print (state, FALSE);
else
state_string = g_strdup ("");
label = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "state"));
gtk_label_set_label (GTK_LABEL (label), state_string);
g_free (state_string);
notify_action_changed (sl, group, action_name);
}
static void
@@ -218,35 +279,14 @@ action_state_changed_cb (GActionGroup *group,
GVariant *state,
GtkInspectorActions *sl)
{
GtkWidget *row;
row = find_row (sl, action_name);
set_row_state (row, state);
notify_action_changed (sl, group, action_name);
}
static void
refresh_all (GtkInspectorActions *sl)
{
GtkWidget *widget;
for (widget = gtk_widget_get_first_child (sl->priv->list);
widget;
widget = gtk_widget_get_next_sibling (widget))
{
const char *name = g_object_get_data (G_OBJECT (widget), "key");
gboolean enabled;
GVariant *state;
GtkInspectorActionEditor *r;
enabled = g_action_group_get_action_enabled (sl->priv->group, name);
state = g_action_group_get_action_state (sl->priv->group, name);
set_row_enabled (widget, enabled);
set_row_state (widget, state);
r = (GtkInspectorActionEditor*)g_object_get_data (G_OBJECT (widget), "editor");
gtk_inspector_action_editor_update (r, enabled, state);
}
guint n = g_list_model_get_n_items (sl->priv->actions);
g_list_model_items_changed (sl->priv->actions, 0, n, n);
}
static void
@@ -283,7 +323,7 @@ add_group (GtkInspectorActions *sl,
names = g_action_group_list_actions (group);
for (i = 0; names[i]; i++)
add_action (sl, group, names[i]);
action_added_cb (group, names[i], sl);
g_strfreev (names);
g_set_object (&sl->priv->group, group);
@@ -305,7 +345,6 @@ gtk_inspector_actions_set_object (GtkInspectorActions *sl,
{
GtkWidget *stack;
GtkStackPage *page;
GtkWidget *child;
stack = gtk_widget_get_parent (GTK_WIDGET (sl));
page = gtk_stack_get_page (GTK_STACK (stack), GTK_WIDGET (sl));
@@ -315,8 +354,7 @@ gtk_inspector_actions_set_object (GtkInspectorActions *sl,
if (sl->priv->group)
remove_group (sl, page, sl->priv->group);
while ((child = gtk_widget_get_first_child (sl->priv->list)))
gtk_list_box_remove (GTK_LIST_BOX (sl->priv->list), child);
g_list_store_remove_all (G_LIST_STORE (sl->priv->actions));
if (GTK_IS_APPLICATION (object))
add_group (sl, page, G_ACTION_GROUP (object));
@@ -328,6 +366,8 @@ gtk_inspector_actions_set_object (GtkInspectorActions *sl,
if (muxer)
add_group (sl, page, G_ACTION_GROUP (muxer));
}
gtk_column_view_sort_by_column (GTK_COLUMN_VIEW (sl->priv->list), sl->priv->name, GTK_SORT_ASCENDING);
}
static void
@@ -370,13 +410,48 @@ set_property (GObject *object,
}
}
static char *
holder_name (gpointer item)
{
return g_strdup (action_holder_get_name (ACTION_HOLDER (item)));
}
static void
constructed (GObject *object)
{
GtkInspectorActions *sl = GTK_INSPECTOR_ACTIONS (object);
GtkSorter *sorter;
GListModel *sorted;
GListModel *model;
g_signal_connect_swapped (sl->priv->button, "clicked",
G_CALLBACK (refresh_all), sl);
sorter = gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING,
NULL,
0, NULL,
(GCallback)holder_name,
NULL, NULL));
gtk_column_view_column_set_sorter (sl->priv->name, sorter);
g_object_unref (sorter);
sl->priv->actions = G_LIST_MODEL (g_list_store_new (ACTION_TYPE_HOLDER));
sorted = G_LIST_MODEL (gtk_sort_list_model_new (sl->priv->actions,
gtk_column_view_get_sorter (GTK_COLUMN_VIEW (sl->priv->list))));
model = G_LIST_MODEL (gtk_no_selection_new (sorted));
gtk_column_view_set_model (GTK_COLUMN_VIEW (sl->priv->list), model);
g_object_unref (sorted);
g_object_unref (model);
}
static void
finalize (GObject *object)
{
GtkInspectorActions *sl = GTK_INSPECTOR_ACTIONS (object);
g_object_unref (sl->priv->actions);
G_OBJECT_CLASS (gtk_inspector_actions_parent_class)->finalize (object);
}
static void
@@ -385,6 +460,7 @@ gtk_inspector_actions_class_init (GtkInspectorActionsClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->finalize = finalize;
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->constructed = constructed;
@@ -396,10 +472,16 @@ gtk_inspector_actions_class_init (GtkInspectorActionsClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/actions.ui");
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, list);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, name);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, enabled);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, parameter);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, state);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, activate);
gtk_widget_class_bind_template_callback (widget_class, setup_name_cb);
gtk_widget_class_bind_template_callback (widget_class, bind_name_cb);
gtk_widget_class_bind_template_callback (widget_class, setup_enabled_cb);
gtk_widget_class_bind_template_callback (widget_class, bind_enabled_cb);
gtk_widget_class_bind_template_callback (widget_class, setup_parameter_cb);
gtk_widget_class_bind_template_callback (widget_class, bind_parameter_cb);
gtk_widget_class_bind_template_callback (widget_class, setup_state_cb);
gtk_widget_class_bind_template_callback (widget_class, bind_state_cb);
gtk_widget_class_bind_template_callback (widget_class, bind_changes_cb);
gtk_widget_class_bind_template_callback (widget_class, unbind_changes_cb);
}
// vim: set et sw=2 ts=2:
+57 -76
View File
@@ -1,98 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="gtk40">
<object class="GtkListStore" id="model">
<columns>
<column type="gchararray"/>
<column type="gchararray"/>
<column type="gboolean"/>
<column type="gchararray"/>
<column type="gchararray"/>
<column type="gpointer"/>
</columns>
</object>
<template class="GtkInspectorActions" parent="GtkBox">
<property name="orientation">vertical</property>
<style>
<class name="view"/>
</style>
<child>
<object class="GtkBox">
<style>
<class name="header"/>
</style>
<child>
<object class="GtkLabel" id="name_heading">
<property name="label" translatable="yes">Name</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkLabel" id="enabled_heading">
<property name="label" translatable="yes">Enabled</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkLabel" id="parameter_heading">
<property name="label" translatable="yes">Parameter Type</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkLabel" id="state_heading">
<property name="label" translatable="yes">State</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkLabel" id="changes_heading">
<property name="label" translatable="yes"></property>
<property name="xalign">0</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="hexpand">1</property>
<property name="vexpand">1</property>
<property name="hscrollbar-policy">never</property>
<child>
<object class="GtkListBox" id="list">
<object class="GtkColumnView" id="list">
<style>
<class name="list"/>
</style>
<property name="selection-mode">none</property>
<child>
<object class="GtkColumnViewColumn" id="name">
<property name="title" translatable="yes">Name</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_name_cb"/>
<signal name="bind" handler="bind_name_cb"/>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="enabled">
<property name="title" translatable="yes">Enabled</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_enabled_cb"/>
<signal name="bind" handler="bind_enabled_cb"/>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="parameter">
<property name="title" translatable="yes">Parameter Type</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_parameter_cb"/>
<signal name="bind" handler="bind_parameter_cb"/>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="state">
<property name="title" translatable="yes">State</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_state_cb"/>
<signal name="bind" handler="bind_state_cb"/>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="changes">
<property name="title"></property>
<property name="expand">1</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="bind" handler="bind_changes_cb"/>
<signal name="unbind" handler="unbind_changes_cb"/>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
<object class="GtkSizeGroup" id="name">
<property name="mode">horizontal</property>
<widgets>
<widget name="name_heading"/>
</widgets>
</object>
<object class="GtkSizeGroup" id="enabled">
<property name="mode">horizontal</property>
<widgets>
<widget name="enabled_heading"/>
</widgets>
</object>
<object class="GtkSizeGroup" id="parameter">
<property name="mode">horizontal</property>
<widgets>
<widget name="parameter_heading"/>
</widgets>
</object>
<object class="GtkSizeGroup" id="state">
<property name="mode">horizontal</property>
<widgets>
<widget name="state_heading"/>
</widgets>
</object>
<object class="GtkSizeGroup" id="activate">
<property name="mode">horizontal</property>
</object>
</interface>
+15 -7
View File
@@ -1,21 +1,29 @@
/* some style for the inspector */
.header {
background: lightgray;
border: 1px solid gray;
.list header {
background: white;
border: 1px solid lightgray;
}
.header>* {
.list header button {
background: none;
}
.list header button.dnd {
background: gray;
}
.list header>* {
padding: 2px;
font-weight: bold;
}
.header sort_indicator {
.list header sort_indicator {
min-width: 16px;
}
.header>*:not(:last-child) {
border-right: 1px solid gray;
.list header>*:not(:last-child) {
border-right: 1px solid lightgray;
}
.list .cell {
+3
View File
@@ -38,4 +38,7 @@ inspector_sources = files(
'updatesoverlay.c',
'visual.c',
'window.c',
'prop-holder.c',
'resource-holder.c',
'action-holder.c'
)
+28
View File
@@ -336,6 +336,29 @@ object_tree_tree_view_get_children (GObject *object)
return G_LIST_MODEL (result);
}
static GListModel *
object_tree_column_view_get_children (GObject *object)
{
GtkColumnView *view = GTK_COLUMN_VIEW (object);
GListStore *result_list;
GtkFlattenListModel *result;
GListModel *columns, *sublist;
result_list = g_list_store_new (G_TYPE_LIST_MODEL);
columns = gtk_column_view_get_columns (view);
g_list_store_append (result_list, columns);
sublist = object_tree_widget_get_children (object);
g_list_store_append (result_list, sublist);
g_object_unref (sublist);
result = gtk_flatten_list_model_new (G_TYPE_OBJECT, G_LIST_MODEL (result_list));
g_object_unref (result_list);
return G_LIST_MODEL (result);
}
static GListModel *
object_tree_icon_view_get_children (GObject *object)
{
@@ -499,6 +522,11 @@ static const ObjectTreeClassFuncs object_tree_class_funcs[] = {
object_tree_widget_get_parent,
object_tree_tree_view_get_children
},
{
gtk_column_view_get_type,
object_tree_widget_get_parent,
object_tree_column_view_get_children
},
{
gtk_combo_box_get_type,
object_tree_widget_get_parent,
+3
View File
@@ -45,6 +45,7 @@
<child>
<object class="GtkColumnViewColumn">
<property name="title">Type</property>
<property name="expand">1</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_type_cb"/>
@@ -57,6 +58,7 @@
<child>
<object class="GtkColumnViewColumn">
<property name="title">Name</property>
<property name="expand">1</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_name_cb"/>
@@ -68,6 +70,7 @@
<child>
<object class="GtkColumnViewColumn">
<property name="title">Label</property>
<property name="expand">1</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_label_cb"/>
+145
View File
@@ -0,0 +1,145 @@
#include "prop-holder.h"
enum {
PROP_OBJECT = 1,
PROP_PSPEC,
PROP_NAME,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
struct _PropHolder {
GObject instance;
GObject *object;
GParamSpec *pspec;
};
G_DEFINE_TYPE (PropHolder, prop_holder, G_TYPE_OBJECT)
static void
prop_holder_init (PropHolder *holder)
{
}
static void
prop_holder_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
PropHolder *holder = PROP_HOLDER (object);
switch (prop_id)
{
case PROP_OBJECT:
holder->object = g_value_dup_object (value);
break;
case PROP_PSPEC:
holder->pspec = g_value_get_param (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
prop_holder_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
PropHolder *holder = PROP_HOLDER (object);
switch (prop_id)
{
case PROP_OBJECT:
g_value_set_object (value, holder->object);
break;
case PROP_PSPEC:
g_value_set_param (value, holder->pspec);
break;
case PROP_NAME:
g_value_set_string (value, holder->pspec->name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
prop_holder_finalize (GObject *object)
{
PropHolder *holder = PROP_HOLDER (object);
g_object_unref (holder->object);
G_OBJECT_CLASS (prop_holder_parent_class)->finalize (object);
}
static void
prop_holder_class_init (PropHolderClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = prop_holder_finalize;
object_class->set_property = prop_holder_set_property;
object_class->get_property = prop_holder_get_property;
properties[PROP_OBJECT] =
g_param_spec_object ("object", "object", "object",
G_TYPE_OBJECT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
properties[PROP_PSPEC] =
g_param_spec_param ("pspec", "pspec", "pspec",
G_TYPE_PARAM,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
properties[PROP_NAME] =
g_param_spec_string ("name", "name", "name",
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
PropHolder *
prop_holder_new (GObject *object,
GParamSpec *pspec)
{
PropHolder *holder;
holder = g_object_new (PROP_TYPE_HOLDER,
"object", object,
"pspec", pspec,
NULL);
return holder;
}
GObject *
prop_holder_get_object (PropHolder *holder)
{
return holder->object;
}
GParamSpec *
prop_holder_get_pspec (PropHolder *holder)
{
return holder->pspec;
}
const char *
prop_holder_get_name (PropHolder *holder)
{
return holder->pspec->name;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef __PROP_HOLDER_H__
#define __PROP_HOLDER_H__
#include <gtk/gtk.h>
#define PROP_TYPE_HOLDER (prop_holder_get_type ())
G_DECLARE_FINAL_TYPE (PropHolder, prop_holder, PROP, HOLDER, GObject)
PropHolder * prop_holder_new (GObject *object,
GParamSpec *pspeC);
GObject *prop_holder_get_object (PropHolder *holder);
GParamSpec *prop_holder_get_pspec (PropHolder *holder);
const char *prop_holder_get_name (PropHolder *holder);
#endif /* __PROP_HOLDER_H__ */
+210 -199
View File
@@ -44,6 +44,7 @@
#include "gtkroot.h"
#include "gtkgestureclick.h"
#include "gtkstylecontext.h"
#include "prop-holder.h"
enum
{
@@ -52,11 +53,6 @@ enum
PROP_SEARCH_ENTRY
};
typedef enum {
COLUMN_NAME,
COLUMN_ORIGIN
} SortColumn;
struct _GtkInspectorPropListPrivate
{
GObject *object;
@@ -64,17 +60,11 @@ struct _GtkInspectorPropListPrivate
GtkInspectorObjectTree *object_tree;
GtkWidget *search_entry;
GtkWidget *search_stack;
GtkWidget *list2;
GtkWidget *name_sort_indicator;
GtkWidget *origin_sort_indicator;
GtkWidget *name_heading;
GtkWidget *origin_heading;
SortColumn sort_column;
GtkSortType sort_direction;
GtkSizeGroup *names;
GtkSizeGroup *types;
GtkSizeGroup *values;
GtkSizeGroup *origins;
GtkWidget *list;
GtkFilter *filter;
GtkColumnViewColumn *name;
GtkColumnViewColumn *type;
GtkColumnViewColumn *origin;
};
G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorPropList, gtk_inspector_prop_list, GTK_TYPE_BOX)
@@ -94,103 +84,66 @@ show_search_entry (GtkInspectorPropList *pl)
pl->priv->search_entry);
}
static void
apply_sort (GtkInspectorPropList *pl,
SortColumn column,
GtkSortType direction)
static char *
holder_prop (gpointer item)
{
const char *icon_name;
GParamSpec *prop = prop_holder_get_pspec (PROP_HOLDER (item));
icon_name = direction == GTK_SORT_ASCENDING ? "pan-down-symbolic"
: "pan-up-symbolic";
if (column == COLUMN_NAME)
{
gtk_image_clear (GTK_IMAGE (pl->priv->origin_sort_indicator));
gtk_image_set_from_icon_name (GTK_IMAGE (pl->priv->name_sort_indicator),
icon_name);
}
else
{
gtk_image_clear (GTK_IMAGE (pl->priv->name_sort_indicator));
gtk_image_set_from_icon_name (GTK_IMAGE (pl->priv->origin_sort_indicator),
icon_name);
}
pl->priv->sort_column = column;
pl->priv->sort_direction = direction;
gtk_list_box_invalidate_sort (GTK_LIST_BOX (pl->priv->list2));
return g_strdup (prop->name);
}
static void
sort_changed (GtkGestureClick *gesture,
int n_press,
double x,
double y,
GtkInspectorPropList *pl)
static char *
holder_type (gpointer item)
{
SortColumn column;
GtkSortType direction;
GtkWidget *widget;
GParamSpec *prop = prop_holder_get_pspec (PROP_HOLDER (item));
widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
if (widget == pl->priv->name_heading)
column = COLUMN_NAME;
else
column = COLUMN_ORIGIN;
if (pl->priv->sort_column == column &&
pl->priv->sort_direction == GTK_SORT_ASCENDING)
direction = GTK_SORT_DESCENDING;
else
direction = GTK_SORT_ASCENDING;
apply_sort (pl, column, direction);
return g_strdup (g_type_name (prop->value_type));
}
static const char *
row_get_column (GtkListBoxRow *row,
SortColumn column)
static char *
holder_origin (gpointer item)
{
GParamSpec *prop = g_object_get_data (G_OBJECT (row), "pspec");
GParamSpec *prop = prop_holder_get_pspec (PROP_HOLDER (item));
if (column == COLUMN_NAME)
return prop->name;
else
return g_type_name (prop->owner_type);
}
static int
sort_func (GtkListBoxRow *row1,
GtkListBoxRow *row2,
gpointer user_data)
{
GtkInspectorPropList *pl = user_data;
const char *s1 = row_get_column (row1, pl->priv->sort_column);
const char *s2 = row_get_column (row2, pl->priv->sort_column);
int ret = strcmp (s1, s2);
return pl->priv->sort_direction == GTK_SORT_ASCENDING ? ret : -ret;
}
static gboolean
filter_func (GtkListBoxRow *row,
gpointer data)
{
GtkInspectorPropList *pl = data;
GParamSpec *pspec = (GParamSpec *)g_object_get_data (G_OBJECT (row), "pspec");
const char *text = gtk_editable_get_text (GTK_EDITABLE (pl->priv->search_entry));
return g_str_has_prefix (pspec->name, text);
return g_strdup (g_type_name (prop->owner_type));
}
static void
gtk_inspector_prop_list_init (GtkInspectorPropList *pl)
{
GtkSorter *sorter;
pl->priv = gtk_inspector_prop_list_get_instance_private (pl);
gtk_widget_init_template (GTK_WIDGET (pl));
apply_sort (pl, COLUMN_NAME, GTK_SORT_ASCENDING);
pl->priv->filter = gtk_string_filter_new ();
gtk_string_filter_set_match_mode (GTK_STRING_FILTER (pl->priv->filter), GTK_STRING_FILTER_MATCH_MODE_PREFIX);
sorter = gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
0, NULL,
(GCallback)holder_prop,
NULL, NULL));
gtk_string_filter_set_expression (GTK_STRING_FILTER (pl->priv->filter),
gtk_string_sorter_get_expression (GTK_STRING_SORTER (sorter)));
gtk_column_view_column_set_sorter (pl->priv->name, sorter);
g_object_unref (sorter);
sorter = gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
0, NULL,
(GCallback)holder_type,
NULL, NULL));
gtk_column_view_column_set_sorter (pl->priv->type, sorter);
g_object_unref (sorter);
sorter = gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
0, NULL,
(GCallback)holder_origin,
NULL, NULL));
gtk_column_view_column_set_sorter (pl->priv->origin, sorter);
g_object_unref (sorter);
}
static void
@@ -253,6 +206,7 @@ show_object (GtkInspectorPropEditor *editor,
gtk_inspector_object_tree_activate_object (pl->priv->object_tree, object);
}
static void cleanup_object (GtkInspectorPropList *pl);
static void
@@ -265,6 +219,16 @@ finalize (GObject *object)
G_OBJECT_CLASS (gtk_inspector_prop_list_parent_class)->finalize (object);
}
static void
update_filter (GtkInspectorPropList *pl,
GtkSearchEntry *entry)
{
const char *text;
text = gtk_editable_get_text (GTK_EDITABLE (entry));
gtk_string_filter_set_search (GTK_STRING_FILTER (pl->priv->filter), text);
}
static void
constructed (GObject *object)
{
@@ -278,10 +242,7 @@ constructed (GObject *object)
g_signal_connect_swapped (pl->priv->search_entry, "search-started",
G_CALLBACK (show_search_entry), pl);
g_signal_connect_swapped (pl->priv->search_entry, "search-changed",
G_CALLBACK (gtk_list_box_invalidate_filter), pl->priv->list2);
gtk_list_box_set_filter_func (GTK_LIST_BOX (pl->priv->list2), filter_func, pl, NULL);
gtk_list_box_set_sort_func (GTK_LIST_BOX (pl->priv->list2), sort_func, pl, NULL);
G_CALLBACK (update_filter), pl);
}
static void
@@ -298,7 +259,7 @@ update_key_capture (GtkInspectorPropList *pl)
focus = gtk_root_get_focus (GTK_ROOT (toplevel));
if (GTK_IS_EDITABLE (focus) &&
gtk_widget_is_ancestor (focus, pl->priv->list2))
gtk_widget_is_ancestor (focus, pl->priv->list))
capture_widget = NULL;
else
capture_widget = toplevel;
@@ -344,6 +305,120 @@ unroot (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_inspector_prop_list_parent_class)->unroot (widget);
}
static void
setup_name_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *label;
label = gtk_label_new (NULL);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_list_item_set_child (list_item, label);
gtk_style_context_add_class (gtk_widget_get_style_context (label), "cell");
}
static void
bind_name_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
GObject *item;
GtkWidget *label;
item = gtk_list_item_get_item (list_item);
label = gtk_list_item_get_child (list_item);
gtk_label_set_label (GTK_LABEL (label), prop_holder_get_name (PROP_HOLDER (item)));
}
static void
setup_type_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *label;
label = gtk_label_new (NULL);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_list_item_set_child (list_item, label);
gtk_style_context_add_class (gtk_widget_get_style_context (label), "cell");
}
static void
bind_type_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
GObject *item;
GtkWidget *label;
GParamSpec *prop;
const char *type;
item = gtk_list_item_get_item (list_item);
label = gtk_list_item_get_child (list_item);
prop = prop_holder_get_pspec (PROP_HOLDER (item));
type = g_type_name (G_PARAM_SPEC_VALUE_TYPE (prop));
gtk_label_set_label (GTK_LABEL (label), type);
}
static void
setup_origin_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *label;
label = gtk_label_new (NULL);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_list_item_set_child (list_item, label);
gtk_style_context_add_class (gtk_widget_get_style_context (label), "cell");
}
static void
bind_origin_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item)
{
GObject *item;
GtkWidget *label;
GParamSpec *prop;
const char *origin;
item = gtk_list_item_get_item (list_item);
label = gtk_list_item_get_child (list_item);
prop = prop_holder_get_pspec (PROP_HOLDER (item));
origin = g_type_name (prop->owner_type);
gtk_label_set_label (GTK_LABEL (label), origin);
}
static void
bind_value_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item,
gpointer data)
{
GObject *item;
GtkWidget *widget;
GObject *object;
const char *name;
item = gtk_list_item_get_item (list_item);
object = prop_holder_get_object (PROP_HOLDER (item));
name = prop_holder_get_name (PROP_HOLDER (item));
widget = gtk_inspector_prop_editor_new (object, name, NULL);
g_signal_connect (widget, "show-object", G_CALLBACK (show_object), data);
gtk_list_item_set_child (list_item, widget);
gtk_style_context_add_class (gtk_widget_get_style_context (widget), "cell");
}
static void
unbind_value_cb (GtkSignalListItemFactory *factory,
GtkListItem *list_item,
gpointer data)
{
gtk_list_item_set_child (list_item, NULL);
}
static void
gtk_inspector_prop_list_class_init (GtkInspectorPropListClass *klass)
{
@@ -369,16 +444,18 @@ gtk_inspector_prop_list_class_init (GtkInspectorPropListClass *klass)
GTK_TYPE_WIDGET, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/prop-list.ui");
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, list2);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, names);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, types);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, values);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, origins);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, name_heading);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, name_sort_indicator);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, origin_heading);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, origin_sort_indicator);
gtk_widget_class_bind_template_callback (widget_class, sort_changed);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, list);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, name);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, type);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, origin);
gtk_widget_class_bind_template_callback (widget_class, setup_name_cb);
gtk_widget_class_bind_template_callback (widget_class, bind_name_cb);
gtk_widget_class_bind_template_callback (widget_class, setup_type_cb);
gtk_widget_class_bind_template_callback (widget_class, bind_type_cb);
gtk_widget_class_bind_template_callback (widget_class, setup_origin_cb);
gtk_widget_class_bind_template_callback (widget_class, bind_origin_cb);
gtk_widget_class_bind_template_callback (widget_class, bind_value_cb);
gtk_widget_class_bind_template_callback (widget_class, unbind_value_cb);
}
/* Like g_strdup_value_contents, but keeps the type name separate */
@@ -483,88 +560,6 @@ strdup_value_contents (const GValue *value,
}
}
static GtkWidget *
gtk_inspector_prop_list_create_row (GtkInspectorPropList *pl,
GParamSpec *prop)
{
GValue gvalue = {0};
gchar *value;
gchar *type;
gchar *attribute = NULL;
gboolean writable;
GtkWidget *row;
GtkWidget *box;
GtkWidget *label;
GtkWidget *widget;
g_value_init (&gvalue, prop->value_type);
g_object_get_property (pl->priv->object, prop->name, &gvalue);
strdup_value_contents (&gvalue, &value, &type);
if (GTK_IS_CELL_RENDERER (pl->priv->object))
{
gpointer *layout;
GtkCellArea *area;
gint column = -1;
area = NULL;
layout = g_object_get_data (pl->priv->object, "gtk-inspector-cell-layout");
if (layout)
area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (layout));
if (area)
column = gtk_cell_area_attribute_get_column (area,
GTK_CELL_RENDERER (pl->priv->object),
prop->name);
if (column != -1)
attribute = g_strdup_printf ("%d", column);
}
writable = ((prop->flags & G_PARAM_WRITABLE) != 0) &&
((prop->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
row = gtk_list_box_row_new ();
gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
g_object_set_data (G_OBJECT (row), "pspec", prop);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box);
label = gtk_label_new (prop->name);
gtk_widget_add_css_class (label, "cell");
gtk_widget_set_sensitive (label, writable);
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_size_group_add_widget (pl->priv->names, label);
gtk_box_append (GTK_BOX (box), label);
label = gtk_label_new (type ? type : "");
gtk_widget_add_css_class (label, "cell");
gtk_widget_set_sensitive (label, writable);
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_size_group_add_widget (pl->priv->types, label);
gtk_box_append (GTK_BOX (box), label);
label = gtk_label_new (g_type_name (prop->owner_type));
gtk_widget_add_css_class (label, "cell");
gtk_widget_set_sensitive (label, writable);
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_size_group_add_widget (pl->priv->origins, label);
gtk_box_append (GTK_BOX (box), label);
widget = gtk_inspector_prop_editor_new (pl->priv->object, prop->name, pl->priv->values);
gtk_widget_add_css_class (widget, "cell");
gtk_box_append (GTK_BOX (box), widget);
g_signal_connect (widget, "show-object", G_CALLBACK (show_object), pl);
g_free (value);
g_free (type);
g_free (attribute);
g_value_unset (&gvalue);
return row;
}
static void
cleanup_object (GtkInspectorPropList *pl)
{
@@ -583,7 +578,10 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
GParamSpec **props;
guint num_properties;
guint i;
GtkWidget *w;
GListStore *store;
GListModel *list;
GListModel *filtered;
GtkSortListModel *sorted;
if (!object)
return FALSE;
@@ -600,19 +598,19 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
pl->priv->object = object;
while ((w = gtk_widget_get_first_child (pl->priv->list2)))
gtk_list_box_remove (GTK_LIST_BOX (pl->priv->list2), w);
store = g_list_store_new (PROP_TYPE_HOLDER);
for (i = 0; i < num_properties; i++)
{
GParamSpec *prop = props[i];
GtkWidget *row;
PropHolder *holder;
if (! (prop->flags & G_PARAM_READABLE))
continue;
row = gtk_inspector_prop_list_create_row (pl, prop);
gtk_list_box_insert (GTK_LIST_BOX (pl->priv->list2), row, -1);
holder = prop_holder_new (object, prop);
g_list_store_append (store, holder);
g_object_unref (holder);
}
g_free (props);
@@ -620,8 +618,21 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
if (GTK_IS_WIDGET (object))
g_signal_connect_object (object, "destroy", G_CALLBACK (cleanup_object), pl, G_CONNECT_SWAPPED);
filtered = G_LIST_MODEL (gtk_filter_list_model_new (G_LIST_MODEL (store), pl->priv->filter));
sorted = gtk_sort_list_model_new (filtered, NULL);
list = G_LIST_MODEL (gtk_no_selection_new (G_LIST_MODEL (sorted)));
gtk_column_view_set_model (GTK_COLUMN_VIEW (pl->priv->list), list);
gtk_sort_list_model_set_sorter (sorted, gtk_column_view_get_sorter (GTK_COLUMN_VIEW (pl->priv->list)));
gtk_column_view_sort_by_column (GTK_COLUMN_VIEW (pl->priv->list), pl->priv->name, GTK_SORT_ASCENDING);
gtk_widget_show (GTK_WIDGET (pl));
g_object_unref (list);
g_object_unref (sorted);
g_object_unref (filtered);
g_object_unref (store);
return TRUE;
}
+46 -96
View File
@@ -8,87 +8,61 @@
<style>
<class name="view"/>
</style>
<child>
<object class="GtkBox">
<style>
<class name="header"/>
</style>
<child>
<object class="GtkBox" id="name_heading">
<property name="hexpand">0</property>
<child>
<object class="GtkGestureClick">
<signal name="pressed" handler="sort_changed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Name</property>
<property name="xalign">0</property>
<property name="hexpand">1</property>
</object>
</child>
<child>
<object class="GtkImage" id="name_sort_indicator">
<style>
<class name="sort_indicator"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel" id="type_heading">
<property name="label">Type</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkBox" id="origin_heading">
<property name="hexpand">0</property>
<child>
<object class="GtkGestureClick">
<signal name="pressed" handler="sort_changed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Defined at</property>
<property name="xalign">0</property>
<property name="hexpand">1</property>
</object>
</child>
<child>
<object class="GtkImage" id="origin_sort_indicator">
<style>
<class name="sort_indicator"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel" id="value_heading">
<property name="label">Value</property>
<property name="xalign">0</property>
<property name="hexpand">0</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="hexpand">1</property>
<property name="vexpand">1</property>
<property name="hscrollbar-policy">never</property>
<child>
<object class="GtkListBox" id="list2">
<object class="GtkColumnView" id="list">
<style>
<class name="list"/>
</style>
<property name="selection-mode">none</property>
<child>
<object class="GtkColumnViewColumn" id="name">
<property name="title" translatable="yes">Name</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_name_cb"/>
<signal name="bind" handler="bind_name_cb"/>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="type">
<property name="title" translatable="yes">Type</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_type_cb"/>
<signal name="bind" handler="bind_type_cb"/>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="origin">
<property name="title" translatable="yes">Defined At</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_origin_cb"/>
<signal name="bind" handler="bind_origin_cb"/>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn">
<property name="title" translatable="yes">Value</property>
<property name="expand">1</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="bind" handler="bind_value_cb"/>
<signal name="unbind" handler="unbind_value_cb"/>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
@@ -96,28 +70,4 @@
</object>
</child>
</template>
<object class="GtkSizeGroup" id="names">
<property name="mode">horizontal</property>
<widgets>
<widget name="name_heading"/>
</widgets>
</object>
<object class="GtkSizeGroup" id="types">
<property name="mode">horizontal</property>
<widgets>
<widget name="type_heading"/>
</widgets>
</object>
<object class="GtkSizeGroup" id="origins">
<property name="mode">horizontal</property>
<widgets>
<widget name="origin_heading"/>
</widgets>
</object>
<object class="GtkSizeGroup" id="values">
<property name="mode">horizontal</property>
<widgets>
<widget name="value_heading"/>
</widgets>
</object>
</interface>
+106
View File
@@ -0,0 +1,106 @@
#include "resource-holder.h"
struct _ResourceHolder {
GObject instance;
char *name;
char *path;
int count;
gsize size;
GListModel *children;
ResourceHolder *parent;
};
G_DEFINE_TYPE (ResourceHolder, resource_holder, G_TYPE_OBJECT)
static void
resource_holder_init (ResourceHolder *holder)
{
}
static void
resource_holder_finalize (GObject *object)
{
ResourceHolder *holder = RESOURCE_HOLDER (object);
g_free (holder->name);
g_free (holder->path);
g_clear_object (&holder->children);
G_OBJECT_CLASS (resource_holder_parent_class)->finalize (object);
}
static void
resource_holder_class_init (ResourceHolderClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = resource_holder_finalize;
}
ResourceHolder *
resource_holder_new (const char *name,
const char *path,
int count,
gsize size,
GListModel *children)
{
ResourceHolder *holder;
holder = g_object_new (RESOURCE_TYPE_HOLDER, NULL);
holder->name = g_strdup (name);
holder->path = g_strdup (path);
holder->count = count;
holder->size = size;
g_set_object (&holder->children, children);
if (children)
{
int i;
for (i = 0; i < g_list_model_get_n_items (children); i++)
{
ResourceHolder *child = g_list_model_get_item (children, i);
child->parent = holder;
g_object_unref (child);
}
}
return holder;
}
const char *
resource_holder_get_name (ResourceHolder *holder)
{
return holder->name;
}
const char *
resource_holder_get_path (ResourceHolder *holder)
{
return holder->path;
}
int
resource_holder_get_count (ResourceHolder *holder)
{
return holder->count;
}
gsize
resource_holder_get_size (ResourceHolder *holder)
{
return holder->size;
}
GListModel *
resource_holder_get_children (ResourceHolder *holder)
{
return holder->children;
}
ResourceHolder *
resource_holder_get_parent (ResourceHolder *holder)
{
return holder->parent;
}
+24
View File
@@ -0,0 +1,24 @@
#ifndef __RESOURCE_HOLDER_H__
#define __RESOURCE_HOLDER_H__
#include <gtk/gtk.h>
#define RESOURCE_TYPE_HOLDER (resource_holder_get_type ())
G_DECLARE_FINAL_TYPE (ResourceHolder, resource_holder, RESOURCE, HOLDER, GObject)
ResourceHolder * resource_holder_new (const char *name,
const char *path,
int count,
gsize size,
GListModel *children);
const char *resource_holder_get_name (ResourceHolder *holder);
const char *resource_holder_get_path (ResourceHolder *holder);
int resource_holder_get_count (ResourceHolder *holder);
gsize resource_holder_get_size (ResourceHolder *holder);
GListModel *resource_holder_get_children (ResourceHolder *holder);
ResourceHolder *resource_holder_get_parent (ResourceHolder *holder);
#endif /* __RESOURCE_HOLDER_H__ */
File diff suppressed because it is too large Load Diff
+29 -37
View File
@@ -30,6 +30,9 @@
<object class="GtkSearchEntry" id="search_entry">
<property name="max-width-chars">40</property>
<signal name="search-changed" handler="on_search_changed"/>
<signal name="next-match" handler="next_match"/>
<signal name="previous-match" handler="previous_match"/>
<signal name="stop-search" handler="stop_search"/>
</object>
</child>
<child>
@@ -54,56 +57,45 @@
<property name="vexpand">1</property>
<property name="hscrollbar-policy">never</property>
<child>
<object class="GtkTreeView" id="tree">
<property name="model">model</property>
<property name="enable-search">0</property>
<property name="enable-grid-lines">vertical</property>
<signal name="row-activated" handler="row_activated"/>
<child internal-child="selection">
<object class="GtkTreeSelection">
<signal name="changed" handler="on_selection_changed"/>
</object>
</child>
<object class="GtkColumnView" id="list">
<signal name="activate" handler="on_row_activated"/>
<child>
<object class="GtkTreeViewColumn" id="path_column">
<object class="GtkColumnViewColumn" id="path">
<property name="title" translatable="yes">Path</property>
<property name="resizable">1</property>
<property name="sort-column-id">0</property>
<child>
<object class="GtkCellRendererText">
<property name="scale">0.8</property>
<property name="ellipsize">end</property>
<property name="width-chars">10</property>
<property name="max-width-chars">5</property>
<property name="expand">1</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_name_cb"/>
<signal name="bind" handler="bind_name_cb"/>
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="count_column">
<object class="GtkColumnViewColumn" id="count">
<property name="title" translatable="yes">Count</property>
<property name="resizable">1</property>
<property name="sort-column-id">1</property>
<child>
<object class="GtkCellRendererText" id="count_renderer">
<property name="scale">0.8</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_count_cb"/>
<signal name="bind" handler="bind_count_cb"/>
</object>
</child>
</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="size_column">
<object class="GtkColumnViewColumn" id="size">
<property name="title" translatable="yes">Size</property>
<property name="resizable">1</property>
<property name="sort-column-id">2</property>
<child>
<object class="GtkCellRendererText" id="size_renderer">
<property name="scale">0.8</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_size_cb"/>
<signal name="bind" handler="bind_size_cb"/>
</object>
</child>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="filler">
<property name="expand">1</property>
</object>
</child>
</object>
-2
View File
@@ -45,8 +45,6 @@
#include "gtkeditable.h"
#include "gtkentry.h"
#include "fallback-c89.c"
#ifdef GDK_WINDOWING_X11
#include "x11/gdkx.h"
#endif
+3 -3
View File
@@ -14,7 +14,6 @@ gtk_cargs = [
# List of sources that do not contain public API, and should not be
# introspected
gtk_private_sources = files([
'fallback-c89.c',
'fnmatch.c',
'tools/gdkpixbufutils.c',
'gsettings-mapping.c',
@@ -135,6 +134,7 @@ gtk_private_sources = files([
'gtkscaler.c',
'gtksearchengine.c',
'gtksearchenginemodel.c',
'gtkset.c',
'gtksizerequestcache.c',
'gtkstyleanimation.c',
'gtkstylecascade.c',
@@ -206,7 +206,6 @@ gtk_public_sources = files([
'gtkconstraintguide.c',
'gtkconstraintlayout.c',
'gtkconstraint.c',
'gtkcoverflow.c',
'gtkcssprovider.c',
'gtkcustomfilter.c',
'gtkcustomlayout.c',
@@ -303,6 +302,7 @@ gtk_public_sources = files([
'gtkmodules.c',
'gtkmountoperation.c',
'gtkmultifilter.c',
'gtkmultiselection.c',
'gtkmultisorter.c',
'gtknativedialog.c',
'gtknomediafile.c',
@@ -486,7 +486,6 @@ gtk_public_headers = files([
'gtkconstraintguide.h',
'gtkconstraintlayout.h',
'gtkconstraint.h',
'gtkcoverflow.h',
'gtkcssprovider.h',
'gtkcustomfilter.h',
'gtkcustomlayout.h',
@@ -577,6 +576,7 @@ gtk_public_headers = files([
'gtkmessagedialog.h',
'gtkmountoperation.h',
'gtkmultifilter.h',
'gtkmultiselection.h',
'gtkmultisorter.h',
'gtknative.h',
'gtknativedialog.h',
-9
View File
@@ -191,16 +191,11 @@ check_functions = [
'getresuid',
'lstat',
'mmap',
'nearbyint',
'posix_fallocate',
'_lock_file',
'flockfile',
'mkstemp',
'mallinfo',
'round',
'rint',
'log2',
'exp2',
'sincos',
'sincosf',
]
@@ -211,10 +206,6 @@ foreach func : check_functions
endif
endforeach
cdata.set('HAVE_DECL_ISINF', cc.has_header_symbol('math.h', 'isinf'))
cdata.set('HAVE_DECL_ISNAN', cc.has_header_symbol('math.h', 'isnan'))
cdata.set('HAVE_DECL_ISNANF', cc.has_header_symbol('math.h', 'isnanf'))
# Disable deprecation checks for all libraries we depend on on stable branches.
# This is so newer versions of those libraries don't cause more warnings with
# a stable GTK version.
+183 -176
View File
@@ -9,8 +9,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gtk+ 2.4.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-05-28 09:12+0000\n"
"PO-Revision-Date: 2020-05-28 13:03+0300\n"
"POT-Creation-Date: 2020-05-31 05:35+0000\n"
"PO-Revision-Date: 2020-05-31 09:11+0300\n"
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
"Language-Team: Ukrainian <kde-i18n-uk@kde.org>\n"
"Language: uk\n"
@@ -460,7 +460,7 @@ msgid "Whether to wrap the license text."
msgstr "Чи переносити рядку у тексті ліцензії."
#: gtk/gtkaccellabel.c:135 gtk/gtkbutton.c:225 gtk/gtkexpander.c:320
#: gtk/gtkframe.c:168 gtk/gtklabel.c:746 gtk/gtkmenubutton.c:422
#: gtk/gtkframe.c:168 gtk/gtklabel.c:745 gtk/gtkmenubutton.c:422
msgid "Label"
msgstr "Позначка"
@@ -469,12 +469,12 @@ msgid "The text displayed next to the accelerator"
msgstr "Текст, який буде показано поряд із акселератором"
#: gtk/gtkaccellabel.c:142 gtk/gtkbutton.c:232 gtk/gtkexpander.c:328
#: gtk/gtklabel.c:767 gtk/gtkmenubutton.c:429 gtk/gtkstack.c:431
#: gtk/gtklabel.c:766 gtk/gtkmenubutton.c:429 gtk/gtkstack.c:431
msgid "Use underline"
msgstr "Використовувати підкреслення"
#: gtk/gtkaccellabel.c:143 gtk/gtkbutton.c:233 gtk/gtkexpander.c:329
#: gtk/gtklabel.c:768 gtk/gtkmenubutton.c:430
#: gtk/gtklabel.c:767 gtk/gtkmenubutton.c:430
msgid ""
"If set, an underline in the text indicates the next character should be used "
"for the mnemonic accelerator key"
@@ -744,7 +744,7 @@ msgstr ""
#: gtk/gtkdragicon.c:372 gtk/gtkexpander.c:366 gtk/gtkflowbox.c:513
#: gtk/gtkframe.c:190 gtk/gtklistbox.c:3455 gtk/gtknotebook.c:570
#: gtk/gtkoverlay.c:319 gtk/gtkpopover.c:1665 gtk/gtkrevealer.c:354
#: gtk/gtkscrolledwindow.c:759 gtk/gtksearchbar.c:316 gtk/gtkstack.c:381
#: gtk/gtkscrolledwindow.c:758 gtk/gtksearchbar.c:316 gtk/gtkstack.c:381
#: gtk/gtkviewport.c:390 gtk/gtkwindow.c:946 gtk/gtkwindowhandle.c:546
msgid "Child"
msgstr "Вкладений елемент"
@@ -752,7 +752,7 @@ msgstr "Вкладений елемент"
#: gtk/gtkaspectframe.c:167 gtk/gtkbutton.c:254 gtk/gtkexpander.c:367
#: gtk/gtkflowbox.c:514 gtk/gtkframe.c:191 gtk/gtklistbox.c:3456
#: gtk/gtkoverlay.c:320 gtk/gtkpopover.c:1666 gtk/gtkrevealer.c:355
#: gtk/gtkscrolledwindow.c:760 gtk/gtksearchbar.c:317 gtk/gtkviewport.c:391
#: gtk/gtkscrolledwindow.c:759 gtk/gtksearchbar.c:317 gtk/gtkviewport.c:391
#: gtk/gtkwindow.c:947 gtk/gtkwindowhandle.c:547
msgid "The child widget"
msgstr "Дочірній віджет"
@@ -806,7 +806,7 @@ msgid "The pages of the assistant."
msgstr "Сторінки помічника."
#: gtk/gtkbox.c:259 gtk/gtkboxlayout.c:730 gtk/gtkcellareabox.c:317
#: gtk/gtkiconview.c:473 gtk/gtktreeviewcolumn.c:304
#: gtk/gtkiconview.c:472 gtk/gtktreeviewcolumn.c:304
msgid "Spacing"
msgstr "Інтервал"
@@ -840,27 +840,27 @@ msgstr "Розподілити рівномірно за простором"
msgid "Spacing between widgets"
msgstr "Інтервал між віджетами"
#: gtk/gtkbuilder.c:311
#: gtk/gtkbuilder.c:313
msgid "Translation Domain"
msgstr "Домен перекладу"
#: gtk/gtkbuilder.c:312
#: gtk/gtkbuilder.c:314
msgid "The translation domain used by gettext"
msgstr "Домен перекладу, що використовується gettext"
#: gtk/gtkbuilder.c:323
#: gtk/gtkbuilder.c:325
msgid "Current object"
msgstr "Поточний об'єкт"
#: gtk/gtkbuilder.c:324
#: gtk/gtkbuilder.c:326
msgid "The object the builder is evaluating for"
msgstr "Об'єкт для оцінки збиральником"
#: gtk/gtkbuilder.c:335 gtk/gtkshortcutcontroller.c:550
#: gtk/gtkbuilder.c:337 gtk/gtkshortcutcontroller.c:551
msgid "Scope"
msgstr "Область"
#: gtk/gtkbuilder.c:336
#: gtk/gtkbuilder.c:338
msgid "The scope the builder is operating in"
msgstr "Область, у якій працює будівник"
@@ -871,7 +871,7 @@ msgid ""
msgstr "Текст віджета позначки в кнопці, якщо кнопка містить віджет позначки"
#: gtk/gtkbutton.c:239 gtk/gtkcombobox.c:671 gtk/gtkentry.c:469
#: gtk/gtkscrolledwindow.c:641
#: gtk/gtkscrolledwindow.c:640
msgid "Has Frame"
msgstr "Має рамку"
@@ -1187,10 +1187,10 @@ msgstr "Встановлення тла комірки"
msgid "Whether the cell background color is set"
msgstr "Чи встановлено колір тла комірки"
#: gtk/gtkcellrenderercombo.c:136 gtk/gtkfilterlistmodel.c:416
#: gtk/gtkcellrenderercombo.c:136 gtk/gtkfilterlistmodel.c:683
#: gtk/gtkflattenlistmodel.c:438 gtk/gtkmaplistmodel.c:404
#: gtk/gtkshortcutcontroller.c:538 gtk/gtkslicelistmodel.c:287
#: gtk/gtksortlistmodel.c:338 gtk/gtktreelistmodel.c:716
#: gtk/gtkshortcutcontroller.c:539 gtk/gtkslicelistmodel.c:287
#: gtk/gtksortlistmodel.c:394 gtk/gtktreelistmodel.c:716
msgid "Model"
msgstr "Модель"
@@ -1377,7 +1377,7 @@ msgstr "Розмітка"
msgid "Marked up text to render"
msgstr "Розмітка тексту до візуалізації"
#: gtk/gtkcellrenderertext.c:269 gtk/gtkentry.c:890 gtk/gtklabel.c:753
#: gtk/gtkcellrenderertext.c:269 gtk/gtkentry.c:890 gtk/gtklabel.c:752
#: gtk/gtktext.c:894
msgid "Attributes"
msgstr "Атрибути"
@@ -1536,7 +1536,7 @@ msgstr ""
"під час відтворення тексту. Якщо ви не розумієте цей параметр, то, напевно, "
"він вам не потрібний."
#: gtk/gtkcellrenderertext.c:446 gtk/gtklabel.c:873 gtk/gtkprogressbar.c:246
#: gtk/gtkcellrenderertext.c:446 gtk/gtklabel.c:872 gtk/gtkprogressbar.c:246
msgid "Ellipsize"
msgstr "Овал"
@@ -1549,15 +1549,15 @@ msgstr ""
"для показу всього рядка"
#: gtk/gtkcellrenderertext.c:463 gtk/gtkfilechooserbutton.c:524
#: gtk/gtklabel.c:891
#: gtk/gtklabel.c:890
msgid "Width In Characters"
msgstr "Ширина у символах"
#: gtk/gtkcellrenderertext.c:464 gtk/gtklabel.c:892
#: gtk/gtkcellrenderertext.c:464 gtk/gtklabel.c:891
msgid "The desired width of the label, in characters"
msgstr "Бажана ширина, у символах"
#: gtk/gtkcellrenderertext.c:483 gtk/gtklabel.c:925
#: gtk/gtkcellrenderertext.c:483 gtk/gtklabel.c:924
msgid "Maximum Width In Characters"
msgstr "Максимальна ширина в символах"
@@ -1772,12 +1772,12 @@ msgstr "Модель CellView"
msgid "The model for cell view"
msgstr "Модель для перегляду комірки"
#: gtk/gtkcellview.c:215 gtk/gtkentrycompletion.c:439 gtk/gtkiconview.c:584
#: gtk/gtkcellview.c:215 gtk/gtkentrycompletion.c:439 gtk/gtkiconview.c:583
#: gtk/gtktreepopover.c:213 gtk/gtktreeviewcolumn.c:423
msgid "Cell Area"
msgstr "Ділянка комірки"
#: gtk/gtkcellview.c:216 gtk/gtkentrycompletion.c:440 gtk/gtkiconview.c:585
#: gtk/gtkcellview.c:216 gtk/gtkentrycompletion.c:440 gtk/gtkiconview.c:584
#: gtk/gtktreepopover.c:214 gtk/gtktreeviewcolumn.c:424
msgid "The GtkCellArea used to layout cells"
msgstr "GtkCellArea для компонування комірок"
@@ -1891,7 +1891,7 @@ msgstr "Колір RGBA"
msgid "Color as RGBA"
msgstr "Колір як RGBA"
#: gtk/gtkcolorswatch.c:485 gtk/gtklabel.c:836 gtk/gtklistbox.c:3448
#: gtk/gtkcolorswatch.c:485 gtk/gtklabel.c:835 gtk/gtklistbox.c:3448
msgid "Selectable"
msgstr "Вибирається"
@@ -2283,11 +2283,11 @@ msgstr "Максимальна ширина у символах"
msgid "The desired maximum width of the entry, in characters"
msgstr "Бажана максимальна ширина поля введення, у символах"
#: gtk/gtkeditable.c:423 gtk/gtklabel.c:790
#: gtk/gtkeditable.c:423 gtk/gtklabel.c:789
msgid "X align"
msgstr "Вирівнювання за X"
#: gtk/gtkeditable.c:424 gtk/gtklabel.c:791
#: gtk/gtkeditable.c:424 gtk/gtklabel.c:790
msgid ""
"The horizontal alignment, from 0 (left) to 1 (right). Reversed for RTL "
"layouts."
@@ -2603,7 +2603,7 @@ msgstr "Піктограма емодзі"
msgid "Whether to show an icon for Emoji"
msgstr "Чи слід показувати піктограму емодзі"
#: gtk/gtkentry.c:928 gtk/gtklabel.c:955 gtk/gtkpasswordentry.c:418
#: gtk/gtkentry.c:928 gtk/gtklabel.c:954 gtk/gtkpasswordentry.c:418
#: gtk/gtktext.c:940 gtk/gtktextview.c:1078
msgid "Extra menu"
msgstr "Додаткове меню"
@@ -2636,7 +2636,7 @@ msgstr "Мінімальна довжина ключа"
msgid "Minimum length of the search key in order to look up matches"
msgstr "Мінімальна довжина ключа, при пошуку відповідностей"
#: gtk/gtkentrycompletion.c:354 gtk/gtkiconview.c:402
#: gtk/gtkentrycompletion.c:354 gtk/gtkiconview.c:401
msgid "Text column"
msgstr "Текстовий стовпчик"
@@ -2741,11 +2741,11 @@ msgstr "Чи розширювач був відкритий для показу
msgid "Text of the expanders label"
msgstr "Текст мітки розгортання"
#: gtk/gtkexpander.c:336 gtk/gtklabel.c:760 gtk/gtkmodelbutton.c:1149
#: gtk/gtkexpander.c:336 gtk/gtklabel.c:759 gtk/gtkmodelbutton.c:1149
msgid "Use markup"
msgstr "Використовувати розмітку"
#: gtk/gtkexpander.c:337 gtk/gtklabel.c:761
#: gtk/gtkexpander.c:337 gtk/gtklabel.c:760
msgid "The text of the label includes XML markup. See pango_parse_markup()"
msgstr "Текст позначки включає розмітку XML. Див. pango_parse_markup()"
@@ -2797,7 +2797,7 @@ msgstr "Дія"
msgid "The type of operation that the file selector is performing"
msgstr "Тип операції, яку виконує діалог вибору файлів"
#: gtk/gtkfilechooser.c:160
#: gtk/gtkfilechooser.c:160 gtk/gtkfilterlistmodel.c:659
msgid "Filter"
msgstr "Фільтр"
@@ -2854,27 +2854,24 @@ msgstr "Підзаголовок"
msgid "The human-readable name for this filter"
msgstr "Зручна для читання назва цього фільтра"
#: gtk/gtkfilterlistmodel.c:392
msgid "has filter"
msgstr "має фільтр"
#: gtk/gtkfilterlistmodel.c:660
#| msgid "If a filter is set for this model"
msgid "The filter set for this model"
msgstr "Набір фільтрів для цієї моделі"
#: gtk/gtkfilterlistmodel.c:393
msgid "If a filter is set for this model"
msgstr "Чи встановлено фільтр для цієї моделі"
#: gtk/gtkfilterlistmodel.c:404 gtk/gtkflattenlistmodel.c:426
#: gtk/gtkfilterlistmodel.c:671 gtk/gtkflattenlistmodel.c:426
#: gtk/gtkmaplistmodel.c:392 gtk/gtkpropertylookuplistmodel.c:376
#: gtk/gtkslicelistmodel.c:275 gtk/gtksortlistmodel.c:326
#: gtk/gtkslicelistmodel.c:275 gtk/gtksortlistmodel.c:382
msgid "Item type"
msgstr "Тип запису"
#: gtk/gtkfilterlistmodel.c:405 gtk/gtkflattenlistmodel.c:427
#: gtk/gtkfilterlistmodel.c:672 gtk/gtkflattenlistmodel.c:427
#: gtk/gtkmaplistmodel.c:393 gtk/gtkpropertylookuplistmodel.c:377
#: gtk/gtkslicelistmodel.c:276
msgid "The type of elements of this object"
msgstr "Тип елементів цього об'єкта"
#: gtk/gtkfilterlistmodel.c:417
#: gtk/gtkfilterlistmodel.c:684
msgid "The model being filtered"
msgstr "Модель, яка фільтрується"
@@ -2890,22 +2887,22 @@ msgstr "Перетворення дочірнього об'єкта із фік
msgid "The model being flattened"
msgstr "Модель, яка сплощується"
#: gtk/gtkflowbox.c:3578 gtk/gtkiconview.c:369 gtk/gtklistbox.c:488
#: gtk/gtkflowbox.c:3578 gtk/gtkiconview.c:368 gtk/gtklistbox.c:488
#: gtk/gtktreeselection.c:140
msgid "Selection mode"
msgstr "Режим виділення"
#: gtk/gtkflowbox.c:3579 gtk/gtkiconview.c:370 gtk/gtklistbox.c:489
#: gtk/gtkflowbox.c:3579 gtk/gtkiconview.c:369 gtk/gtklistbox.c:489
msgid "The selection mode"
msgstr "Режим виділення"
#: gtk/gtkflowbox.c:3592 gtk/gtkiconview.c:598 gtk/gtklistbox.c:496
#: gtk/gtktreeview.c:1205
#: gtk/gtkflowbox.c:3592 gtk/gtkiconview.c:597 gtk/gtklistbox.c:496
#: gtk/gtktreeview.c:1203
msgid "Activate on Single Click"
msgstr "Активувати одиночним клацанням"
#: gtk/gtkflowbox.c:3593 gtk/gtkiconview.c:599 gtk/gtklistbox.c:497
#: gtk/gtktreeview.c:1206
#: gtk/gtkflowbox.c:3593 gtk/gtkiconview.c:598 gtk/gtklistbox.c:497
#: gtk/gtktreeview.c:1204
msgid "Activate row on a single click"
msgstr "Активувати рядок одиночним клацанням"
@@ -3017,11 +3014,11 @@ msgstr "Параметри шрифту у форматі рядка"
msgid "Language for which features have been selected"
msgstr "Мова, для якої були вибрані параметри"
#: gtk/gtkfontchooserwidget.c:740
#: gtk/gtkfontchooserwidget.c:664
msgid "The tweak action"
msgstr "Дія коригування"
#: gtk/gtkfontchooserwidget.c:741
#: gtk/gtkfontchooserwidget.c:665
msgid "The toggle action to switch to the tweak page"
msgstr "Увімкнути дію для перемикання сторінки коригування"
@@ -3252,7 +3249,6 @@ msgid "Icon name"
msgstr "Назва піктограми"
#: gtk/gtkicontheme.c:3588
#| msgid "The icon name choosen during lookup"
msgid "The icon name chosen during lookup"
msgstr "Назва піктограми, яку вибрано під час пошуку"
@@ -3264,112 +3260,112 @@ msgstr "Є символічною"
msgid "If the icon is symbolic"
msgstr "Чи є піктограма символічною"
#: gtk/gtkiconview.c:386
#: gtk/gtkiconview.c:385
msgid "Pixbuf column"
msgstr "Стовпчик pixbuf"
#: gtk/gtkiconview.c:387
#: gtk/gtkiconview.c:386
msgid "Model column used to retrieve the icon pixbuf from"
msgstr ""
"Модель стовпця, що використовується для отримання зображення (pixbuf) значка"
#: gtk/gtkiconview.c:403
#: gtk/gtkiconview.c:402
msgid "Model column used to retrieve the text from"
msgstr "Модель стовпчика, що використовується для отримання з неї тексту"
#: gtk/gtkiconview.c:420
#: gtk/gtkiconview.c:419
msgid "Markup column"
msgstr "Розмітка стовпчика"
#: gtk/gtkiconview.c:421
#: gtk/gtkiconview.c:420
msgid "Model column used to retrieve the text if using Pango markup"
msgstr ""
"Модель стовпчика, що використовується для тримання з неї тексту, якщо "
"використовується розмітка pango"
#: gtk/gtkiconview.c:428
#: gtk/gtkiconview.c:427
msgid "Icon View Model"
msgstr "Модель Icon View"
#: gtk/gtkiconview.c:429
#: gtk/gtkiconview.c:428
msgid "The model for the icon view"
msgstr "Модель для перегляду у вигляді значків"
#: gtk/gtkiconview.c:443
#: gtk/gtkiconview.c:442
msgid "Number of columns"
msgstr "Кількість стовпчиків"
#: gtk/gtkiconview.c:444
#: gtk/gtkiconview.c:443
msgid "Number of columns to display"
msgstr "Кількість стовпчиків, які показуються"
#: gtk/gtkiconview.c:459
#: gtk/gtkiconview.c:458
msgid "Width for each item"
msgstr "Віджет для кожного елемента"
#: gtk/gtkiconview.c:460
#: gtk/gtkiconview.c:459
msgid "The width used for each item"
msgstr "Ширина, що використовується для кожного елемента"
#: gtk/gtkiconview.c:474
#: gtk/gtkiconview.c:473
msgid "Space which is inserted between cells of an item"
msgstr "Простір, що вставляється між комірками елемента"
#: gtk/gtkiconview.c:487
#: gtk/gtkiconview.c:486
msgid "Row Spacing"
msgstr "Міжрядковий інтервал"
#: gtk/gtkiconview.c:488
#: gtk/gtkiconview.c:487
msgid "Space which is inserted between grid rows"
msgstr "Простір, що вставляється між рядками сітки"
#: gtk/gtkiconview.c:501
#: gtk/gtkiconview.c:500
msgid "Column Spacing"
msgstr "Інтервал між стовпчиками"
#: gtk/gtkiconview.c:502
#: gtk/gtkiconview.c:501
msgid "Space which is inserted between grid columns"
msgstr "Простір, що вставляється між стовпчиками сітки"
#: gtk/gtkiconview.c:515
#: gtk/gtkiconview.c:514
msgid "Margin"
msgstr "Відступ"
#: gtk/gtkiconview.c:516
#: gtk/gtkiconview.c:515
msgid "Space which is inserted at the edges of the icon view"
msgstr "Простір, що вставляється на краях перегляду піктограми"
#: gtk/gtkiconview.c:529
#: gtk/gtkiconview.c:528
msgid "Item Orientation"
msgstr "Орієнтація пункту "
#: gtk/gtkiconview.c:530
#: gtk/gtkiconview.c:529
msgid ""
"How the text and icon of each item are positioned relative to each other"
msgstr ""
"Як текст та значки кожного елемента розташовуються один відносно іншого"
#: gtk/gtkiconview.c:544 gtk/gtktreeview.c:1071 gtk/gtktreeviewcolumn.c:379
#: gtk/gtkiconview.c:543 gtk/gtktreeview.c:1069 gtk/gtktreeviewcolumn.c:379
msgid "Reorderable"
msgstr "Дозволено перестановку"
#: gtk/gtkiconview.c:545 gtk/gtktreeview.c:1072
#: gtk/gtkiconview.c:544 gtk/gtktreeview.c:1070
msgid "View is reorderable"
msgstr "Перегляд можна перегрупувати"
#: gtk/gtkiconview.c:552 gtk/gtktreeview.c:1191
#: gtk/gtkiconview.c:551 gtk/gtktreeview.c:1189
msgid "Tooltip Column"
msgstr "Стовпчик підказки"
#: gtk/gtkiconview.c:553
#: gtk/gtkiconview.c:552
msgid "The column in the model containing the tooltip texts for the items"
msgstr "Стовпчик у моделі, що містить текстову підказку для елементів"
#: gtk/gtkiconview.c:568
#: gtk/gtkiconview.c:567
msgid "Item Padding"
msgstr "Доповнення елемента"
#: gtk/gtkiconview.c:569
#: gtk/gtkiconview.c:568
msgid "Padding around icon view items"
msgstr "Доповнення навколо значків"
@@ -3449,19 +3445,19 @@ msgstr "Чи потрібно додавати стандартну кнопку
msgid "Controls whether the info bar shows its contents or not"
msgstr "Керує тим, чи буде показано вміст інформаційної панелі"
#: gtk/gtklabel.c:747
#: gtk/gtklabel.c:746
msgid "The text of the label"
msgstr "Текст позначки"
#: gtk/gtklabel.c:754
#: gtk/gtklabel.c:753
msgid "A list of style attributes to apply to the text of the label"
msgstr "Список стильових ознак для застосування до тексту позначки"
#: gtk/gtklabel.c:774 gtk/gtktexttag.c:386 gtk/gtktextview.c:877
#: gtk/gtklabel.c:773 gtk/gtktexttag.c:386 gtk/gtktextview.c:877
msgid "Justification"
msgstr "Вирівнювання"
#: gtk/gtklabel.c:775
#: gtk/gtklabel.c:774
msgid ""
"The alignment of the lines in the text of the label relative to each other. "
"This does NOT affect the alignment of the label within its allocation. See "
@@ -3471,80 +3467,80 @@ msgstr ""
"вирівнювання мітки всередині вказаного для неї місця. З цього питання див. "
"GtkLabel::xalign"
#: gtk/gtklabel.c:806
#: gtk/gtklabel.c:805
msgid "Y align"
msgstr "Вирівнювання за Y"
#: gtk/gtklabel.c:807
#: gtk/gtklabel.c:806
msgid "The vertical alignment, from 0 (top) to 1 (bottom)"
msgstr "Вертикальне вирівнювання, від 0 (вгору) до 1 (вниз)"
#: gtk/gtklabel.c:814
#: gtk/gtklabel.c:813
msgid "Line wrap"
msgstr "Перенос рядків"
#: gtk/gtklabel.c:815
#: gtk/gtklabel.c:814
msgid "If set, wrap lines if the text becomes too wide"
msgstr "Якщо встановлено, занадто довгі рядки переносяться"
#: gtk/gtklabel.c:828
#: gtk/gtklabel.c:827
msgid "Line wrap mode"
msgstr "Режим переносу рядків"
#: gtk/gtklabel.c:829
#: gtk/gtklabel.c:828
msgid "If wrap is set, controls how linewrapping is done"
msgstr ""
"Якщо встановлено перенос рядків, контролює як виконувати перенос рядків"
#: gtk/gtklabel.c:837
#: gtk/gtklabel.c:836
msgid "Whether the label text can be selected with the mouse"
msgstr "Чи може текст позначки бути виділений використовуючи мишу"
#: gtk/gtklabel.c:843
#: gtk/gtklabel.c:842
msgid "Mnemonic key"
msgstr "Мнемонічна клавіша"
#: gtk/gtklabel.c:844
#: gtk/gtklabel.c:843
msgid "The mnemonic accelerator key for this label"
msgstr "Мнемонічна клавіша-прискорювач для цієї позначки"
#: gtk/gtklabel.c:851
#: gtk/gtklabel.c:850
msgid "Mnemonic widget"
msgstr "Мнемонічний віджет"
#: gtk/gtklabel.c:852
#: gtk/gtklabel.c:851
msgid "The widget to be activated when the labels mnemonic key is pressed"
msgstr ""
"Віджет, який буде активовано під час натиснення мнемонічної клавіші позначки"
#: gtk/gtklabel.c:874
#: gtk/gtklabel.c:873
msgid ""
"The preferred place to ellipsize the string, if the label does not have "
"enough room to display the entire string"
msgstr ""
"Бажане місце для овалу рядка, якщо для показу усього рядка не вистачає місця."
#: gtk/gtklabel.c:908
#: gtk/gtklabel.c:907
msgid "Single Line Mode"
msgstr "Режим одного рядка"
#: gtk/gtklabel.c:909
#: gtk/gtklabel.c:908
msgid "Whether the label is in single line mode"
msgstr "Чи знаходиться рядок у режимі одного рядка"
#: gtk/gtklabel.c:926
#: gtk/gtklabel.c:925
msgid "The desired maximum width of the label, in characters"
msgstr "Бажана максимальна ширина ярлика, у символах"
#: gtk/gtklabel.c:941
#: gtk/gtklabel.c:940
msgid "Number of lines"
msgstr "Кількість ліній"
#: gtk/gtklabel.c:942
#: gtk/gtklabel.c:941
msgid "The desired number of lines, when ellipsizing a wrapping label"
msgstr "Число рядків при скороченні мітки з використанням трьох крапок"
#: gtk/gtklabel.c:956 gtk/gtktext.c:941 gtk/gtktextview.c:1079
#: gtk/gtklabel.c:955 gtk/gtktext.c:941 gtk/gtktextview.c:1079
msgid "Menu model to append to the context menu"
msgstr "Модель меню для долучення до контекстного меню"
@@ -3819,7 +3815,7 @@ msgstr "Гучність"
msgid "Volume of the audio stream."
msgstr "Гучність відтворення потоку звукових даних."
#: gtk/gtkmenubutton.c:370 gtk/gtkpopovermenubar.c:595 gtk/gtkpopovermenu.c:518
#: gtk/gtkmenubutton.c:370 gtk/gtkpopovermenubar.c:593 gtk/gtkpopovermenu.c:517
msgid "Menu model"
msgstr "Модель меню"
@@ -4503,19 +4499,19 @@ msgstr "Мнемоніка видима"
msgid "Whether mnemonics are currently visible in this popover"
msgstr "Чи показано мнемоніку у цьому накладному меню"
#: gtk/gtkpopovermenubar.c:596
#: gtk/gtkpopovermenubar.c:594
msgid "The model from which the bar is made."
msgstr "Модель, на основі якої створено панель."
#: gtk/gtkpopovermenu.c:510
#: gtk/gtkpopovermenu.c:509
msgid "Visible submenu"
msgstr "Видиме підменю"
#: gtk/gtkpopovermenu.c:511
#: gtk/gtkpopovermenu.c:510
msgid "The name of the visible submenu"
msgstr "Назва видимого підменю"
#: gtk/gtkpopovermenu.c:519
#: gtk/gtkpopovermenu.c:518
msgid "The model from which the menu is made."
msgstr "Модель, для якої створено меню."
@@ -5054,105 +5050,105 @@ msgstr "Політика вертикальної прокрутки"
msgid "The GtkAdjustment that contains the current value of this scrollbar"
msgstr "Об'єкт GtkAdjustment, що містить поточне значення цієї смужки гортання"
#: gtk/gtkscrolledwindow.c:603
#: gtk/gtkscrolledwindow.c:602
msgid "Horizontal Adjustment"
msgstr "Горизонтальне вирівнювання"
#: gtk/gtkscrolledwindow.c:604
#: gtk/gtkscrolledwindow.c:603
msgid "The GtkAdjustment for the horizontal position"
msgstr "Об'єкт GtkAdjustment для горизонтальної позиції"
#: gtk/gtkscrolledwindow.c:610
#: gtk/gtkscrolledwindow.c:609
msgid "Vertical Adjustment"
msgstr "Вертикальне вирівнювання"
#: gtk/gtkscrolledwindow.c:611
#: gtk/gtkscrolledwindow.c:610
msgid "The GtkAdjustment for the vertical position"
msgstr "Об'єкт GtkAdjustment для вертикальної позиції"
#: gtk/gtkscrolledwindow.c:617
#: gtk/gtkscrolledwindow.c:616
msgid "Horizontal Scrollbar Policy"
msgstr "Правило горизонтальної прокрутки"
#: gtk/gtkscrolledwindow.c:618
#: gtk/gtkscrolledwindow.c:617
msgid "When the horizontal scrollbar is displayed"
msgstr "Коли показано горизонтальну панель прокрутки"
#: gtk/gtkscrolledwindow.c:625
#: gtk/gtkscrolledwindow.c:624
msgid "Vertical Scrollbar Policy"
msgstr "Правило вертикальної прокрутки"
#: gtk/gtkscrolledwindow.c:626
#: gtk/gtkscrolledwindow.c:625
msgid "When the vertical scrollbar is displayed"
msgstr "Коли показано вертикальну панель прокрутки"
#: gtk/gtkscrolledwindow.c:633
#: gtk/gtkscrolledwindow.c:632
msgid "Window Placement"
msgstr "Розміщення вікна"
#: gtk/gtkscrolledwindow.c:634
#: gtk/gtkscrolledwindow.c:633
msgid "Where the contents are located with respect to the scrollbars."
msgstr "Чи розташовувати вміст у відповідності зі смугами гортання."
#: gtk/gtkscrolledwindow.c:642
#: gtk/gtkscrolledwindow.c:641
msgid "Whether to draw a frame around the contents"
msgstr "Чи слід малювати рамку навколо вмісту"
#: gtk/gtkscrolledwindow.c:653
#: gtk/gtkscrolledwindow.c:652
msgid "Minimum Content Width"
msgstr "Мінімальна ширина вмісту"
#: gtk/gtkscrolledwindow.c:654
#: gtk/gtkscrolledwindow.c:653
msgid "The minimum width that the scrolled window will allocate to its content"
msgstr "Мінімальна ширина, яку вікна прокрутки виділять з його вмістом"
#: gtk/gtkscrolledwindow.c:665
#: gtk/gtkscrolledwindow.c:664
msgid "Minimum Content Height"
msgstr "Мінімальна висота вмісту"
#: gtk/gtkscrolledwindow.c:666
#: gtk/gtkscrolledwindow.c:665
msgid ""
"The minimum height that the scrolled window will allocate to its content"
msgstr "Мінімальна висота, яку вікна прокрутки виділять з його вмістом"
#: gtk/gtkscrolledwindow.c:678
#: gtk/gtkscrolledwindow.c:677
msgid "Kinetic Scrolling"
msgstr "Кінетичне прокручування"
#: gtk/gtkscrolledwindow.c:679
#: gtk/gtkscrolledwindow.c:678
msgid "Kinetic scrolling mode."
msgstr "Режим кінетичного прокручування."
#: gtk/gtkscrolledwindow.c:696
#: gtk/gtkscrolledwindow.c:695
msgid "Overlay Scrolling"
msgstr "Накладні смуги гортання"
#: gtk/gtkscrolledwindow.c:697
#: gtk/gtkscrolledwindow.c:696
msgid "Overlay scrolling mode"
msgstr "Режим накладних смуг гортання"
#: gtk/gtkscrolledwindow.c:708
#: gtk/gtkscrolledwindow.c:707
msgid "Maximum Content Width"
msgstr "Максимальна ширина вмісту"
#: gtk/gtkscrolledwindow.c:709
#: gtk/gtkscrolledwindow.c:708
msgid "The maximum width that the scrolled window will allocate to its content"
msgstr "Максимальна ширина, яку вікна зі смужками гортання матимуть для вмісту"
#: gtk/gtkscrolledwindow.c:720
#: gtk/gtkscrolledwindow.c:719
msgid "Maximum Content Height"
msgstr "Максимальна висота вмісту"
#: gtk/gtkscrolledwindow.c:721
#: gtk/gtkscrolledwindow.c:720
msgid ""
"The maximum height that the scrolled window will allocate to its content"
msgstr "Максимальна висота, яку вікна зі смужками гортання матимуть для вмісту"
#: gtk/gtkscrolledwindow.c:736 gtk/gtkscrolledwindow.c:737
#: gtk/gtkscrolledwindow.c:735 gtk/gtkscrolledwindow.c:736
msgid "Propagate Natural Width"
msgstr "Передавати природну ширину"
#: gtk/gtkscrolledwindow.c:752 gtk/gtkscrolledwindow.c:753
#: gtk/gtkscrolledwindow.c:751 gtk/gtkscrolledwindow.c:752
msgid "Propagate Natural Height"
msgstr "Передавати природну висоту"
@@ -5591,19 +5587,19 @@ msgstr "Чи показувати курсор у тексті"
msgid "Whether to use overlay scrollbars"
msgstr "Чи використовувати накладання смужок гортання"
#: gtk/gtkshortcutaction.c:935
#: gtk/gtkshortcutaction.c:942
msgid "Signal Name"
msgstr "Назва сигналу"
#: gtk/gtkshortcutaction.c:936
#: gtk/gtkshortcutaction.c:943
msgid "The name of the signal to emit"
msgstr "Назва сигналу, який слід надіслати"
#: gtk/gtkshortcutaction.c:1174 gtk/gtkshortcutsshortcut.c:718
#: gtk/gtkshortcutaction.c:1181 gtk/gtkshortcutsshortcut.c:718
msgid "Action Name"
msgstr "Назва дії"
#: gtk/gtkshortcutaction.c:1175
#: gtk/gtkshortcutaction.c:1182
msgid "The name of the action to activate"
msgstr "Назва дії для активації"
@@ -5627,21 +5623,21 @@ msgstr "Перемикач"
msgid "The trigger for this shortcut"
msgstr "Перемикач для цього скорочення"
#: gtk/gtkshortcutcontroller.c:525
#: gtk/gtkshortcutcontroller.c:526
msgid "Mnemonic modifers"
msgstr "Модифікатори мнемоніки"
#: gtk/gtkshortcutcontroller.c:526
#: gtk/gtkshortcutcontroller.c:527
msgid "The modifiers to be pressed to allow mnemonics activation"
msgstr ""
"Клавіші-модифікатори, які має бути натиснуто, щоб уможливити активацію "
"мнемоніки"
#: gtk/gtkshortcutcontroller.c:539
#: gtk/gtkshortcutcontroller.c:540
msgid "A list model to take shortcuts from"
msgstr "Модель списку, з якої слід брати скорочення"
#: gtk/gtkshortcutcontroller.c:551
#: gtk/gtkshortcutcontroller.c:552
msgid "What scope the shortcuts will be handled in"
msgstr "Область, у якій оброблятимуться скорочення"
@@ -5823,19 +5819,21 @@ msgstr "Зсув зрізу"
msgid "Maximum size of slice"
msgstr "Максимальний розмір зрізу"
#: gtk/gtksortlistmodel.c:314
msgid "has sort"
msgstr "має упорядкування"
#: gtk/gtksortlistmodel.c:370
#| msgid "Sort order"
msgid "Sorter"
msgstr "Сортувальник"
#: gtk/gtksortlistmodel.c:315
msgid "If a sort function is set for this model"
msgstr "Чи встановлено функцію упорядкування для цієї моделі"
#: gtk/gtksortlistmodel.c:371
#| msgid "The trigger for this shortcut"
msgid "The sorter for this model"
msgstr "Сортувальник для цієї моделі"
#: gtk/gtksortlistmodel.c:327
#: gtk/gtksortlistmodel.c:383
msgid "The type of items of this list"
msgstr "Тип пунктів у цьому списку"
#: gtk/gtksortlistmodel.c:339
#: gtk/gtksortlistmodel.c:395
msgid "The model being sorted"
msgstr "Модель, яка зберігається"
@@ -6750,126 +6748,126 @@ msgstr "модель"
msgid "The model for the popover"
msgstr "Модель для накладного меню"
#: gtk/gtktreeview.c:1043
#: gtk/gtktreeview.c:1041
msgid "TreeView Model"
msgstr "Модель TreeView"
#: gtk/gtktreeview.c:1044
#: gtk/gtktreeview.c:1042
msgid "The model for the tree view"
msgstr "Модель для перегляду у вигляді дерева"
#: gtk/gtktreeview.c:1050
#: gtk/gtktreeview.c:1048
msgid "Headers Visible"
msgstr "Заголовки видимі"
#: gtk/gtktreeview.c:1051
#: gtk/gtktreeview.c:1049
msgid "Show the column header buttons"
msgstr "Показувати кнопки заголовків стовпчиків"
#: gtk/gtktreeview.c:1057
#: gtk/gtktreeview.c:1055
msgid "Headers Clickable"
msgstr "Заголовки натискаються"
#: gtk/gtktreeview.c:1058
#: gtk/gtktreeview.c:1056
msgid "Column headers respond to click events"
msgstr "Заголовки стовпчиків відповідають на події клацання"
#: gtk/gtktreeview.c:1064
#: gtk/gtktreeview.c:1062
msgid "Expander Column"
msgstr "Колонка-розширювач"
#: gtk/gtktreeview.c:1065
#: gtk/gtktreeview.c:1063
msgid "Set the column for the expander column"
msgstr "Встановити колонку для розширювача"
#: gtk/gtktreeview.c:1078
#: gtk/gtktreeview.c:1076
msgid "Enable Search"
msgstr "Дозволено пошук"
#: gtk/gtktreeview.c:1079
#: gtk/gtktreeview.c:1077
msgid "View allows user to search through columns interactively"
msgstr ""
"Перегляд дозволяє користувачу виконувати пошук у стовпчиках в інтерактивному "
"режимі"
#: gtk/gtktreeview.c:1085
#: gtk/gtktreeview.c:1083
msgid "Search Column"
msgstr "Стовпчик пошуку"
#: gtk/gtktreeview.c:1086
#: gtk/gtktreeview.c:1084
msgid "Model column to search through during interactive search"
msgstr ""
"Стовпчик моделі, за яким слід виконати інтерактивний пошук в міру набору"
#: gtk/gtktreeview.c:1102
#: gtk/gtktreeview.c:1100
msgid "Fixed Height Mode"
msgstr "Режим фіксованої висоти"
#: gtk/gtktreeview.c:1103
#: gtk/gtktreeview.c:1101
msgid "Speeds up GtkTreeView by assuming that all rows have the same height"
msgstr "Прискорює GtkTreeView, вважаючи що всі рядки мають ту ж саму висоту"
#: gtk/gtktreeview.c:1120
#: gtk/gtktreeview.c:1118
msgid "Hover Selection"
msgstr "Курсорне виділення"
#: gtk/gtktreeview.c:1121
#: gtk/gtktreeview.c:1119
msgid "Whether the selection should follow the pointer"
msgstr "Чи повинно виділення слідувати за вказівником"
#: gtk/gtktreeview.c:1137
#: gtk/gtktreeview.c:1135
msgid "Hover Expand"
msgstr "Курсорне розширення"
#: gtk/gtktreeview.c:1138
#: gtk/gtktreeview.c:1136
msgid ""
"Whether rows should be expanded/collapsed when the pointer moves over them"
msgstr ""
"Чи повинні рядки розгортатись/згортатись при переміщенні над ними вказівника"
#: gtk/gtktreeview.c:1149
#: gtk/gtktreeview.c:1147
msgid "Show Expanders"
msgstr "Показувати розширювачі"
#: gtk/gtktreeview.c:1150
#: gtk/gtktreeview.c:1148
msgid "View has expanders"
msgstr "Віджет містить розширювачі"
#: gtk/gtktreeview.c:1161
#: gtk/gtktreeview.c:1159
msgid "Level Indentation"
msgstr "Вирівнювання позначок"
#: gtk/gtktreeview.c:1162
#: gtk/gtktreeview.c:1160
msgid "Extra indentation for each level"
msgstr "Додаткове вирівнювання для кожного рівня"
#: gtk/gtktreeview.c:1169
#: gtk/gtktreeview.c:1167
msgid "Rubber Banding"
msgstr "Гумова стрічка"
#: gtk/gtktreeview.c:1170
#: gtk/gtktreeview.c:1168
msgid ""
"Whether to enable selection of multiple items by dragging the mouse pointer"
msgstr ""
"Чи дозволяти виділення кількох елементів перетягуванням вказівника миші"
#: gtk/gtktreeview.c:1176
#: gtk/gtktreeview.c:1174
msgid "Enable Grid Lines"
msgstr "Увімкнути лінії сітки"
#: gtk/gtktreeview.c:1177
#: gtk/gtktreeview.c:1175
msgid "Whether grid lines should be drawn in the tree view"
msgstr "Чи слід показувати лінії ґраток у віджеті"
#: gtk/gtktreeview.c:1184
#: gtk/gtktreeview.c:1182
msgid "Enable Tree Lines"
msgstr "Увімкнути лінії рівня вкладення"
#: gtk/gtktreeview.c:1185
#: gtk/gtktreeview.c:1183
msgid "Whether tree lines should be drawn in the tree view"
msgstr "Чи слід показувати лінії рівня вкладення у віджеті"
#: gtk/gtktreeview.c:1192
#: gtk/gtktreeview.c:1190
msgid "The column in the model containing the tooltip texts for the rows"
msgstr ""
"Стовпчик у моделі джерела даних, що містить текстові підказки для рядків"
@@ -7466,6 +7464,15 @@ msgstr "Назва профілю кольору"
msgid "The title of the color profile to use"
msgstr "Заголовок вікна вибору профілю кольору"
#~ msgid "has filter"
#~ msgstr "має фільтр"
#~ msgid "has sort"
#~ msgstr "має упорядкування"
#~ msgid "If a sort function is set for this model"
#~ msgstr "Чи встановлено функцію упорядкування для цієї моделі"
#~ msgid "Application menu"
#~ msgstr "Меню програм"
+352 -354
View File
File diff suppressed because it is too large Load Diff
+262 -36
View File
@@ -15,23 +15,13 @@
*/
#include <gtk/gtk.h>
static int win_activated;
static int box_activated;
static void
win_activate (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
activate (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
win_activated++;
}
static void
box_activate (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
box_activated++;
int *activated = user_data;
(*activated)++;
}
/* Test that inheriting actions along the widget
@@ -40,20 +30,31 @@ box_activate (GSimpleAction *action,
* the effect of activating them.
*/
static void
test_action (void)
test_inheritance (void)
{
GtkWidget *window;
GtkWidget *box;
GtkWidget *button;
GSimpleActionGroup *win_actions;
GSimpleActionGroup *box_actions;
GActionEntry win_entries[] = {
{ "action", win_activate, NULL, NULL, NULL },
};
GActionEntry box_entries[] = {
{ "action", box_activate, NULL, NULL, NULL },
GActionEntry entries[] = {
{ "action", activate, NULL, NULL, NULL },
};
gboolean found;
int win_activated;
int box_activated;
win_activated = 0;
box_activated = 0;
/* Our hierarchy looks like this:
*
* window win.action
* |
* box box.action
* |
* button
*/
window = gtk_window_new ();
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
button = gtk_button_new ();
@@ -63,15 +64,13 @@ test_action (void)
win_actions = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (win_actions),
win_entries,
G_N_ELEMENTS (win_entries),
NULL);
entries, G_N_ELEMENTS (entries),
&win_activated);
box_actions = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (box_actions),
box_entries,
G_N_ELEMENTS (box_entries),
NULL);
entries, G_N_ELEMENTS (entries),
&box_activated);
gtk_widget_insert_action_group (window, "win", G_ACTION_GROUP (win_actions));
gtk_widget_insert_action_group (box, "box", G_ACTION_GROUP (box_actions));
@@ -79,23 +78,27 @@ test_action (void)
g_assert_cmpint (win_activated, ==, 0);
g_assert_cmpint (box_activated, ==, 0);
gtk_widget_activate_action (button, "win.action", NULL);
found = gtk_widget_activate_action (button, "win.action", NULL);
g_assert_true (found);
g_assert_cmpint (win_activated, ==, 1);
g_assert_cmpint (box_activated, ==, 0);
gtk_widget_activate_action (box, "win.action", NULL);
found = gtk_widget_activate_action (box, "win.action", NULL);
g_assert_true (found);
g_assert_cmpint (win_activated, ==, 2);
g_assert_cmpint (box_activated, ==, 0);
gtk_widget_activate_action (button, "box.action", NULL);
found = gtk_widget_activate_action (button, "box.action", NULL);
g_assert_true (found);
g_assert_cmpint (win_activated, ==, 2);
g_assert_cmpint (box_activated, ==, 1);
gtk_widget_activate_action (window, "box.action", NULL);
found = gtk_widget_activate_action (window, "box.action", NULL);
g_assert_false (found);
g_assert_cmpint (win_activated, ==, 2);
g_assert_cmpint (box_activated, ==, 1);
@@ -104,6 +107,225 @@ test_action (void)
g_object_unref (box_actions);
}
/* Test action inheritance with hierarchy changes */
static void
test_inheritance2 (void)
{
GtkWidget *window;
GtkWidget *box;
GtkWidget *box1;
GtkWidget *box2;
GtkWidget *button;
GSimpleActionGroup *win_actions;
GSimpleActionGroup *box1_actions;
GSimpleActionGroup *box2_actions;
GActionEntry entries[] = {
{ "action", activate, NULL, NULL, NULL },
};
gboolean found;
int win_activated;
int box1_activated;
int box2_activated;
win_activated = 0;
box1_activated = 0;
box2_activated = 0;
/* Our hierarchy looks like this:
*
* window win.action
* |
* box--------------------+
* | |
* box1 box1.action box2 box2.action;
* |
* button
*/
window = gtk_window_new ();
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
button = gtk_button_new ();
gtk_window_set_child (GTK_WINDOW (window), box);
gtk_box_append (GTK_BOX (box), box1);
gtk_box_append (GTK_BOX (box), box2);
gtk_box_append (GTK_BOX (box1), button);
win_actions = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (win_actions),
entries, G_N_ELEMENTS (entries),
&win_activated);
box1_actions = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (box1_actions),
entries, G_N_ELEMENTS (entries),
&box1_activated);
box2_actions = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (box2_actions),
entries, G_N_ELEMENTS (entries),
&box2_activated);
gtk_widget_insert_action_group (window, "win", G_ACTION_GROUP (win_actions));
gtk_widget_insert_action_group (box1, "box1", G_ACTION_GROUP (box1_actions));
gtk_widget_insert_action_group (box2, "box2", G_ACTION_GROUP (box2_actions));
g_assert_cmpint (win_activated, ==, 0);
g_assert_cmpint (box1_activated, ==, 0);
g_assert_cmpint (box2_activated, ==, 0);
found = gtk_widget_activate_action (button, "win.action", NULL);
g_assert_true (found);
g_assert_cmpint (win_activated, ==, 1);
g_assert_cmpint (box1_activated, ==, 0);
g_assert_cmpint (box2_activated, ==, 0);
found = gtk_widget_activate_action (button, "box1.action", NULL);
g_assert_true (found);
g_assert_cmpint (win_activated, ==, 1);
g_assert_cmpint (box1_activated, ==, 1);
g_assert_cmpint (box2_activated, ==, 0);
found = gtk_widget_activate_action (button, "box2.action", NULL);
g_assert_false (found);
g_assert_cmpint (win_activated, ==, 1);
g_assert_cmpint (box1_activated, ==, 1);
g_assert_cmpint (box2_activated, ==, 0);
g_object_ref (button);
gtk_box_remove (GTK_BOX (box1), button);
gtk_box_append (GTK_BOX (box2), button);
g_object_unref (button);
found = gtk_widget_activate_action (button, "win.action", NULL);
g_assert_true (found);
g_assert_cmpint (win_activated, ==, 2);
g_assert_cmpint (box1_activated, ==, 1);
g_assert_cmpint (box2_activated, ==, 0);
found = gtk_widget_activate_action (button, "box1.action", NULL);
g_assert_false (found);
g_assert_cmpint (win_activated, ==, 2);
g_assert_cmpint (box1_activated, ==, 1);
g_assert_cmpint (box2_activated, ==, 0);
found = gtk_widget_activate_action (button, "box2.action", NULL);
g_assert_true (found);
g_assert_cmpint (win_activated, ==, 2);
g_assert_cmpint (box1_activated, ==, 1);
g_assert_cmpint (box2_activated, ==, 1);
gtk_window_destroy (GTK_WINDOW (window));
g_object_unref (win_actions);
g_object_unref (box1_actions);
g_object_unref (box2_actions);
}
/* Similar to test_inheritance2, but using the actionable machinery
*/
static void
test_inheritance3 (void)
{
GtkWidget *window;
GtkWidget *box;
GtkWidget *box1;
GtkWidget *box2;
GtkWidget *button;
GSimpleActionGroup *win_actions;
GSimpleActionGroup *box1_actions;
GActionEntry entries[] = {
{ "action", activate, NULL, NULL, NULL },
};
int activated;
/* Our hierarchy looks like this:
*
* window win.action
* |
* box--------------------+
* | |
* box1 box1.action box2
* |
* button
*/
window = gtk_window_new ();
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
button = gtk_button_new ();
gtk_window_set_child (GTK_WINDOW (window), box);
gtk_box_append (GTK_BOX (box), box1);
gtk_box_append (GTK_BOX (box), box2);
gtk_box_append (GTK_BOX (box1), button);
win_actions = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (win_actions),
entries, G_N_ELEMENTS (entries),
&activated);
box1_actions = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (box1_actions),
entries, G_N_ELEMENTS (entries),
&activated);
gtk_widget_insert_action_group (window, "win", G_ACTION_GROUP (win_actions));
gtk_widget_insert_action_group (box1, "box1", G_ACTION_GROUP (box1_actions));
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "box1.action");
g_assert_true (gtk_widget_get_sensitive (button));
g_object_ref (button);
gtk_box_remove (GTK_BOX (box1), button);
gtk_box_append (GTK_BOX (box2), button);
g_object_unref (button);
g_assert_false (gtk_widget_get_sensitive (button));
g_object_ref (button);
gtk_box_remove (GTK_BOX (box2), button);
gtk_box_append (GTK_BOX (box1), button);
g_object_unref (button);
g_assert_true (gtk_widget_get_sensitive (button));
g_object_ref (button);
gtk_box_remove (GTK_BOX (box1), button);
gtk_box_append (GTK_BOX (box2), button);
g_object_unref (button);
g_assert_false (gtk_widget_get_sensitive (button));
g_object_ref (box2);
gtk_box_remove (GTK_BOX (box), box2);
gtk_box_append (GTK_BOX (box1), box2);
g_object_unref (box2);
g_assert_true (gtk_widget_get_sensitive (button));
gtk_widget_insert_action_group (box1, "box1", NULL);
g_assert_false (gtk_widget_get_sensitive (button));
gtk_widget_insert_action_group (box1, "box1", G_ACTION_GROUP (box1_actions));
g_assert_true (gtk_widget_get_sensitive (button));
gtk_window_destroy (GTK_WINDOW (window));
g_object_unref (win_actions);
g_object_unref (box1_actions);
}
static int cut_activated;
static int copy_activated;
static int paste_activated;
@@ -199,13 +421,15 @@ test_overlap (void)
GtkWidget *window;
GtkWidget *box;
GActionEntry win_entries[] = {
{ "win", win_activate, NULL, NULL, NULL },
{ "win", activate, NULL, NULL, NULL },
};
GActionEntry box_entries[] = {
{ "box", box_activate, NULL, NULL, NULL },
{ "box", activate, NULL, NULL, NULL },
};
GSimpleActionGroup *win_actions;
GSimpleActionGroup *box_actions;
int win_activated;
int box_activated;
window = gtk_window_new ();
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
@@ -216,13 +440,13 @@ test_overlap (void)
g_action_map_add_action_entries (G_ACTION_MAP (win_actions),
win_entries,
G_N_ELEMENTS (win_entries),
NULL);
&win_activated);
box_actions = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (box_actions),
box_entries,
G_N_ELEMENTS (box_entries),
NULL);
&box_activated);
gtk_widget_insert_action_group (window, "actions", G_ACTION_GROUP (win_actions));
gtk_widget_insert_action_group (box, "actions", G_ACTION_GROUP (box_actions));
@@ -434,7 +658,9 @@ main (int argc,
{
gtk_test_init (&argc, &argv);
g_test_add_func ("/action/inheritance", test_action);
g_test_add_func ("/action/inheritance", test_inheritance);
g_test_add_func ("/action/inheritance2", test_inheritance2);
g_test_add_func ("/action/inheritance3", test_inheritance3);
g_test_add_func ("/action/text", test_text);
g_test_add_func ("/action/overlap", test_overlap);
g_test_add_func ("/action/overlap2", test_overlap2);
+4 -2
View File
@@ -114,7 +114,8 @@ test_type (gconstpointer data)
}
else if (g_type_is_a (type, GTK_TYPE_FILTER_LIST_MODEL) ||
g_type_is_a (type, GTK_TYPE_NO_SELECTION) ||
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION))
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION) ||
g_type_is_a (type, GTK_TYPE_MULTI_SELECTION))
{
GListStore *list_store = g_list_store_new (G_TYPE_OBJECT);
instance = g_object_new (type,
@@ -277,7 +278,8 @@ G_GNUC_END_IGNORE_DEPRECATIONS
if ((g_type_is_a (type, GTK_TYPE_FILTER_LIST_MODEL) ||
g_type_is_a (type, GTK_TYPE_NO_SELECTION) ||
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION)) &&
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION) ||
g_type_is_a (type, GTK_TYPE_MULTI_SELECTION)) &&
strcmp (pspec->name, "model") == 0)
continue;
+1
View File
@@ -39,6 +39,7 @@ tests = [
['listbox'],
['main'],
['maplistmodel'],
['multiselection'],
['notify'],
['no-gtk-init'],
['object'],
+393
View File
@@ -0,0 +1,393 @@
/*
* Copyright (C) 2019, Red Hat, Inc.
* Authors: Matthias Clasen <mclasen@redhat.com>
*
* 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/>.
*/
#include <locale.h>
#include <gtk/gtk.h>
static GQuark number_quark;
static GQuark changes_quark;
static GQuark selection_quark;
static guint
get (GListModel *model,
guint position)
{
GObject *object = g_list_model_get_item (model, position);
g_assert (object != NULL);
return GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark));
}
static char *
model_to_string (GListModel *model)
{
GString *string = g_string_new (NULL);
guint i;
for (i = 0; i < g_list_model_get_n_items (model); i++)
{
if (i > 0)
g_string_append (string, " ");
g_string_append_printf (string, "%u", get (model, i));
}
return g_string_free (string, FALSE);
}
static char *
selection_to_string (GListModel *model)
{
GString *string = g_string_new (NULL);
guint i;
for (i = 0; i < g_list_model_get_n_items (model); i++)
{
if (!gtk_selection_model_is_selected (GTK_SELECTION_MODEL (model), i))
continue;
if (string->len > 0)
g_string_append (string, " ");
g_string_append_printf (string, "%u", get (model, i));
}
return g_string_free (string, FALSE);
}
static GListStore *
new_store (guint start,
guint end,
guint step);
static GObject *
make_object (guint number)
{
GObject *object;
/* 0 cannot be differentiated from NULL, so don't use it */
g_assert (number != 0);
object = g_object_new (G_TYPE_OBJECT, NULL);
g_object_set_qdata (object, number_quark, GUINT_TO_POINTER (number));
return object;
}
static void
splice (GListStore *store,
guint pos,
guint removed,
guint *numbers,
guint added)
{
GObject **objects;
guint i;
objects = g_new0 (GObject *, added);
for (i = 0; i < added; i++)
objects[i] = make_object (numbers[i]);
g_list_store_splice (store, pos, removed, (gpointer *) objects, added);
for (i = 0; i < added; i++)
g_object_unref (objects[i]);
g_free (objects);
}
static void
add (GListStore *store,
guint number)
{
GObject *object = make_object (number);
g_list_store_append (store, object);
g_object_unref (object);
}
static void
insert (GListStore *store,
guint position,
guint number)
{
GObject *object = make_object (number);
g_list_store_insert (store, position, object);
g_object_unref (object);
}
#define assert_model(model, expected) G_STMT_START{ \
char *s = model_to_string (G_LIST_MODEL (model)); \
if (!g_str_equal (s, expected)) \
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
#model " == " #expected, s, "==", expected); \
g_free (s); \
}G_STMT_END
#define ignore_changes(model) G_STMT_START{ \
GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
g_string_set_size (changes, 0); \
}G_STMT_END
#define assert_changes(model, expected) G_STMT_START{ \
GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
if (!g_str_equal (changes->str, expected)) \
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
#model " == " #expected, changes->str, "==", expected); \
g_string_set_size (changes, 0); \
}G_STMT_END
#define assert_selection(model, expected) G_STMT_START{ \
char *s = selection_to_string (G_LIST_MODEL (model)); \
if (!g_str_equal (s, expected)) \
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
#model " == " #expected, s, "==", expected); \
g_free (s); \
}G_STMT_END
#define ignore_selection_changes(model) G_STMT_START{ \
GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \
g_string_set_size (changes, 0); \
}G_STMT_END
#define assert_selection_changes(model, expected) G_STMT_START{ \
GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \
if (!g_str_equal (changes->str, expected)) \
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
#model " == " #expected, changes->str, "==", expected); \
g_string_set_size (changes, 0); \
}G_STMT_END
static GListStore *
new_empty_store (void)
{
return g_list_store_new (G_TYPE_OBJECT);
}
static GListStore *
new_store (guint start,
guint end,
guint step)
{
GListStore *store = new_empty_store ();
guint i;
for (i = start; i <= end; i += step)
add (store, i);
return store;
}
static void
items_changed (GListModel *model,
guint position,
guint removed,
guint added,
GString *changes)
{
g_assert (removed != 0 || added != 0);
if (changes->len)
g_string_append (changes, ", ");
if (removed == 1 && added == 0)
{
g_string_append_printf (changes, "-%u", position);
}
else if (removed == 0 && added == 1)
{
g_string_append_printf (changes, "+%u", position);
}
else
{
g_string_append_printf (changes, "%u", position);
if (removed > 0)
g_string_append_printf (changes, "-%u", removed);
if (added > 0)
g_string_append_printf (changes, "+%u", added);
}
}
static void
selection_changed (GListModel *model,
guint position,
guint n_items,
GString *changes)
{
if (changes->len)
g_string_append (changes, ", ");
g_string_append_printf (changes, "%u:%u", position, n_items);
}
static void
free_changes (gpointer data)
{
GString *changes = data;
/* all changes must have been checked via assert_changes() before */
g_assert_cmpstr (changes->str, ==, "");
g_string_free (changes, TRUE);
}
static GtkSelectionModel *
new_model (GListStore *store)
{
GtkSelectionModel *result;
GString *changes;
result = GTK_SELECTION_MODEL (gtk_multi_selection_new (G_LIST_MODEL (store)));
changes = g_string_new ("");
g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes);
g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes);
changes = g_string_new ("");
g_object_set_qdata_full (G_OBJECT(result), selection_quark, changes, free_changes);
g_signal_connect (result, "selection-changed", G_CALLBACK (selection_changed), changes);
return result;
}
static void
test_create (void)
{
GtkSelectionModel *selection;
GListStore *store;
store = new_store (1, 5, 2);
selection = new_model (store);
assert_model (selection, "1 3 5");
assert_changes (selection, "");
assert_selection (selection, "");
assert_selection_changes (selection, "");
g_object_unref (store);
assert_model (selection, "1 3 5");
assert_changes (selection, "");
assert_selection (selection, "");
assert_selection_changes (selection, "");
g_object_unref (selection);
}
static void
test_changes (void)
{
GtkSelectionModel *selection;
GListStore *store;
store = new_store (1, 5, 1);
selection = new_model (store);
assert_model (selection, "1 2 3 4 5");
assert_changes (selection, "");
assert_selection (selection, "");
assert_selection_changes (selection, "");
g_list_store_remove (store, 3);
assert_model (selection, "1 2 3 5");
assert_changes (selection, "-3");
assert_selection (selection, "");
assert_selection_changes (selection, "");
insert (store, 3, 99);
assert_model (selection, "1 2 3 99 5");
assert_changes (selection, "+3");
assert_selection (selection, "");
assert_selection_changes (selection, "");
splice (store, 3, 2, (guint[]) { 97 }, 1);
assert_model (selection, "1 2 3 97");
assert_changes (selection, "3-2+1");
assert_selection (selection, "");
assert_selection_changes (selection, "");
g_object_unref (store);
g_object_unref (selection);
}
static void
test_selection (void)
{
GtkSelectionModel *selection;
GListStore *store;
gboolean ret;
store = new_store (1, 5, 1);
selection = new_model (store);
assert_selection (selection, "");
assert_selection_changes (selection, "");
ret = gtk_selection_model_select_item (selection, 3, FALSE);
g_assert_true (ret);
assert_selection (selection, "4");
assert_selection_changes (selection, "3:1");
ret = gtk_selection_model_unselect_item (selection, 3);
g_assert_true (ret);
assert_selection (selection, "");
assert_selection_changes (selection, "3:1");
ret = gtk_selection_model_select_item (selection, 1, FALSE);
g_assert_true (ret);
assert_selection (selection, "2");
assert_selection_changes (selection, "1:1");
ret = gtk_selection_model_select_range (selection, 3, 2, FALSE);
g_assert_true (ret);
assert_selection (selection, "2 4 5");
assert_selection_changes (selection, "3:2");
ret = gtk_selection_model_unselect_range (selection, 3, 2);
g_assert_true (ret);
assert_selection (selection, "2");
assert_selection_changes (selection, "3:2");
ret = gtk_selection_model_select_all (selection);
g_assert_true (ret);
assert_selection (selection, "1 2 3 4 5");
assert_selection_changes (selection, "0:5");
ret = gtk_selection_model_unselect_all (selection);
g_assert_true (ret);
assert_selection (selection, "");
assert_selection_changes (selection, "0:5");
g_object_unref (store);
g_object_unref (selection);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
setlocale (LC_ALL, "C");
g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=%s");
number_quark = g_quark_from_static_string ("Hell and fire was spawned to be released.");
changes_quark = g_quark_from_static_string ("What did I see? Can I believe what I saw?");
selection_quark = g_quark_from_static_string ("Mana mana, badibidibi");
g_test_add_func ("/multiselection/create", test_create);
#if GLIB_CHECK_VERSION (2, 58, 0) /* g_list_store_splice() is broken before 2.58 */
g_test_add_func ("/multiselection/changes", test_changes);
#endif
g_test_add_func ("/multiselection/selection", test_selection);
return g_test_run ();
}
+2 -1
View File
@@ -457,7 +457,8 @@ test_type (gconstpointer data)
}
else if (g_type_is_a (type, GTK_TYPE_FILTER_LIST_MODEL) ||
g_type_is_a (type, GTK_TYPE_NO_SELECTION) ||
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION))
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION) ||
g_type_is_a (type, GTK_TYPE_MULTI_SELECTION))
{
GListStore *list_store = g_list_store_new (G_TYPE_OBJECT);
instance = g_object_new (type,
+2 -1
View File
@@ -71,7 +71,8 @@ test_finalize_object (gconstpointer data)
}
else if (g_type_is_a (test_type, GTK_TYPE_FILTER_LIST_MODEL) ||
g_type_is_a (test_type, GTK_TYPE_NO_SELECTION) ||
g_type_is_a (test_type, GTK_TYPE_SINGLE_SELECTION))
g_type_is_a (test_type, GTK_TYPE_SINGLE_SELECTION) ||
g_type_is_a (test_type, GTK_TYPE_MULTI_SELECTION))
{
GListStore *list_store = g_list_store_new (G_TYPE_OBJECT);
object = g_object_new (test_type,