Compare commits

...

98 Commits

Author SHA1 Message Date
Matthias Clasen f425c3b7ab wayland: Rely on server key repeat info
Using gsettings for this information does not work
in sandboxed scenarios, where settings are per-app.
Since the Wayland protocol provides this information
nowadays, just drop the old code for reading
the gsettings.
2019-10-29 21:55:13 -04:00
Benjamin Otte 10911bfaf0 gtk-demo: Add a minesweeper demo
The demo shows creating ones own listmodel and using it to fill a grid.

I am totally getting the hang of React btw:
500 lines of logic with no UI code and 100 lines of GtkBuilder XML and
I get a sweet UI.
2019-10-28 10:57:27 +01:00
Benjamin Otte b36a58fa97 Add GtkColumnView skeleton
It's just a copy/paste of the listview code with all the internals
gutted. The code doesn't do anything.
2019-10-28 10:57:27 +01:00
Benjamin Otte f978575e13 wip: Add GtkCoverFlow
The widget mostly works out of the box, but some tweaking may be
necessary (in particular in the theme) and the gtk-demo changes might
require removing before this is production-ready.
2019-10-28 10:49:02 +01:00
Benjamin Otte cabbe06c5b listbase: Take over anchor handling
With that, pretty much all code but allocating the widgets is gone from
the gridview and listview.
2019-10-28 10:49:02 +01:00
Benjamin Otte f2044539ee listbase: Add vfuncs to convert positions to/from coordinates
... and use that to implement PageUp/PageDown.

With that, all keyboard handling has been moved to GtkListBase.
2019-10-28 10:49:02 +01:00
Benjamin Otte 165d4e8307 listbase: Move focus moving keybindings here
The focus tracker is not yet moved because that depends on scroll_to()
support and we don't have that yet.
Whoops.
So we use a hack.
2019-10-28 10:49:02 +01:00
Benjamin Otte 2ba06352b1 Remove gtk_selection_model_user_select_item() again
This reverts commit 6a164ab306dad9096bde736c907494c71086d3c4.

The function was awkward and we now have only one caller again, so we
can fold it back into it.
2019-10-28 10:49:02 +01:00
Benjamin Otte f41f288fe7 listbase: Move orientable implementation here 2019-10-28 10:49:02 +01:00
Benjamin Otte deb39d804d listbase: Move selection handling here 2019-10-28 10:49:02 +01:00
Benjamin Otte f7f2d9ce98 listbase: Move item manager here
Nothing really changes, because both ListView and GridView still keep
self->item_manager around, but it's set up to point at the base's item
manager.

This way we can slowly move things to GtkListBase that need the item
manager (like trackers).
2019-10-28 10:49:01 +01:00
Benjamin Otte 010808794f listbase: Move GtkScrollable implementation
Shared code between GtkGridView and GtkListView.
2019-10-28 10:49:01 +01:00
Benjamin Otte 6ad943c400 Add GtkListBase
This is a base item for GTK's list widgets so they can share some (read:
hopefully a lot of) code.
2019-10-28 10:49:01 +01:00
Benjamin Otte 305d0aaae1 gridview: Simplify allocation code
It doesn't fix the bug I'm after, but it looks a lot better.
2019-10-28 10:49:01 +01:00
Benjamin Otte e22f480af9 listview: Port various gridview improvements
- Handle anchor as align + top/bottom
  This fixes behavior for cells that are higher than the view
- Add gtk_list_view_adjustment_is_flipped()
  This should fix RTL handling of horizontal lists
- Fix scrolling
  This should make scrolling more reliable, particularly on short lists
  that are only a few pages long.
2019-10-28 10:49:01 +01:00
Benjamin Otte d479c4a6d4 demo: Add a file browser demo 2019-10-28 10:49:01 +01:00
Benjamin Otte e7b523bc69 gridview: Add move keybindings 2019-10-28 10:49:01 +01:00
Benjamin Otte 367a711c9d gridview: Implement (un)select all
Adds listitem.select-all and listitem.unselect-all actions and installs
the same keybindings as the treeview for those actions.
2019-10-28 10:49:01 +01:00
Benjamin Otte cc5808f99f gridview: Add a focus tracker
... and use that to properly update selections when moving around with
the arrow keys.
2019-10-28 10:49:01 +01:00
Benjamin Otte 408140bb09 gridview: Implement list.scroll-to action 2019-10-28 10:49:01 +01:00
Benjamin Otte 8b75d0c50a gridview: Add activation 2019-10-28 10:49:01 +01:00
Benjamin Otte 1a9b2d1110 gridview: Implement minimum row height
We only allocate a certain amount of widgets - and we don't want to run
out of them. So we make all widgets high enough for this to never
happen.
2019-10-28 10:49:01 +01:00
Benjamin Otte c885a44d73 gridview: Implement the list.select-item action 2019-10-28 10:49:01 +01:00
Benjamin Otte 1499d1e68e selectionmodel: Add gtk_selection_model_user_select_item()
I'm not sure this should be public API because it's incredibly awkward.
But it should definitely be shared between list widget implementations.
2019-10-28 10:49:01 +01:00
Benjamin Otte b2b001988f gridview: Implement anchors and scrolling 2019-10-28 10:49:01 +01:00
Benjamin Otte bad5b5e8ed widget: Add gtk_widget_get_size()
A little bit of convenience.
2019-10-28 10:49:01 +01:00
Benjamin Otte a9365373bb lul
https://i.imgur.com/iKX62LU.png
2019-10-28 10:49:01 +01:00
Benjamin Otte 51f9750339 listitemmanager: Handle NULL factory
Just don't call it and create empty listitems.
2019-10-28 10:49:01 +01:00
Timm Bäder 20efe0e501 demo: Use a listview as sidebar 2019-10-28 10:49:01 +01:00
Benjamin Otte 1dcf35c84d Add GtkExpression
This is the new replacement for bindings, though I'm not entirely sure
what I'm doing.
2019-10-28 10:49:01 +01:00
Benjamin Otte 4ea3d89ffe listitemfactory: Make the builder factory properly buildable
Turn the construct arguments into construct properties so that they can
be set from ui files.
2019-10-28 10:49:01 +01:00
Benjamin Otte 4d610f4b1f listview: Add move keybindings
My god, these are a lot.

And my god, these are complicated to get right.
2019-10-26 08:51:18 +02:00
Benjamin Otte 4fd5f96771 listview: Add gtk_list_view_get_position_at_y() 2019-10-26 08:51:18 +02:00
Benjamin Otte 7bddadf75f listitem: Add "listitem.select" action and keybindings for it
In fact, grab space with all modifiers to toggle selection of the
current item.
2019-10-26 08:51:18 +02:00
Benjamin Otte cfde1598b5 listview: Add a focus tracker
This ensures that the row with the input focus always stays available,
even when scrolled out of view.
2019-10-26 08:51:18 +02:00
Benjamin Otte c1d5d3036c listview: Implement (un)select all
Adds listitem.select-all and listitem.unselect-all actions and installs
the same keybindings as the treeview for those actions.
2019-10-26 08:51:18 +02:00
Benjamin Otte 101095075c listview: Track focus movements and update selection
When focus moves via tab/arrow, we need to select the new item.
2019-10-26 08:51:18 +02:00
Benjamin Otte 16be945de2 listview: Implement activation
- a GtkListview::activate signal
- a GtkListItem::activatable property
- activate list items on double clicks and <Enter> presses
2019-10-26 08:51:18 +02:00
Benjamin Otte 3a27672f95 treeexpander: Implement input support
This implements all the keybindings from GtkTreeView that can be
supported.

It does not implement expand-all, because supporting that means
causing the TreeListModel to emit lots of create_model vfuncs which in
turn would cause many items-changed signal which in turn would cause
many signal handlers to run which in turn would make "expand-all" very
reentrant, and I'm uneasy about supporting that.

For the mouse, just add a click gesture to the expander icon that toggles
expanded state.
2019-10-26 08:51:18 +02:00
Benjamin Otte ed6aa467bc listitem: Change focus handling
Focus in the listitem now works like this:
1. If any child can take focus, do not ever attempt
   to take focus.
2. Otherwise, if this item is selectable or activatable,
   allow focusing this widget.

This makes sure every item in a list is focusable for
activation and selection handling, but no useless widgets
get focused and moving focus is as fast as possible.
2019-10-26 08:51:17 +02:00
Benjamin Otte ddd0218d97 popover: Remove unneeded vfunc
The vfunc is identical to the GtkWidget implementation it replaces. So
just keep using that one.
2019-10-26 08:51:17 +02:00
Benjamin Otte 538df82ea0 inspector: Make the recorder node list use a ListView
It's quite a bit faster now, but the code is also a bit more awkward.

Pain points:

- GtkTreeListModel cannot be created in UI files because it needs
  a CreateModelFunc.
  Using a signal for this doesn't work because autoexpand wants to
  expand the model before the signal handler is connected.

- The list item factory usage is still awkward. It's bearable here
  because the list items are very simple, but still.
2019-10-26 08:51:17 +02:00
Benjamin Otte 778a11748b inspector: Use a GtkTreeExpander in the object tree 2019-10-26 08:51:17 +02:00
Benjamin Otte 98941cd696 inspector: Use a treeexpander in the recorder 2019-10-26 08:51:17 +02:00
Benjamin Otte 9d33d8b92b demo: Add a GSettings tree demo
It is meant to look somewhat like dconf-editor when it is done.

So far, it's just a list.
2019-10-26 08:51:17 +02:00
Benjamin Otte 5b1b4290ca Add GtkTreeExpander
This is a container widget that takes over all the duties of tree
expanding and collapsing.
It has to be a container so it can capture keybindings while focus is
inside the listitem.

So far, this widget does not allow interacting with it, but it shows the
expander arrow in its correct state.

Also, testlistview uses this widget now instead of implementing
expanding itself.
2019-10-26 08:51:17 +02:00
Benjamin Otte baa564a3ba gridview: Actually do something
Implement measuring and allocating items - which makes the items appear
when drawing and allows interacting with the items.

However, the gridview still does not allow any user interaction
(including scrolling).
2019-10-26 08:51:17 +02:00
Benjamin Otte 3048d330b0 listview: Pass the CSS name of listitems to the manager
... instead of hardcoding "row".
2019-10-26 08:51:17 +02:00
Benjamin Otte ef1eea6071 gridview: Implement GtkOrientable
Again, this is just the skeleton, because the Gridview does nothing yet.
2019-10-26 08:51:17 +02:00
Benjamin Otte 50f4ad6a38 gridview: Add factory handling
Just copy the listview APIs.

Code still doesn't do anything with it.
2019-10-26 08:51:17 +02:00
Benjamin Otte 399c275b1c listview: Expose GtkListItemFactory APIs
Due to the many different ways to set factories, it makes sense to
expose them as custom objects.

This makes the actual APIs for the list widgets simpler, because they
can just have a regular "factory" property.

As a convenience function, gtk_list_view_new_with_factory() was added
to make this whole approach easy to use from C.
2019-10-26 08:51:17 +02:00
Benjamin Otte b7bdf99f54 textview: Make cursor work when blinking is disabled 2019-10-26 08:51:17 +02:00
Benjamin Otte eae964a546 gtk-demo: Add a rough start at a Weather demo
This demos a horizontal listview.
2019-10-26 08:51:17 +02:00
Benjamin Otte b326f8aed3 listview: Implement GtkOrientable 2019-10-26 08:51:17 +02:00
Benjamin Otte aade4dd9bf tests: Add a rough form of multiselection
Just store a "filechooser::selected" attribute in the GFileInfo if
the file is meant to be selected.
2019-10-26 08:51:17 +02:00
Benjamin Otte 795f9f2cb5 listview: Implement extending selections
Shift-clicking to extend selections now also works, imitating the
behavior of normal clicking and Windows Explorer (but not treeview):

1. We track the last selected item (normally, not via extend-clicking).

2. When shift-selecting, we modify the range from the last selected item
   to this item the same way we modify the regular item when not using
   shift:

2a. If Ctrl is not pressed, we select the range and unselect everything
    else.

2b. If Ctrl is pressed, we make the range have the same selection state
    as the last selected item:
    - If the last selected item is selected, select the range.
    - If the last selected item is not selected, unselect the range.
2019-10-26 08:51:17 +02:00
Benjamin Otte 7a864583cf listview: Add list.scroll_to_item action
The action scrolls the given item into view.

Listitems activate this action when they gain focus.
2019-10-26 08:51:17 +02:00
Benjamin Otte 523b600119 testlistview: Load icons async
Speeds up loading by 4x, because out of view icons aren't loaded
anymore.
2019-10-26 08:51:17 +02:00
Benjamin Otte 8d5cff3d91 directorylist: Add
Adds a new listmodel called GtkDirectoryList that lists the children of
a GFile as GFileInfos.

This is supposed to be used by the filechooser.
2019-10-26 08:51:17 +02:00
Benjamin Otte 795cb9e43b gtk-demo: Introduce awards
We need a way to get a useful listbox, so here we go!
2019-10-26 08:51:17 +02:00
Benjamin Otte b9d639e34a listitemfactory: Add a factory for ui files
Reuse <template> magic to initialize GtkListItems. This feels
amazingly hacky, but it also amazingly worked on the first try.
2019-10-26 08:51:17 +02:00
Benjamin Otte 1d83e50f39 listitemfactory: Split implementation out
.. into gtkfunctionslistitemfactory.c

Now we can add a different implmenetation.
2019-10-26 08:51:17 +02:00
Benjamin Otte 8c960139dc listitemfactory: vfuncify
No functional changes other than a new indirection.
2019-10-26 08:51:17 +02:00
Benjamin Otte c7092d07f4 listitemfactory: Sanitize APIs
Make sure the APIs follow a predictable path:

setup
  bind
    rebind/update (0-N times)
  unbind
teardown

This is the first step towards providing multiple different factories.
2019-10-26 08:51:17 +02:00
Benjamin Otte 5e273c957b listview: Add gtk_list_view_set_show_separators()
Do the same thing that GtkListBox does in commit
0249bd4f8a
2019-10-26 08:51:17 +02:00
Benjamin Otte 46feacf6b8 listitemmanager: Add trackers
... and replace the anchor tracking with a tracker.

Trackers track an item through the list across changes and ensure that
this item (and potentially siblings before/after it) are always backed
by a GtkListItem and that if the item gets removed a replacement gets
chosen.

This is now used for tracking the anchor but can also be used to add
trackers for the cursor later.
2019-10-26 08:51:17 +02:00
Benjamin Otte 0353bb27d9 listitemmanager: Simplify
Remove a bunch of API from the headers that isn't used anymore and then
refactor code to not call it anymore.

In particular, get rid of GtkListItemManagerChange and replace it with a
GHashTable.
2019-10-26 08:51:17 +02:00
Benjamin Otte b5385dee22 gridview: Implement GtkScrollable
We can now scroll all the nothing we display.

We also clip it properly.
2019-10-26 08:51:17 +02:00
Benjamin Otte bafa91580b listitemmanager: Move list of listitems here
All the listview infrastructure moved with it, so the next step is
moving that back...
2019-10-26 08:51:17 +02:00
Benjamin Otte add133eb1d wayland: Remove function declaration for nonexisting function 2019-10-26 08:51:17 +02:00
Benjamin Otte 46e183f33d gridview: Add API for setting number of columns
The API isn't used yet.
2019-10-26 08:51:17 +02:00
Benjamin Otte 3c8323b11b gtk: Add a GtkGridView skeleton 2019-10-26 08:51:17 +02:00
Benjamin Otte f350416c72 listitem: Add a press gesture to select the item
This is implemented by using actions, which are a neat trick to get to
allow the ListItem to call functions on the ListView without actually
needing to be aware of it.
2019-10-26 08:51:17 +02:00
Benjamin Otte 7a58bfd7ad listview: Add initial support for displaying selections 2019-10-26 08:51:17 +02:00
Benjamin Otte ade9016987 listview: Reset listitems' CSS animations when rebinding
This way, newly displayed rows don't play an unselect animation (text
fading in) when they are unselected, but the row was previously used for
a selected item.
2019-10-26 08:51:17 +02:00
Benjamin Otte b469c16c4e listview: Add selection properties to ListItem
This just brings the infrastructure into place, we're not using the
properties yet.
2019-10-26 08:51:17 +02:00
Benjamin Otte febc5f2030 listview: Try to keep the list items in order when scrolling
Instead of just destroying all items and then recreating them (or even
hide()ing and then show()ing them again (or even even repositioning
them in the widget tree)), just try to reust them in the order they are.

This works surprisingly well when scrolling and most/all widgets
just moved.
2019-10-26 08:51:17 +02:00
Benjamin Otte 4530253974 listlistmodel: Add gtk_list_list_model_item_moved()
Use it to fix a case that just said g_warning ("oops").

Apparently I had forgotten the case where a container moved a child
in the widget tree.
2019-10-26 08:51:17 +02:00
Benjamin Otte 2fd13bd601 listitemmanager: Switch from "insert_before" to "insert_after" argumnet
We reorder widgets start to end, so when reusing a list item, we
correctly know the previous sibling for that list item, but not the
next sibling yet. We just know the widget it should ultimately be in
front of.
So we can do a more correct guess of the list item's place in the widget
tree if we think about where to place an item like this.

Actually using this change will come in the next commit.
2019-10-26 08:51:17 +02:00
Benjamin Otte 89003822c3 testlistview: Create widgets only once
Previously, we were recreating all widgets every time the list item was
rebound, which caused a lot of extra work every time we scrolled.

Now we keep the widgets around and only set their properties again when
the item changes.
2019-10-26 08:51:17 +02:00
Benjamin Otte 0595e73a8b testlistview: Show the row number
Always show the current row. This is mostly useful for debugging, not
for beauty.
2019-10-26 08:51:17 +02:00
Benjamin Otte 154a9dfde6 listview: Only allocate necesary rows
This is the big one.

The listview only allocates 200 rows around the visible row now.
Everything else is kept in ListRow instances with row->widget == NULL.

For rows without a widget, we assign the median height of the child
widgets as the row's height and then do all calculations as if there
were widgets that had requested that height (like setting adjustment
values or reacting to adjustment value changes).

When the view is scrolled, we bind the 200 rows to the new visible area,
so that the part of the listview that can be seen is always allocated.
2019-10-26 08:51:17 +02:00
Benjamin Otte 8f34c13dfe listview: Change anchor handling again
The anchor is now a tuple of { listitem, align }.

Using the actual list item allows keeping the anchor across changes
in position (ie when lists get resorted) while still being able to fall
back to positions (list items store their position) when an item gets
removed.

The align value is in the range [0..1] and defines where in the visible
area to do the alignment.
0.0 means to align the top of the row with the top of the visible area,
1.0 aligns the bottom of the widget with the visible area and 0.5 keeps
the center of the widget at the center of the visible area.
It works conceptually the same as percentages in CSS background-position
(where the background area and the background image's size are matched
the same way) or CSS transform-origin.
2019-10-26 08:51:17 +02:00
Benjamin Otte f9db1c38e7 listview: Change how binding is done
We now don't let the functions create widgets for the item from the
listmodel, instead we hand out a GtkListItem for them to add a widget
to.

GtkListItems are created in advance and can only be filled in by the
binding code by gtk_container_add()ing a widget.
However, they are GObjects, so they can provide properties that the
binding code can make use of - either via notify signals or GBinding.
2019-10-26 08:51:17 +02:00
Benjamin Otte 0c1c0242ee listitem: Add gtk_list_item_get_position()
Also refactor the whole list item management yet again.

Now, list item APIs doesn't have bind/unbind functions anymore, but only
property setters.

The item factory is the only one doing the binding.
As before, the item manager manages when items need to be bound.
2019-10-26 08:51:17 +02:00
Benjamin Otte 9afdd0ca3e tests: Make animating listview do random resorts 2019-10-26 08:51:17 +02:00
Benjamin Otte 79700acecb listview: Change change management
Add a GtkListItemManagerChange object that tracks all removed list
rows during an item-changed signal so they can be added back later.
2019-10-26 08:51:17 +02:00
Benjamin Otte 849f67c198 listview: Make the listitemmanager stricter
Require that items created with the manager get destroyed via the
manager.

To that purpose, renamed create_list_item() to acquire_list_item() and
add a matching release_list_item() function.

This way, the manager can in the future keep track of all items and
cache information about them.
2019-10-26 08:51:17 +02:00
Benjamin Otte 6f57ced025 listview: Add GtkListItem
GtkListItem is a generic row widget that is supposed to replace
GtkListBoxRow and GtkFlowBoxChild.
2019-10-26 08:51:17 +02:00
Benjamin Otte 54b65ce465 listview: Add GtkListItemManager
It's all stubs for now, but here's the basic ideas about what
this object is supposed to do:

(1) It's supposed to be handling all the child GtkWidgets that are
    used by the listview, so that the listview can concern
    itself with how many items it needs and where to put them.
(2) It's meant to do the caching of widgets that are not (currently)
    used.
(3) It's meant to track items that remain in the model across
    items-changed emissions and just change position.
(2) It's code that can be shared between listview and potential
    other widgets like a GridView.

It's also free to assume that the number of items it's supposed to
manage doesn't grow too much, so it's free to use O(N) algorithms.
2019-10-26 08:51:17 +02:00
Benjamin Otte 47225f0ceb listview: Implement an anchor
The anchor selection is very basic: just anchor the top row.

That's vastly better than any other widget already though.
2019-10-26 08:51:16 +02:00
Benjamin Otte 90bb2c4133 tests: Add a test for a permanently changing listview
This is mostly for dealing with proper anchoring and can be used to
check that things don't scroll or that selection and focus handling
properly works.

For comparison purposes, a ListBox is provided next to it.
2019-10-26 08:51:16 +02:00
Benjamin Otte d440dae1b7 listview: Implement GtkScrollable
Scrolling in a very basic form is also supported
2019-10-26 08:51:16 +02:00
Benjamin Otte f0758a2af9 listview: Make widget actually do something
The thing we're actually doing is create and maintain a widget for every
row. That's it.

Also add a testcase using this. The testcase quickly allocates too many
rows though and then becomes unresponsive though. You have been warned.
2019-10-26 08:51:16 +02:00
Benjamin Otte d11e075c2f listview: Introduce GtkListItemFactory
Thisis the abstraction I intend to use for creating widgets and binding
them to the item out of the listview.

For now this is a very dumb wrapper around the functions that exist in
the API.

But it leaves the freedom to turn this into public API, make an
interface out of it and most of all write different implementations, in
particular one that uses GtkBuilder.
2019-10-26 08:51:16 +02:00
Benjamin Otte 40ecd921b0 gtk: Add a GtkListView skeleton 2019-10-26 08:51:16 +02:00
Benjamin Otte 5aa45a2df9 builder: Allow parsing G_TYPE_BYTES from strings
Just create a bytes containing the literal string.
2019-10-26 08:51:16 +02:00
Benjamin Otte 801a3064bc togglebutton: Don't emit clicked during set_active()
set_active() is meant to set the state of the property, which happens
during setup.
The clicked signal is emitting events from user actions.

It is impossible to use Togglebuttons for MVC applications as long as it
does that.
2019-10-26 08:51:16 +02:00
85 changed files with 85755 additions and 570 deletions
+248
View File
@@ -0,0 +1,248 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "award.h"
struct _GtkAward
{
GObject parent;
char *explanation;
char *name;
char *title;
GDateTime *granted; /* or NULL if not granted */
};
enum {
PROP_0,
PROP_EXPLANATION,
PROP_NAME,
PROP_TITLE,
PROP_GRANTED,
N_PROPS,
};
static GParamSpec *properties[N_PROPS] = { NULL, };
G_DEFINE_TYPE (GtkAward, gtk_award, G_TYPE_OBJECT)
static void
gtk_award_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkAward *self = GTK_AWARD (object);
switch (prop_id)
{
case PROP_EXPLANATION:
self->explanation = g_value_dup_string (value);
break;
case PROP_NAME:
self->name = g_value_dup_string (value);
break;
case PROP_TITLE:
self->title = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_award_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkAward *self = GTK_AWARD (object);
switch (prop_id)
{
case PROP_EXPLANATION:
g_value_set_string (value, self->explanation);
break;
case PROP_NAME:
g_value_set_string (value, self->name);
break;
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
case PROP_GRANTED:
g_value_set_boxed (value, self->granted);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_award_dispose (GObject *object)
{
GtkAward *self = GTK_AWARD (object);
g_clear_pointer (&self->name, g_free);
g_clear_pointer (&self->title, g_free);
g_clear_pointer (&self->granted, g_date_time_unref);
G_OBJECT_CLASS (gtk_award_parent_class)->dispose (object);
}
static void
gtk_award_class_init (GtkAwardClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_award_set_property;
gobject_class->get_property = gtk_award_get_property;
gobject_class->dispose = gtk_award_dispose;
properties[PROP_EXPLANATION] =
g_param_spec_string ("explanation",
"Explanation",
"How to get the title",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_NAME] =
g_param_spec_string ("name",
"Name",
"internal name of the award",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_TITLE] =
g_param_spec_string ("title",
"Title",
"user-visible title",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_GRANTED] =
g_param_spec_boxed ("granted",
"Granted",
"Timestamp the award was granted or NULL if not granted yet",
G_TYPE_DATE_TIME,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_award_init (GtkAward *self)
{
}
GListModel *
gtk_award_get_list (void)
{
static GListModel *list = NULL;
if (list == NULL)
{
GtkBuilder *builder;
g_type_ensure (GTK_TYPE_AWARD);
builder = gtk_builder_new_from_resource ("/awards.ui");
gtk_builder_connect_signals (builder, NULL);
list = G_LIST_MODEL (gtk_builder_get_object (builder, "list"));
g_object_ref (list);
g_object_unref (builder);
}
return g_object_ref (list);
}
const char *
gtk_award_get_name (GtkAward *award)
{
return award->name;
}
const char *
gtk_award_get_title (GtkAward *award)
{
return award->title;
}
GDateTime *
gtk_award_get_granted (GtkAward *award)
{
return award->granted;
}
GtkAward *
award_find (const char *name)
{
GListModel *list;
GtkAward *self;
guint i;
list = gtk_award_get_list ();
g_object_unref (list);
for (i = 0; i < g_list_model_get_n_items (list); i++)
{
self = g_list_model_get_item (list, i);
g_object_unref (self);
if (g_ascii_strcasecmp (name, self->name) == 0)
return self;
}
return NULL;
}
void
award (const char *name)
{
GtkAward *self;
GNotification *notification;
self = award_find (name);
if (self == NULL)
{
g_warning ("Did not find award \"%s\"", name);
return;
}
if (self->granted)
return;
self->granted = g_date_time_new_now_utc ();
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_GRANTED]);
notification = g_notification_new ("You won an award!");
g_notification_set_body (notification, self->title);
g_application_send_notification (g_application_get_default (), NULL, notification);
g_object_unref (notification);
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef __AWARD_H__
#define __AWARD_H__
#include <gtk/gtk.h>
#define GTK_TYPE_AWARD (gtk_award_get_type ())
G_DECLARE_FINAL_TYPE (GtkAward, gtk_award, GTK, AWARD, GObject)
GListModel * gtk_award_get_list (void);
const char * gtk_award_get_name (GtkAward *award);
const char * gtk_award_get_title (GtkAward *award);
GDateTime * gtk_award_get_granted (GtkAward *award);
void award (const char *name);
#endif /* __AWARD_H__ */
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="gtk40">
<template class="GtkListItem">
<child>
<object class="GtkLabel">
<property name="label" bind-source="GtkListItem" bind-property="position"></property>
<property name="margin">6</property>
</object>
</child>
</template>
</interface>
+89
View File
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GListStore" id="list">
<property name="item-type">GtkAward</property>
<child>
<object class="GtkAward">
<property name="name">demo-inspector</property>
<!-- Transformers -->
<property name="title" translatable="yes">You got a high-rise double-pump carburetor.</property>
<property name="explanation" translatable="yes">Launch the inspector</property>
</object>
</child>
<child>
<object class="GtkAward">
<property name="name">demo-start</property>
<!-- The Matrix -->
<property name="title" translatable="yes">After this, there is no turning back.</property>
<property name="explanation" translatable="yes">Start gtk-demo</property>
</object>
</child>
<child>
<object class="GtkAward">
<property name="name">listbox-reshare</property>
<!-- Mean Girls -->
<property name="title" translatable="yes">Trying to make fetch happen</property>
<property name="explanation" translatable="yes">Reshare a tweet</property>
</object>
</child>
<child>
<object class="GtkAward">
<property name="name">listbox-100th-row</property>
<!-- Aladdin -->
<property name="title" translatable="yes">The ever impressive, long contained, often imitated, but never duplicated Genie of the lamp.</property>
<property name="explanation" translatable="yes">Select a 100th row in a list</property>
</object>
</child>
<child>
<object class="GtkAward">
<property name="name">password-best</property>
<!-- Spaceballs -->
<property name="title" translatable="yes">I've got the same combination on my luggage!</property>
<property name="explanation" translatable="yes">Use "12345" as the password</property>
</object>
</child>
<child>
<object class="GtkAward">
<property name="name">password-correct</property>
<!-- Stanley Parable -->
<property name="title" translatable="yes">Night Shark 1-1-5</property>
<property name="explanation" translatable="yes">Correctly enter a password</property>
</object>
</child>
<child>
<object class="GtkAward">
<property name="name">puzzle-give-up</property>
<!-- Pretty Woman -->
<property name="title" translatable="yes">Big Mistake. Big. Huge!</property>
<property name="explanation" translatable="yes">Close the puzzle without finishing it</property>
</object>
</child>
<child>
<object class="GtkAward">
<property name="name">puzzle-solve</property>
<!-- The Incredibles -->
<property name="title" translatable="yes">That was totally wicked!</property>
<property name="explanation" translatable="yes">Solve a puzzle</property>
</object>
</child>
<child>
<object class="GtkAward">
<property name="name">puzzle-solve-animated</property>
<!-- The Phantom Menace -->
<property name="title" translatable="yes">A surprise to be sure but a welcome one.</property>
<property name="explanation" translatable="yes">Solve an animated puzzle</property>
</object>
</child>
<child>
<object class="GtkAward">
<property name="name">puzzle-solve-large</property>
<!-- Portal -->
<property name="title" translatable="yes">Science isn't about WHY. It's about WHY NOT?!</property>
<property name="explanation" translatable="yes">Solve a puzzle with at least 20 pieces</property>
</object>
</child>
</object>
</interface>
+79
View File
@@ -0,0 +1,79 @@
/* Awards
*
* This demo demonstrates how to use lists to show the awards you have collected
* while exploring this demo.
*
*/
#include <gtk/gtk.h>
/* Include the header for accessing the awards */
#include "award.h"
static GtkWidget *window = NULL;
#if 0
static void
create_listitem (GtkListItem *item, gpointer user_data)
{
GtkWidget *label;
label = gtk_label_new (NULL);
g_object_set (label, "margin", 6, NULL); /* omg, why do we need to do this?! */
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_container_add (GTK_CONTAINER (item), label);
}
static void
bind_listitem (GtkListItem *item, gpointer user_data)
{
GtkAward *award = gtk_list_item_get_item (item);
gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item))),
gtk_award_get_title (award));
}
#endif
GtkWidget *
do_awardview (GtkWidget *do_widget)
{
if (!window)
{
GtkWidget *sw, *listview;
GListModel *list;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Awards");
gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (window), sw);
#if 0
listview = gtk_list_view_new ();
gtk_list_view_set_functions (GTK_LIST_VIEW (listview),
create_listitem,
bind_listitem,
NULL, NULL);
#else
listview = gtk_list_view_new_with_factory (
gtk_builder_list_item_factory_new_from_resource ("/awardview/awardlistitem.ui"));
#endif
list = gtk_award_get_list ();
gtk_list_view_set_model (GTK_LIST_VIEW (listview), list);
g_object_unref (list);
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
gtk_container_add (GTK_CONTAINER (sw), listview);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}
+21
View File
@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/">
<file>awards.ui</file>
</gresource>
<gresource prefix="/ui">
<file preprocess="xml-stripblanks">main.ui</file>
<file preprocess="xml-stripblanks">main-listitem.ui</file>
<file preprocess="xml-stripblanks">appmenu.ui</file>
</gresource>
<gresource prefix="/application_demo">
@@ -9,6 +13,9 @@
<file>application.ui</file>
<file>menus.ui</file>
</gresource>
<gresource prefix="/awardview">
<file>awardlistitem.ui</file>
</gresource>
<gresource prefix="/builder">
<file>demo.ui</file>
</gresource>
@@ -111,6 +118,16 @@
<file>gnome-fs-directory.png</file>
<file>gnome-fs-regular.png</file>
</gresource>
<gresource prefix="/listview_filebrowser">
<file>listview_filebrowser.ui</file>
</gresource>
<gresource prefix="/listview_minesweeper">
<file>listview_minesweeper.ui</file>
<file>listview_minesweeper_cell.ui</file>
</gresource>
<gresource prefix="/listview_weather">
<file compressed="true">listview_weather.txt</file>
</gresource>
<gresource prefix="/shortcuts">
<file>shortcuts.ui</file>
<file>shortcuts-builder.ui</file>
@@ -147,6 +164,7 @@
</gresource>
<gresource prefix="/sources">
<file>application_demo.c</file>
<file>awardview.c</file>
<file>assistant.c</file>
<file>builder.c</file>
<file>changedisplay.c</file>
@@ -189,6 +207,9 @@
<file>infobar.c</file>
<file>links.c</file>
<file>listbox.c</file>
<file>listview_filebrowser.c</file>
<file>listview_settings.c</file>
<file>listview_weather.c</file>
<file>list_store.c</file>
<file>markup.c</file>
<file>menus.c</file>
+1 -16
View File
@@ -28,22 +28,7 @@
</child>
<child type="end">
<object class="GtkLabel">
<property name="label">fps</property>
</object>
</child>
<child type="end">
<object class="GtkLabel">
<property name="label" bind-source="bowl" bind-property="framerate-string"/>
</object>
</child>
<child type="end">
<object class="GtkLabel">
<property name="label">Icons, </property>
</object>
</child>
<child type="end">
<object class="GtkLabel">
<property name="label" bind-source="bowl" bind-property="count"/>
<binding name="label">bowl.count + " Icons, " + bowl.framerate + "fps"</binding>
</object>
</child>
<child type="end">
+8 -9
View File
@@ -13,17 +13,16 @@ in_files = sys.argv[2:]
file_output = """
typedef GtkWidget *(*GDoDemoFunc) (GtkWidget *do_widget);
typedef struct _Demo Demo;
typedef struct _DemoData DemoData;
struct _Demo
struct _DemoData
{
gchar *name;
gchar *title;
gchar *filename;
char *name;
char *title;
char *filename;
GDoDemoFunc func;
Demo *children;
DemoData *children;
};
"""
# Demo = namedtuple('Demo', ['name', 'title', 'file', 'func'])
@@ -67,7 +66,7 @@ for demo in demos:
i = 0
for parent in parents:
id = parent_ids[i]
file_output += "\nDemo child" + str(id) + "[] = {\n"
file_output += "\nDemoData child" + str(id) + "[] = {\n"
# iterate over all demos and check if the name starts with the given parent name
for child in demos:
if child[1].startswith(parent + "/"):
@@ -82,7 +81,7 @@ for parent in parents:
# Sort demos by title
demos = sorted(demos, key=lambda x: x[1])
file_output += "\nDemo gtk_demos[] = {\n"
file_output += "\nDemoData gtk_demos[] = {\n"
for demo in demos:
# Do not generate one of these for demos with a parent demo
if "/" not in demo[1]:
+7 -3
View File
@@ -8,6 +8,7 @@
#include <gtk/gtk.h>
#include <stdlib.h>
#include <string.h>
#include "award.h"
static GdkPixbuf *avatar_pixbuf_other;
static GtkWidget *window = NULL;
@@ -234,9 +235,9 @@ reshare_clicked (GtkMessageRow *row,
{
GtkMessageRowPrivate *priv = row->priv;
award ("listbox-reshare");
priv->message->n_reshares++;
gtk_message_row_update (row);
}
static void
@@ -255,11 +256,14 @@ gtk_message_row_state_flags_changed (GtkWidget *widget,
{
GtkMessageRowPrivate *priv = GTK_MESSAGE_ROW (widget)->priv;
GtkStateFlags flags;
gboolean visible;
flags = gtk_widget_get_state_flags (widget);
gtk_widget_set_visible (priv->extra_buttons_box,
flags & (GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED));
visible = flags & (GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED) ? TRUE : FALSE;
gtk_widget_set_visible (priv->extra_buttons_box, visible);
if (visible && gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (widget)) % 100 == 99)
award ("listbox-100th-row");
GTK_WIDGET_CLASS (gtk_message_row_parent_class)->state_flags_changed (widget, previous_state_flags);
}
+210
View File
@@ -0,0 +1,210 @@
/* Lists/File browser
*
* This demo shows off the different layouts that are quickly achievable
* with GtkGridView by implementing a file browser with different views.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
static GtkWidget *window = NULL;
/* Create a simple object that holds the data for the different views */
typedef struct _FileBrowserView FileBrowserView;
struct _FileBrowserView
{
GObject parent_instance;
GtkListItemFactory *factory;
char *icon_name;
GtkOrientation orientation;
};
enum {
PROP_0,
PROP_FACTORY,
PROP_ICON_NAME,
PROP_ORIENTATION,
N_PROPS
};
#define FILE_BROWSER_TYPE_VIEW (file_browser_view_get_type ())
G_DECLARE_FINAL_TYPE (FileBrowserView, file_browser_view, FILE_BROWSER, VIEW, GObject);
G_DEFINE_TYPE (FileBrowserView, file_browser_view, G_TYPE_OBJECT);
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
file_browser_view_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
FileBrowserView *self = FILE_BROWSER_VIEW (object);
switch (property_id)
{
case PROP_FACTORY:
g_value_set_object (value, self->factory);
break;
case PROP_ICON_NAME:
g_value_set_string (value, self->icon_name);
break;
case PROP_ORIENTATION:
g_value_set_enum (value, self->orientation);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
file_browser_view_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
FileBrowserView *self = FILE_BROWSER_VIEW (object);
switch (prop_id)
{
case PROP_FACTORY:
g_set_object (&self->factory, g_value_get_object (value));
break;
case PROP_ICON_NAME:
g_free (self->icon_name);
self->icon_name = g_value_dup_string (value);
break;
case PROP_ORIENTATION:
self->orientation = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
file_browser_view_finalize (GObject *object)
{
FileBrowserView *self = FILE_BROWSER_VIEW (object);
g_object_unref (self->factory);
g_free (self->icon_name);
G_OBJECT_CLASS (file_browser_view_parent_class)->dispose (object);
}
static void
file_browser_view_class_init (FileBrowserViewClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = file_browser_view_get_property;
gobject_class->set_property = file_browser_view_set_property;
gobject_class->finalize = file_browser_view_finalize;
properties[PROP_FACTORY] =
g_param_spec_object ("factory",
"factory",
"factory to use in the main view",
GTK_TYPE_LIST_ITEM_FACTORY,
G_PARAM_READWRITE);
properties[PROP_ICON_NAME] =
g_param_spec_string ("icon-name",
"icon name",
"icon to display for selecting this view",
NULL,
G_PARAM_READWRITE);
properties[PROP_ORIENTATION] =
g_param_spec_enum ("orientation",
"orientation",
"orientation of the view",
GTK_TYPE_ORIENTATION,
GTK_ORIENTATION_VERTICAL,
G_PARAM_READWRITE);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void file_browser_view_init (FileBrowserView *self)
{
}
static void
up_clicked_cb (GtkButton *button,
GtkDirectoryList *list)
{
GFile *file;
file = g_file_get_parent (gtk_directory_list_get_file (list));
if (file == NULL)
return;
gtk_directory_list_set_file (list, file);
}
static void
view_activated_cb (GtkGridView *view,
guint pos,
GtkDirectoryList *list)
{
GFileInfo *info;
info = g_list_model_get_item (gtk_grid_view_get_model (view), pos);
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
gtk_directory_list_set_file (list, G_FILE (g_file_info_get_attribute_object (info, "standard::file")));
g_object_unref (info);
}
GtkWidget *
do_listview_filebrowser (GtkWidget *do_widget)
{
if (!window)
{
GtkWidget *view;
GtkBuilder *builder;
GtkDirectoryList *dirlist;
GFile *file;
builder = gtk_builder_new_from_resource ("/listview_filebrowser/listview_filebrowser.ui");
gtk_builder_add_callback_symbols (builder,
"up_clicked_cb", G_CALLBACK (up_clicked_cb),
"view_activated_cb", G_CALLBACK (view_activated_cb),
NULL);
gtk_builder_connect_signals (builder, NULL);
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
/* Create the model and fill it with the contents of the current directory */
file = g_file_new_for_path (g_get_current_dir ());
dirlist = GTK_DIRECTORY_LIST (gtk_builder_get_object (builder, "dirlist"));
gtk_directory_list_set_file (dirlist, file);
g_object_unref (file);
/* grab focus in the view */
view = GTK_WIDGET (gtk_builder_get_object (builder, "view"));
gtk_widget_grab_focus (view);
g_object_unref (builder);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}
+196
View File
@@ -0,0 +1,196 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GListStore" id="viewlist">
<property name="item-type">FileBrowserView</property>
<child>
<object class="FileBrowserView">
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<child>
<object class="GtkBox">
<child>
<object class="GtkImage">
<binding name="gicon">file-info(GtkListItem.item:GFileInfo, "standard::icon", object):GIcon</binding>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<binding name="label">file-info(GtkListItem.item:GFileInfo, "standard::display-name", string)</binding>
</object>
</child>
</object>
</child>
</template>
</interface>
]]></property>
</object>
</property>
<property name="icon-name">view-list-symbolic</property>
<property name="orientation">horizontal</property>
</object>
</child>
<child>
<object class="FileBrowserView">
<property name="icon-name">view-grid-symbolic</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkImage">
<property name="icon-size">large</property>
<binding name="gicon">file-info(GtkListItem.item:GFileInfo, "standard::icon", object):GIcon</binding>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="wrap">1</property>
<property name="wrap-mode">word-char</property>
<property name="lines">2</property>
<property name="ellipsize">end</property>
<property name="width-chars">10</property>
<property name="max-width-chars">30</property>
<binding name="label">file-info(GtkListItem.item:GFileInfo, "standard::display-name", string)</binding>
</object>
</child>
</object>
</child>
</template>
</interface>
]]></property>
</object>
</property>
<property name="orientation">vertical</property>
</object>
</child>
<child>
<object class="FileBrowserView">
<property name="icon-name">view-paged-symbolic</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<child>
<object class="GtkBox">
<child>
<object class="GtkImage">
<property name="icon-size">large</property>
<binding name="gicon">file-info(GtkListItem.item:GFileInfo, "standard::icon", object):GIcon</binding>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<binding name="label">file-info(GtkListItem.item:GFileInfo, "standard::display-name", string)</binding>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<binding name="label">file-info(GtkListItem.item:GFileInfo, "standard::size", uint64) + " bytes"</binding>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<binding name="label">file-info(GtkListItem.item:GFileInfo, "standard::content-type", string)</binding>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>
]]></property>
</object>
</property>
<property name="orientation">horizontal</property>
</object>
</child>
</object>
<object class="GtkDirectoryList" id="dirlist">
<property name="attributes">standard::name,standard::display-name,standard::icon,standard::size,standard::content-type</property>
</object>
<object class="GtkWindow" id="window">
<property name="title" translatable="yes">File browser</property>
<property name="default-width">600</property>
<property name="default-height">400</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="">
<property name="show-title-buttons">1</property>
<child>
<object class="GtkButton">
<property name="icon-name">go-up</property>
<signal name="clicked" handler="up_clicked_cb" object="dirlist" swapped="no"/>
</object>
</child>
<child type="end">
<object class="GtkListView">
<property name="valign">center</property>
<property name="orientation">horizontal</property>
<style>
<class name="linked"/>
</style>
<property name="model">
<object class="GtkSingleSelection" id="selected-view">
<property name="model">viewlist</property>
</object>
</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<child>
<object class="GtkImage">
<binding name="icon-name">GtkListItem.item:FileBrowserView.icon-name</binding>
</object>
</child>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="can-focus">1</property>
<child>
<object class="GtkGridView" id="view">
<property name="model">dirlist</property>
<property name="max-columns">15</property>
<binding name="factory">selected-view.selected-item:FileBrowserView.factory</binding>
<binding name="orientation">selected-view.selected-item:FileBrowserView.orientation</binding>
<signal name="activate" handler="view_activated_cb" object="dirlist" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</interface>
+478
View File
@@ -0,0 +1,478 @@
/* Lists/Minesweeper
*
* This demo shows how to develop a user interface for small game using a
* gridview.
*
* It demonstrates how to use the activate signal and single-press behavior
* to implement rather different interaction behavior to a typical list.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
/*** The cell object ***/
/* Create an object that holds the data for a cell in the game */
typedef struct _SweeperCell SweeperCell;
struct _SweeperCell
{
GObject parent_instance;
gboolean is_mine;
gboolean is_visible;
guint neighbor_mines;
};
enum {
CELL_PROP_0,
CELL_PROP_LABEL,
N_CELL_PROPS
};
#define SWEEPER_TYPE_CELL (sweeper_cell_get_type ())
G_DECLARE_FINAL_TYPE (SweeperCell, sweeper_cell, SWEEPER, CELL, GObject);
G_DEFINE_TYPE (SweeperCell, sweeper_cell, G_TYPE_OBJECT);
static GParamSpec *cell_properties[N_CELL_PROPS] = { NULL, };
static const char *
sweeper_cell_get_label (SweeperCell *self)
{
static const char *minecount_labels[10] = { "", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
if (!self->is_visible)
return "?";
if (self->is_mine)
return "💣";
return minecount_labels[self->neighbor_mines];
}
static void
sweeper_cell_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
SweeperCell *self = SWEEPER_CELL (object);
switch (property_id)
{
case CELL_PROP_LABEL:
g_value_set_string (value, sweeper_cell_get_label (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
sweeper_cell_class_init (SweeperCellClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = sweeper_cell_get_property;
cell_properties[CELL_PROP_LABEL] =
g_param_spec_string ("label",
"label",
"label to display for this row",
NULL,
G_PARAM_READABLE);
g_object_class_install_properties (gobject_class, N_CELL_PROPS, cell_properties);
}
static void
sweeper_cell_init (SweeperCell *self)
{
}
static void
sweeper_cell_reveal (SweeperCell *self)
{
if (self->is_visible)
return;
self->is_visible = TRUE;
g_object_notify_by_pspec (G_OBJECT (self), cell_properties[CELL_PROP_LABEL]);
}
static SweeperCell *
sweeper_cell_new ()
{
return g_object_new (SWEEPER_TYPE_CELL, NULL);
}
/*** The board object ***/
/* Create an object that holds the data for the game */
typedef struct _SweeperGame SweeperGame;
struct _SweeperGame
{
GObject parent_instance;
GPtrArray *cells;
guint width;
guint height;
gboolean playing;
gboolean win;
};
enum {
GAME_PROP_0,
GAME_PROP_HEIGHT,
GAME_PROP_PLAYING,
GAME_PROP_WIDTH,
GAME_PROP_WIN,
N_GAME_PROPS
};
#define SWEEPER_TYPE_GAME (sweeper_game_get_type ())
G_DECLARE_FINAL_TYPE (SweeperGame, sweeper_game, SWEEPER, GAME, GObject);
static GType
sweeper_game_list_model_get_item_type (GListModel *model)
{
return SWEEPER_TYPE_GAME;
}
static guint
sweeper_game_list_model_get_n_items (GListModel *model)
{
SweeperGame *self = SWEEPER_GAME (model);
return self->width * self->height;
}
static gpointer
sweeper_game_list_model_get_item (GListModel *model,
guint position)
{
SweeperGame *self = SWEEPER_GAME (model);
return g_object_ref (g_ptr_array_index (self->cells, position));
}
static void
sweeper_game_list_model_init (GListModelInterface *iface)
{
iface->get_item_type = sweeper_game_list_model_get_item_type;
iface->get_n_items = sweeper_game_list_model_get_n_items;
iface->get_item = sweeper_game_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (SweeperGame, sweeper_game, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, sweeper_game_list_model_init))
static GParamSpec *game_properties[N_GAME_PROPS] = { NULL, };
static void
sweeper_game_dispose (GObject *object)
{
SweeperGame *self = SWEEPER_GAME (object);
g_clear_pointer (&self->cells, g_ptr_array_unref);
G_OBJECT_CLASS (sweeper_game_parent_class)->dispose (object);
}
static void
sweeper_game_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
SweeperGame *self = SWEEPER_GAME (object);
switch (property_id)
{
case GAME_PROP_HEIGHT:
g_value_set_uint (value, self->height);
break;
case GAME_PROP_PLAYING:
g_value_set_boolean (value, self->playing);
break;
case GAME_PROP_WIDTH:
g_value_set_uint (value, self->width);
break;
case GAME_PROP_WIN:
g_value_set_boolean (value, self->win);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
sweeper_game_class_init (SweeperGameClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = sweeper_game_dispose;
gobject_class->get_property = sweeper_game_get_property;
game_properties[GAME_PROP_HEIGHT] =
g_param_spec_uint ("height",
"height",
"height of the game grid",
1, G_MAXUINT, 8,
G_PARAM_READABLE);
game_properties[GAME_PROP_PLAYING] =
g_param_spec_boolean ("playing",
"playing",
"if the game is still going on",
FALSE,
G_PARAM_READABLE);
game_properties[GAME_PROP_WIDTH] =
g_param_spec_uint ("width",
"width",
"width of the game grid",
1, G_MAXUINT, 8,
G_PARAM_READABLE);
game_properties[GAME_PROP_WIN] =
g_param_spec_boolean ("win",
"win",
"if the game was won",
FALSE,
G_PARAM_READABLE);
g_object_class_install_properties (gobject_class, N_GAME_PROPS, game_properties);
}
static void
sweeper_game_reset_board (SweeperGame *self,
guint width,
guint height)
{
guint i;
g_ptr_array_set_size (self->cells, 0);
for (i = 0; i < width * height; i++)
{
g_ptr_array_add (self->cells, sweeper_cell_new ());
}
if (self->width != width)
{
self->width = width;
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIDTH]);
}
if (self->height != height)
{
self->height = height;
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_HEIGHT]);
}
if (!self->playing)
{
self->playing = TRUE;
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_PLAYING]);
}
if (self->win)
{
self->win = FALSE;
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIN]);
}
}
static void
sweeper_game_place_mines (SweeperGame *self,
guint n_mines)
{
guint i;
for (i = 0; i < n_mines; i++)
{
SweeperCell *cell;
do {
cell = g_ptr_array_index (self->cells, g_random_int_range (0, self->cells->len));
} while (cell->is_mine);
cell->is_mine = TRUE;
}
}
static SweeperCell *
get_cell (SweeperGame *self,
guint x,
guint y)
{
return g_ptr_array_index (self->cells, y * self->width + x);
}
static void
sweeper_game_count_neighbor_mines (SweeperGame *self,
guint width,
guint height)
{
guint x, y, x2, y2;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
SweeperCell *cell = get_cell (self, x, y);
for (y2 = MAX (1, y) - 1; y2 < MIN (height, y + 2); y2++)
{
for (x2 = MAX (1, x) - 1; x2 < MIN (width, x + 2); x2++)
{
SweeperCell *other = get_cell (self, x2, y2);
if (other->is_mine)
cell->neighbor_mines++;
}
}
}
}
}
static void
sweeper_game_new_game (SweeperGame *self,
guint width,
guint height,
guint n_mines)
{
guint n_items_before;
g_return_if_fail (n_mines <= width * height);
n_items_before = self->width * self->height;
g_object_freeze_notify (G_OBJECT (self));
sweeper_game_reset_board (self, width, height);
sweeper_game_place_mines (self, n_mines);
sweeper_game_count_neighbor_mines (self, width, height);
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items_before, width * height);
g_object_thaw_notify (G_OBJECT (self));
}
static void
sweeper_game_init (SweeperGame *self)
{
self->cells = g_ptr_array_new_with_free_func (g_object_unref);
sweeper_game_new_game (self, 8, 8, 10);
}
static void
sweeper_game_end (SweeperGame *self,
gboolean win)
{
if (self->playing)
{
self->playing = FALSE;
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_PLAYING]);
}
if (self->win != win)
{
self->win = win;
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIN]);
}
}
static void
sweeper_game_check_finished (SweeperGame *self)
{
guint i;
if (!self->playing)
return;
for (i = 0; i < self->cells->len; i++)
{
SweeperCell *cell = g_ptr_array_index (self->cells, i);
/* There's still a non-revealed cell that isn't a mine */
if (!cell->is_visible && !cell->is_mine)
return;
}
sweeper_game_end (self, TRUE);
}
static void
sweeper_game_reveal_cell (SweeperGame *self,
guint position)
{
SweeperCell *cell;
if (!self->playing)
return;
cell = g_ptr_array_index (self->cells, position);
sweeper_cell_reveal (cell);
if (cell->is_mine)
sweeper_game_end (self, FALSE);
sweeper_game_check_finished (self);
}
static void
cell_clicked_cb (GtkGridView *gridview,
guint pos,
SweeperGame *game)
{
sweeper_game_reveal_cell (game, pos);
}
static void
new_game_cb (GtkButton *button,
SweeperGame *game)
{
sweeper_game_new_game (game, 8, 8, 10);
}
static GtkWidget *window = NULL;
GtkWidget *
do_listview_minesweeper (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkBuilder *builder;
g_type_ensure (SWEEPER_TYPE_GAME);
builder = gtk_builder_new_from_resource ("/listview_minesweeper/listview_minesweeper.ui");
gtk_builder_add_callback_symbols (builder,
"cell_clicked_cb", G_CALLBACK (cell_clicked_cb),
"new_game_cb", G_CALLBACK (new_game_cb),
NULL);
gtk_builder_connect_signals (builder, NULL);
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
g_object_unref (builder);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}
+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="SweeperGame" id="game">
</object>
<object class="GtkWindow" id="window">
<property name="title" translatable="yes">Minesweeper</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="">
<property name="show-title-buttons">1</property>
<child>
<object class="GtkButton">
<property name="label">New Game</property>
<signal name="clicked" handler="new_game_cb" object="game" swapped="no"/>
</object>
</child>
<child type="title">
<object class="GtkImage">
<property name="icon-name">trophy-gold</property>
<binding name="visible">game.win</binding>
</object>
</child>
</object>
</child>
<child>
<object class="GtkGridView" id="view">
<property name="model">
<object class="GtkNoSelection">
<property name="model">game</property>
</object>
</property>
<binding name="max-columns">game.width</binding>
<binding name="min-columns">game.width</binding>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="resource">/listview_minesweeper/listview_minesweeper_cell.ui</property>
</object>
</property>
<signal name="activate" handler="cell_clicked_cb" object="game" swapped="no"/>
</object>
</child>
</object>
</interface>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<child>
<object class="GtkLabel">
<property name="halign">center</property>
<property name="valign">center</property>
<binding name="label">GtkListItem.item:SweeperCell.label</binding>
</object>
</child>
</template>
</interface>
+147
View File
@@ -0,0 +1,147 @@
/* Lists/Settings
*
* This demo shows a settings viwer for GSettings.
*
* It demonstrates how to implement support for trees with listview.
*/
#include <gtk/gtk.h>
static int
strvcmp (gconstpointer p1,
gconstpointer p2)
{
const char * const *s1 = p1;
const char * const *s2 = p2;
return strcmp (*s1, *s2);
}
static GListModel *
create_settings_model (gpointer item,
gpointer unused)
{
GSettings *settings = item;
char **schemas;
GListStore *result;
guint i;
if (settings == NULL)
{
g_settings_schema_source_list_schemas (g_settings_schema_source_get_default (),
TRUE,
&schemas,
NULL);
}
else
{
schemas = g_settings_list_children (settings);
}
if (schemas == NULL || schemas[0] == NULL)
{
g_free (schemas);
return NULL;
}
qsort (schemas, g_strv_length (schemas), sizeof (char *), strvcmp);
result = g_list_store_new (G_TYPE_SETTINGS);
for (i = 0; schemas[i] != NULL; i++)
{
GSettings *child;
if (settings == NULL)
child = g_settings_new (schemas[i]);
else
child = g_settings_get_child (settings, schemas[i]);
g_list_store_append (result, child);
g_object_unref (child);
}
return G_LIST_MODEL (result);
}
static void
setup_widget (GtkListItem *list_item,
gpointer unused)
{
GtkWidget *label, *expander;
expander = gtk_tree_expander_new ();
gtk_container_add (GTK_CONTAINER (list_item), expander);
label = gtk_label_new (NULL);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), label);
}
static void
bind_widget (GtkListItem *list_item,
gpointer unused)
{
GSettings *settings;
GtkWidget *label, *expander;
GSettingsSchema *schema;
expander = gtk_bin_get_child (GTK_BIN (list_item));
gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (expander), gtk_list_item_get_item (list_item));
label = gtk_tree_expander_get_child (GTK_TREE_EXPANDER (expander));
settings = gtk_tree_expander_get_item (GTK_TREE_EXPANDER (expander));
g_object_get (settings, "settings-schema", &schema, NULL);
gtk_label_set_label (GTK_LABEL (label), g_settings_schema_get_id (schema));
g_settings_schema_unref (schema);
}
static GtkWidget *window = NULL;
GtkWidget *
do_listview_settings (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *listview, *sw;;
GListModel *model;
GtkTreeListModel *treemodel;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), 400, 600);
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Settings");
g_signal_connect (window, "destroy",
G_CALLBACK(gtk_widget_destroyed), &window);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (window), sw);
listview = gtk_list_view_new_with_factory (
gtk_functions_list_item_factory_new (setup_widget,
bind_widget,
NULL, NULL));
model = create_settings_model (NULL, NULL);
treemodel = gtk_tree_list_model_new (FALSE,
model,
TRUE,
create_settings_model,
NULL,
NULL);
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (treemodel));
g_object_unref (treemodel);
g_object_unref (model);
gtk_container_add (GTK_CONTAINER (sw), listview);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}
+316
View File
@@ -0,0 +1,316 @@
/* Lists/Weather
*
* This demo shows a few of the rarer features of GtkListView and
* how they can be used to display weather information.
*
* The hourly weather info uses a horizontal listview. This is easy
* to achieve because GtkListView implements the GtkOrientable interface.
*
* To make the items in the list stand out more, the listview uses
* separators.
*
* A GtkNoSelectionModel is used to make sure no item in the list can be
* selected. All other interactions with the items is still possible.
*/
#include <gtk/gtk.h>
#define GTK_TYPE_WEATHER_INFO (gtk_weather_info_get_type ())
G_DECLARE_FINAL_TYPE (GtkWeatherInfo, gtk_weather_info, GTK, WEATHER_INFO, GObject)
typedef enum {
GTK_WEATHER_CLEAR,
GTK_WEATHER_FEW_CLOUDS,
GTK_WEATHER_FOG,
GTK_WEATHER_OVERCAST,
GTK_WEATHER_SCATTERED_SHOWERS,
GTK_WEATHER_SHOWERS,
GTK_WEATHER_SNOW,
GTK_WEATHER_STORM
} GtkWeatherType;
struct _GtkWeatherInfo
{
GObject parent_instance;
gint64 timestamp;
int temperature;
GtkWeatherType weather_type;
};
struct _GtkWeatherInfoClass
{
GObjectClass parent_class;
};
static void
gtk_weather_info_class_init (GtkWeatherInfoClass *klass)
{
}
static void
gtk_weather_info_init (GtkWeatherInfo *self)
{
}
G_DEFINE_TYPE (GtkWeatherInfo, gtk_weather_info, G_TYPE_OBJECT);
static GtkWeatherInfo *
gtk_weather_info_new (GDateTime *timestamp,
GtkWeatherInfo *copy_from)
{
GtkWeatherInfo *result;
result = g_object_new (GTK_TYPE_WEATHER_INFO, NULL);
result->timestamp = g_date_time_to_unix (timestamp);
if (copy_from)
{
result->temperature = copy_from->temperature;
result->weather_type = copy_from->weather_type;
g_object_unref (copy_from);
}
return result;
}
static GDateTime *
parse_timestamp (const char *string,
GTimeZone *timezone)
{
char *with_seconds;
GDateTime *result;
with_seconds = g_strconcat (string, ":00", NULL);
result = g_date_time_new_from_iso8601 (with_seconds, timezone);
g_free (with_seconds);
return result;
}
static GtkWeatherType
parse_weather_type (const char *clouds,
const char *precip,
GtkWeatherType fallback)
{
if (strstr (precip, "SN"))
return GTK_WEATHER_SNOW;
if (strstr (precip, "TS"))
return GTK_WEATHER_STORM;
if (strstr (precip, "DZ"))
return GTK_WEATHER_SCATTERED_SHOWERS;
if (strstr (precip, "SH") || strstr (precip, "RA"))
return GTK_WEATHER_SHOWERS;
if (strstr (precip, "FG"))
return GTK_WEATHER_FOG;
if (g_str_equal (clouds, "M") ||
g_str_equal (clouds, ""))
return fallback;
if (strstr (clouds, "OVC") ||
strstr (clouds, "BKN"))
return GTK_WEATHER_OVERCAST;
if (strstr (clouds, "BKN") ||
strstr (clouds, "SCT"))
return GTK_WEATHER_FEW_CLOUDS;
if (strstr (clouds, "VV"))
return GTK_WEATHER_FOG;
return GTK_WEATHER_CLEAR;
}
static double
parse_temperature (const char *s,
double fallback)
{
char *endptr;
double d;
d = g_ascii_strtod (s, &endptr);
if (*endptr != '\0')
return fallback;
return d;
}
static GListModel *
create_weather_model (void)
{
GListStore *store;
GTimeZone *utc;
GDateTime *timestamp;
GtkWeatherInfo *info;
GBytes *data;
char **lines;
guint i;
store = g_list_store_new (GTK_TYPE_WEATHER_INFO);
data = g_resources_lookup_data ("/listview_weather/listview_weather.txt", 0, NULL);
lines = g_strsplit (g_bytes_get_data (data, NULL), "\n", 0);
utc = g_time_zone_new_utc ();
timestamp = g_date_time_new (utc, 2011, 1, 1, 0, 0, 0);
info = gtk_weather_info_new (timestamp, NULL);
g_list_store_append (store, info);
for (i = 0; lines[i] != NULL && *lines[i]; i++)
{
char **fields;
GDateTime *date;
fields = g_strsplit (lines[i], ",", 0);
date = parse_timestamp (fields[0], utc);
while (g_date_time_difference (date, timestamp) > 30 * G_TIME_SPAN_MINUTE)
{
GDateTime *new_timestamp = g_date_time_add_hours (timestamp, 1);
g_date_time_unref (timestamp);
timestamp = new_timestamp;
info = gtk_weather_info_new (timestamp, info);
g_list_store_append (store, info);
}
info->temperature = parse_temperature (fields[1], info->temperature);
info->weather_type = parse_weather_type (fields[2], fields[3], info->weather_type);
g_date_time_unref (date);
g_strfreev (fields);
}
g_strfreev (lines);
g_bytes_unref (data);
g_time_zone_unref (utc);
return G_LIST_MODEL (store);
}
static void
setup_widget (GtkListItem *list_item,
gpointer unused)
{
GtkWidget *box, *child;
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (list_item), box);
child = gtk_label_new (NULL);
gtk_label_set_width_chars (GTK_LABEL (child), 5);
gtk_container_add (GTK_CONTAINER (box), child);
child = gtk_image_new ();
gtk_image_set_icon_size (GTK_IMAGE (child), GTK_ICON_SIZE_LARGE);
gtk_container_add (GTK_CONTAINER (box), child);
child = gtk_label_new (NULL);
gtk_widget_set_vexpand (child, TRUE);
gtk_widget_set_valign (child, GTK_ALIGN_END);
gtk_label_set_width_chars (GTK_LABEL (child), 4);
gtk_container_add (GTK_CONTAINER (box), child);
}
static void
bind_widget (GtkListItem *list_item,
gpointer unused)
{
GtkWidget *box, *child;
GtkWeatherInfo *info;
GDateTime *timestamp;
char *s;
box = gtk_bin_get_child (GTK_BIN (list_item));
info = gtk_list_item_get_item (list_item);
child = gtk_widget_get_first_child (box);
timestamp = g_date_time_new_from_unix_utc (info->timestamp);
s = g_date_time_format (timestamp, "%R");
gtk_label_set_text (GTK_LABEL (child), s);
g_free (s);
g_date_time_unref (timestamp);
child = gtk_widget_get_next_sibling (child);
switch (info->weather_type)
{
case GTK_WEATHER_CLEAR:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-clear-symbolic");
break;
case GTK_WEATHER_FEW_CLOUDS:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-few-clouds-symbolic");
break;
case GTK_WEATHER_FOG:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-fog-symbolic");
break;
case GTK_WEATHER_OVERCAST:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-overcast-symbolic");
break;
case GTK_WEATHER_SCATTERED_SHOWERS:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-showers-scattered-symbolic");
break;
case GTK_WEATHER_SHOWERS:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-showers-symbolic");
break;
case GTK_WEATHER_SNOW:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-snow-symbolic");
break;
case GTK_WEATHER_STORM:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-storm-symbolic");
break;
default:
gtk_image_clear (GTK_IMAGE (child));
break;
}
child = gtk_widget_get_next_sibling (child);
s = g_strdup_printf ("%d°", info->temperature);
gtk_label_set_text (GTK_LABEL (child), s);
g_free (s);
}
static GtkWidget *window = NULL;
GtkWidget *
do_listview_weather (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *listview, *sw;;
GListModel *model, *selection;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Weather");
g_signal_connect (window, "destroy",
G_CALLBACK(gtk_widget_destroyed), &window);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (window), sw);
listview = gtk_list_view_new_with_factory (
gtk_functions_list_item_factory_new (setup_widget,
bind_widget,
NULL, NULL));
gtk_orientable_set_orientation (GTK_ORIENTABLE (listview), GTK_ORIENTATION_HORIZONTAL);
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
model = create_weather_model ();
selection = G_LIST_MODEL (gtk_no_selection_new (model));
gtk_list_view_set_model (GTK_LIST_VIEW (listview), selection);
g_object_unref (selection);
g_object_unref (model);
gtk_container_add (GTK_CONTAINER (sw), listview);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}
File diff suppressed because it is too large Load Diff
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<child>
<object class="GtkTreeExpander" id="expander">
<binding name="list-row">GtkListItem.item</binding>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<binding name="label">expander.item:GtkDemo.title</binding>
</object>
</child>
<child>
<object class="GtkLabel">
<binding name="visible">!expander.list-row.expandable</binding>
<property name="halign">start</property>
<property name="use-markup">1</property>
<binding name="label">"&lt;small&gt;" + expander.item:GtkDemo.filename + "&lt;/small&gt;"</binding>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
</property>
</object>
</child>
</template>
</interface>
+194 -171
View File
@@ -8,6 +8,7 @@
#include <gtk/gtk.h>
#include <glib/gstdio.h>
#include "award.h"
#include "demos.h"
static GtkWidget *info_view;
@@ -15,19 +16,99 @@ static GtkWidget *source_view;
static gchar *current_file = NULL;
static GtkWidget *window;
static GtkWidget *notebook;
static GtkWidget *treeview;
static GtkWidget *listview;
static GtkSingleSelection *selection;
static GtkWidget *headerbar;
enum {
NAME_COLUMN,
TITLE_COLUMN,
FILENAME_COLUMN,
FUNC_COLUMN,
STYLE_COLUMN,
NUM_COLUMNS
typedef struct _GtkDemo GtkDemo;
struct _GtkDemo
{
GObject parent_instance;
const char *name;
const char *title;
const char *filename;
GDoDemoFunc func;
GListModel *children_model;
};
enum {
PROP_0,
PROP_FILENAME,
PROP_NAME,
PROP_TITLE,
N_PROPS
};
# define GTK_TYPE_DEMO (gtk_demo_get_type ())
G_DECLARE_FINAL_TYPE (GtkDemo, gtk_demo, GTK, DEMO, GObject);
G_DEFINE_TYPE (GtkDemo, gtk_demo, G_TYPE_OBJECT);
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_demo_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkDemo *self = GTK_DEMO (object);
switch (property_id)
{
case PROP_FILENAME:
g_value_set_string (value, self->filename);
break;
case PROP_NAME:
g_value_set_string (value, self->name);
break;
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void gtk_demo_class_init (GtkDemoClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = gtk_demo_get_property;
properties[PROP_FILENAME] =
g_param_spec_string ("filename",
"filename",
"filename",
NULL,
G_PARAM_READABLE);
properties[PROP_NAME] =
g_param_spec_string ("name",
"name",
"name",
NULL,
G_PARAM_READABLE);
properties[PROP_TITLE] =
g_param_spec_string ("title",
"title",
"title",
NULL,
G_PARAM_READABLE);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void gtk_demo_init (GtkDemo *self)
{
}
typedef struct _CallbackData CallbackData;
struct _CallbackData
{
@@ -35,6 +116,27 @@ struct _CallbackData
GtkTreePath *path;
};
static gboolean
gtk_demo_run (GtkDemo *self,
GtkWidget *window)
{
GtkWidget *result;
if (!self->func)
return FALSE;
result = self->func (window);
if (result == NULL)
return FALSE;
if (GTK_IS_WINDOW (result))
{
gtk_window_set_transient_for (GTK_WINDOW (result), GTK_WINDOW (window));
gtk_window_set_modal (GTK_WINDOW (result), TRUE);
}
return TRUE;
}
static void
activate_about (GSimpleAction *action,
GVariant *parameter,
@@ -111,69 +213,7 @@ activate_inspector (GSimpleAction *action,
gpointer user_data)
{
gtk_window_set_interactive_debugging (TRUE);
}
static void
window_closed_cb (GtkWidget *window, gpointer data)
{
CallbackData *cbdata = data;
GtkTreeIter iter;
PangoStyle style;
gtk_tree_model_get_iter (cbdata->model, &iter, cbdata->path);
gtk_tree_model_get (GTK_TREE_MODEL (cbdata->model), &iter,
STYLE_COLUMN, &style,
-1);
if (style == PANGO_STYLE_ITALIC)
gtk_tree_store_set (GTK_TREE_STORE (cbdata->model), &iter,
STYLE_COLUMN, PANGO_STYLE_NORMAL,
-1);
gtk_tree_path_free (cbdata->path);
g_free (cbdata);
}
static void
run_example_for_row (GtkWidget *window,
GtkTreeModel *model,
GtkTreeIter *iter)
{
PangoStyle style;
GDoDemoFunc func;
GtkWidget *demo;
gtk_tree_model_get (GTK_TREE_MODEL (model),
iter,
FUNC_COLUMN, &func,
STYLE_COLUMN, &style,
-1);
if (func)
{
gtk_tree_store_set (GTK_TREE_STORE (model),
iter,
STYLE_COLUMN, (style == PANGO_STYLE_ITALIC ? PANGO_STYLE_NORMAL : PANGO_STYLE_ITALIC),
-1);
demo = (func) (window);
if (demo != NULL)
{
CallbackData *cbdata;
cbdata = g_new (CallbackData, 1);
cbdata->model = model;
cbdata->path = gtk_tree_model_get_path (model, iter);
if (GTK_IS_WINDOW (demo))
{
gtk_window_set_transient_for (GTK_WINDOW (demo), GTK_WINDOW (window));
gtk_window_set_modal (GTK_WINDOW (demo), TRUE);
}
g_signal_connect (demo, "destroy",
G_CALLBACK (window_closed_cb), cbdata);
}
}
award ("demo-inspector");
}
static void
@@ -181,14 +221,10 @@ activate_run (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkWidget *window = user_data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeListRow *row = gtk_single_selection_get_selected_item (selection);
GtkDemo *demo = gtk_tree_list_row_get_item (row);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
if (gtk_tree_selection_get_selected (selection, &model, &iter))
run_example_for_row (window, model, &iter);
gtk_demo_run (demo, window);
}
/* Stupid syntax highlighting.
@@ -888,81 +924,72 @@ load_file (const gchar *demoname,
}
static void
selection_cb (GtkTreeSelection *selection,
GtkTreeModel *model)
selection_cb (GtkSingleSelection *selection,
GParamSpec *pspec,
gpointer user_data)
{
GtkTreeIter iter;
char *name;
char *filename;
char *title;
GtkTreeListRow *row = gtk_single_selection_get_selected_item (selection);
GtkDemo *demo = gtk_tree_list_row_get_item (row);
if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
return;
if (demo->filename)
load_file (demo->name, demo->filename);
gtk_tree_model_get (model, &iter,
NAME_COLUMN, &name,
TITLE_COLUMN, &title,
FILENAME_COLUMN, &filename,
-1);
if (filename)
load_file (name, filename);
gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), title);
g_free (name);
g_free (title);
g_free (filename);
gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), demo->title);
}
static void
populate_model (GtkTreeModel *model)
static GListModel *
create_demo_model (void)
{
Demo *d = gtk_demos;
GListStore *store = g_list_store_new (GTK_TYPE_DEMO);
DemoData *demo = gtk_demos;
/* this code only supports 1 level of children. If we
* want more we probably have to use a recursing function.
*/
while (d->title)
while (demo->title)
{
Demo *children = d->children;
GtkTreeIter iter;
GtkDemo *d = GTK_DEMO (g_object_new (GTK_TYPE_DEMO, NULL));
DemoData *children = demo->children;
gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
d->name = demo->name;
d->title = demo->title;
d->filename = demo->filename;
d->func = demo->func;
gtk_tree_store_set (GTK_TREE_STORE (model),
&iter,
NAME_COLUMN, d->name,
TITLE_COLUMN, d->title,
FILENAME_COLUMN, d->filename,
FUNC_COLUMN, d->func,
STYLE_COLUMN, PANGO_STYLE_NORMAL,
-1);
g_list_store_append (store, d);
d++;
if (!children)
continue;
while (children->title)
if (children)
{
GtkTreeIter child_iter;
d->children_model = G_LIST_MODEL (g_list_store_new (GTK_TYPE_DEMO));
gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &iter);
while (children->title)
{
GtkDemo *child = GTK_DEMO (g_object_new (GTK_TYPE_DEMO, NULL));
gtk_tree_store_set (GTK_TREE_STORE (model),
&child_iter,
NAME_COLUMN, children->name,
TITLE_COLUMN, children->title,
FILENAME_COLUMN, children->filename,
FUNC_COLUMN, children->func,
STYLE_COLUMN, PANGO_STYLE_NORMAL,
-1);
child->name = children->name;
child->title = children->title;
child->filename = children->filename;
child->func = children->func;
children++;
g_list_store_append (G_LIST_STORE (d->children_model), child);
children++;
}
}
demo++;
}
return G_LIST_MODEL (store);
}
static GListModel *
get_child_model (gpointer item,
gpointer user_data)
{
GtkDemo *demo = item;
if (demo->children_model)
return g_object_ref (G_LIST_MODEL (demo->children_model));
return NULL;
}
static void
@@ -982,22 +1009,6 @@ startup (GApplication *app)
g_object_unref (builder);
}
static void
row_activated_cb (GtkWidget *tree_view,
GtkTreePath *path,
GtkTreeViewColumn *column)
{
GtkTreeIter iter;
GtkWidget *window;
GtkTreeModel *model;
window = GTK_WIDGET (gtk_widget_get_root (tree_view));
model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
gtk_tree_model_get_iter (model, &iter, path);
run_example_for_row (window, model, &iter);
}
static void
start_cb (GtkMenuItem *item, GtkWidget *scrollbar)
{
@@ -1024,34 +1035,45 @@ scrollbar_popup (GtkWidget *scrollbar, GtkWidget *menu)
return TRUE;
}
static void
listview_activate_cb (GtkCoverFlow *listview,
guint position,
gpointer user_data)
{
GtkTreeListRow *row = g_list_model_get_item (gtk_cover_flow_get_model (listview), position);
GtkDemo *demo = gtk_tree_list_row_get_item (row);
gtk_demo_run (demo, window);
}
static void
activate (GApplication *app)
{
GtkBuilder *builder;
GtkWindow *window;
GtkWidget *widget;
GtkTreeModel *model;
GtkTreeIter iter;
GError *error = NULL;
GtkWidget *sw;
GtkWidget *scrollbar;
GtkWidget *menu;
GtkWidget *item;
GListModel *listmodel;
GtkTreeListModel *treemodel;
static GActionEntry win_entries[] = {
{ "run", activate_run, NULL, NULL, NULL }
};
builder = gtk_builder_new ();
gtk_builder_add_callback_symbol (builder, "listview_activate_cb", G_CALLBACK (listview_activate_cb));
gtk_builder_add_from_resource (builder, "/ui/main.ui", &error);
if (error != NULL)
{
g_critical ("%s", error->message);
exit (1);
}
gtk_builder_connect_signals (builder, NULL);
window = (GtkWindow *)gtk_builder_get_object (builder, "window");
gtk_application_add_window (GTK_APPLICATION (app), window);
window = (GtkWidget *)gtk_builder_get_object (builder, "window");
gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (window));
g_action_map_add_action_entries (G_ACTION_MAP (window),
win_entries, G_N_ELEMENTS (win_entries),
window);
@@ -1061,8 +1083,7 @@ activate (GApplication *app)
info_view = (GtkWidget *)gtk_builder_get_object (builder, "info-textview");
source_view = (GtkWidget *)gtk_builder_get_object (builder, "source-textview");
headerbar = (GtkWidget *)gtk_builder_get_object (builder, "headerbar");
treeview = (GtkWidget *)gtk_builder_get_object (builder, "treeview");
model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
listview = (GtkWidget *)gtk_builder_get_object (builder, "listview");
sw = (GtkWidget *)gtk_builder_get_object (builder, "source-scrolledwindow");
scrollbar = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (sw));
@@ -1081,17 +1102,19 @@ activate (GApplication *app)
load_file (gtk_demos[0].name, gtk_demos[0].filename);
populate_model (model);
listmodel = create_demo_model ();
treemodel = gtk_tree_list_model_new (FALSE,
G_LIST_MODEL (listmodel),
FALSE,
get_child_model,
NULL,
NULL);
selection = gtk_single_selection_new (G_LIST_MODEL (treemodel));
g_signal_connect (selection, "notify::selected-item", G_CALLBACK (selection_cb), NULL);
gtk_cover_flow_set_model (GTK_COVER_FLOW (listview),
G_LIST_MODEL (selection));
g_signal_connect (treeview, "row-activated", G_CALLBACK (row_activated_cb), model);
widget = (GtkWidget *)gtk_builder_get_object (builder, "treeview-selection");
g_signal_connect (widget, "changed", G_CALLBACK (selection_cb), model);
gtk_tree_model_get_iter_first (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)), &iter);
gtk_tree_selection_select_iter (GTK_TREE_SELECTION (widget), &iter);
gtk_tree_view_collapse_all (GTK_TREE_VIEW (treeview));
award ("demo-start");
gtk_widget_show (GTK_WIDGET (window));
@@ -1108,7 +1131,7 @@ auto_quit (gpointer data)
static void
list_demos (void)
{
Demo *d, *c;
DemoData *d, *c;
d = gtk_demos;
@@ -1135,7 +1158,7 @@ command_line (GApplication *app,
const gchar *name = NULL;
gboolean autoquit = FALSE;
gboolean list = FALSE;
Demo *d, *c;
DemoData *d, *c;
GDoDemoFunc func = 0;
GtkWidget *window, *demo;
+7 -25
View File
@@ -28,7 +28,6 @@
<property name="default-width">800</property>
<property name="default-height">600</property>
<property name="title">GTK Demo</property>
<signal name="destroy" handler="gtk_main_quit" swapped="no"/>
<child type="titlebar">
<object class="GtkHeaderBar" id="headerbar">
<property name="show-title-buttons">1</property>
@@ -66,32 +65,15 @@
<property name="can-focus">1</property>
<property name="hscrollbar-policy">never</property>
<property name="min-content-width">150</property>
<child>
<object class="GtkTreeView" id="treeview">
<property name="can-focus">1</property>
<property name="model">treestore</property>
<property name="headers-visible">0</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection">
<property name="mode">browse</property>
<object class="GtkCoverFlow" id="listview">
<signal name="activate" handler="listview_activate_cb" swapped="no" />
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="resource">/ui/main-listitem.ui</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="style">4</attribute>
<attribute name="text">1</attribute>
</attributes>
</child>
<child>
<object class="GtkCellRendererText">
<property name="text"> </property>
</object>
</child>
</object>
</child>
</property>
</object>
</child>
</object>
+6
View File
@@ -3,6 +3,7 @@
demos = files([
'application_demo.c',
'assistant.c',
'awardview.c',
'builder.c',
'changedisplay.c',
'clipboard.c',
@@ -43,6 +44,10 @@ demos = files([
'listbox.c',
'flowbox.c',
'list_store.c',
'listview_filebrowser.c',
'listview_minesweeper.c',
'listview_settings.c',
'listview_weather.c',
'markup.c',
'menus.c',
'modelbutton.c',
@@ -85,6 +90,7 @@ demos = files([
gtkdemo_deps = [ libgtk_dep, ]
extra_demo_sources = files(['main.c',
'award.c',
'gtkfishbowl.c',
'fontplane.c',
'gtkgears.c',
+15 -1
View File
@@ -11,6 +11,8 @@
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "award.h"
static GtkWidget *entry;
static GtkWidget *entry2;
static GtkWidget *button;
@@ -25,6 +27,18 @@ update_button (GObject *object,
gtk_widget_set_sensitive (button,
text[0] != '\0' && g_str_equal (text, text2));
if (g_str_equal (text, text2) &&
g_ascii_strcasecmp (text, "12345") == 0)
award ("password-best");
}
static void
button_pressed (GtkButton *button,
GtkWidget *window)
{
award ("password-correct");
gtk_widget_destroy (window);
}
GtkWidget *
@@ -72,7 +86,7 @@ do_password_entry (GtkWidget *do_widget)
button = gtk_button_new_with_mnemonic ("_Done");
gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action");
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
g_signal_connect (button, "clicked", G_CALLBACK (button_pressed), window);
gtk_widget_set_sensitive (button, FALSE);
gtk_header_bar_pack_end (GTK_HEADER_BAR (header), button);
+24 -1
View File
@@ -11,6 +11,9 @@
#include "puzzlepiece.h"
#include "paintable.h"
/* Give out awards */
#include "award.h"
static GtkWidget *window = NULL;
static GtkWidget *frame = NULL;
static GtkWidget *choices = NULL;
@@ -156,6 +159,14 @@ check_solved (GtkWidget *grid)
picture = gtk_grid_get_child_at (GTK_GRID (grid), pos_x, pos_y);
gtk_picture_set_paintable (GTK_PICTURE (picture), piece);
/* Hand out a bunch of awards
*/
award ("puzzle-solve");
if ((gdk_paintable_get_flags (piece) & GDK_PAINTABLE_STATIC_CONTENTS) == 0)
award ("puzzle-solve-animated");
if (height * width > 20)
award ("puzzle-solve-large");
return TRUE;
}
@@ -401,6 +412,18 @@ add_choice (GtkWidget *choices,
gtk_container_add (GTK_CONTAINER (choices), icon);
}
static void
widget_destroyed (GtkWidget *widget,
GtkWidget **widget_pointer)
{
if (widget_pointer)
*widget_pointer = NULL;
if (!solved)
award ("puzzle-give-up");
}
GtkWidget *
do_sliding_puzzle (GtkWidget *do_widget)
{
@@ -469,7 +492,7 @@ do_sliding_puzzle (GtkWidget *do_widget)
gtk_window_set_titlebar (GTK_WINDOW (window), header);
gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
G_CALLBACK (widget_destroyed), &window);
frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, (float) gdk_paintable_get_intrinsic_aspect_ratio (puzzle), FALSE);
gtk_container_add (GTK_CONTAINER (window), frame);
+4
View File
@@ -53,6 +53,7 @@
<xi:include href="xml/gtkselectionmodel.xml" />
<xi:include href="xml/gtknoselection.xml" />
<xi:include href="xml/gtksingleselection.xml" />
<xi:include href="xml/gtkdirectorylist.xml" />
</chapter>
<chapter id="Application">
@@ -87,6 +88,9 @@
<xi:include href="xml/gtkrevealer.xml" />
<xi:include href="xml/gtklistbox.xml" />
<xi:include href="xml/gtkflowbox.xml" />
<xi:include href="xml/gtklistview.xml" />
<xi:include href="xml/gtkgridview.xml" />
<xi:include href="xml/gtktreeexpander.xml" />
<xi:include href="xml/gtkstack.xml" />
<xi:include href="xml/gtkstackswitcher.xml" />
<xi:include href="xml/gtkstacksidebar.xml" />
+155
View File
@@ -493,6 +493,114 @@ gtk_single_selection_set_can_unselect
gtk_single_selection_get_type
</SECTION>
<SECTION>
<FILE>gtklistitem</FILE>
<TITLE>GtkListItem</TITLE>
GtkListItem
gtk_list_item_get_item
gtk_list_item_get_position
gtk_list_item_get_selected
gtk_list_item_get_selectable
gtk_list_item_set_selectable
<SUBSECTION Standard>
GTK_LIST_ITEM
GTK_LIST_ITEM_CLASS
GTK_LIST_ITEM_GET_CLASS
GTK_IS_LIST_ITEM
GTK_IS_LIST_ITEM_CLASS
GTK_TYPE_LIST_ITEM
<SUBSECTION Private>
gtk_list_item_get_type
</SECTION>
<SECTION>
<FILE>gtklistview</FILE>
<TITLE>GtkListView</TITLE>
GtkListView
gtk_list_view_new
gtk_list_view_new_with_factory
gtk_list_view_set_factory
gtk_list_view_get_factory
gtk_list_view_set_model
gtk_list_view_get_model
gtk_list_view_set_show_separators
gtk_list_view_get_show_separators
<SUBSECTION Standard>
GTK_LIST_VIEW
GTK_LIST_VIEW_CLASS
GTK_LIST_VIEW_GET_CLASS
GTK_IS_LIST_VIEW
GTK_IS_LIST_VIEW_CLASS
GTK_TYPE_LIST_VIEW
<SUBSECTION Private>
gtk_list_view_get_type
</SECTION>
<SECTION>
<FILE>gtkcolumnview</FILE>
<TITLE>GtkColumnView</TITLE>
GtkColumnView
gtk_column_view_new
gtk_column_view_get_columns
gtk_column_view_get_model
gtk_column_view_set_model
gtk_column_view_get_show_separators
gtk_column_view_set_show_separators
<SUBSECTION Standard>
GTK_COLUMN_VIEW
GTK_COLUMN_VIEW_CLASS
GTK_COLUMN_VIEW_GET_CLASS
GTK_IS_COLUMN_VIEW
GTK_IS_COLUMN_VIEW_CLASS
GTK_TYPE_COLUMN_VIEW
<SUBSECTION Private>
gtk_column_view_get_type
</SECTION>
<SECTION>
<FILE>gtkcolumnviewcolumn</FILE>
<TITLE>GtkColumnViewColumn</TITLE>
GtkColumnViewColumn
gtk_column_view_column_new
gtk_column_view_column_new_with_factory
gtk_column_view_column_get_column_view
gtk_column_view_column_set_factory
gtk_column_view_column_get_factory
gtk_column_view_column_set_title
gtk_column_view_column_get_title
<SUBSECTION Standard>
GTK_COLUMN_VIEW_COLUMN
GTK_COLUMN_VIEW_COLUMN_CLASS
GTK_COLUMN_VIEW_COLUMN_GET_CLASS
GTK_IS_COLUMN_VIEW_COLUMN
GTK_IS_COLUMN_VIEW_COLUMN_CLASS
GTK_TYPE_COLUMN_VIEW_COLUMN
<SUBSECTION Private>
gtk_column_view_column_get_type
</SECTION>
<SECTION>
<FILE>gtkgridview</FILE>
<TITLE>GtkGridView</TITLE>
GtkGridView
gtk_grid_view_new
gtk_grid_view_set_model
gtk_grid_view_get_model
gtk_grid_view_set_max_columns
gtk_grid_view_get_max_columns
gtk_grid_view_set_min_columns
gtk_grid_view_get_min_columns
<SUBSECTION Standard>
GTK_GRID_VIEW
GTK_GRID_VIEW_CLASS
GTK_GRID_VIEW_GET_CLASS
GTK_IS_GRID_VIEW
GTK_IS_GRID_VIEW_CLASS
GTK_TYPE_GRID_VIEW
<SUBSECTION Private>
gtk_grid_view_get_type
</SECTION>
<SECTION>
<FILE>gtkbuildable</FILE>
GtkBuildable
@@ -1322,6 +1430,32 @@ GTK_TYPE_FILE_FILTER
gtk_file_filter_get_type
</SECTION>
<SECTION>
<FILE>gtkdirectorylist</FILE>
<TITLE>GtkDirectoryList</TITLE>
GtkDirectoryList
gtk_directory_list_new
gtk_directory_list_get_attributes
gtk_directory_list_set_attributes
gtk_directory_list_get_file
gtk_directory_list_set_file
gtk_directory_list_get_io_priority
gtk_directory_list_set_io_priority
gtk_directory_list_is_loading
gtk_directory_list_get_error
<SUBSECTION Standard>
GTK_DIRECTORY_LIST
GTK_IS_DIRECTORY_LIST
GTK_TYPE_DIRECTORY_LIST
GTK_DIRECTORY_LIST_CLASS
GTK_IS_DIRECTORY_LIST_CLASS
GTK_DIRECTORY_LIST_GET_CLASS
<SUBSECTION Private>
gtk_directory_list_get_type
</SECTION>
<SECTION>
<FILE>gtkspinbutton</FILE>
<SECTION>
<FILE>gtkfilterlistmodel</FILE>
<TITLE>GtkFilterListModel</TITLE>
@@ -3464,6 +3598,26 @@ gtk_tree_list_model_get_type
gtk_tree_list_row_get_type
</SECTION>
<SECTION>
<FILE>gtktreeexpander</FILE>
<TITLE>GtkTreeExpander</TITLE>
gtk_tree_expander_new
gtk_tree_expander_get_child
gtk_tree_expander_set_child
gtk_tree_expander_get_item
gtk_tree_expander_get_list_row
gtk_tree_expander_set_list_row
<SUBSECTION Standard>
GTK_TREE_EXPANDER
GTK_IS_TREE_EXPANDER
GTK_TYPE_TREE_EXPANDER
GTK_TREE_EXPANDER_CLASS
GTK_IS_TREE_EXPANDER_CLASS
GTK_TREE_EXPANDER_GET_CLASS
<SUBSECTION Private>
gtk_tree_expander_get_type
</SECTION>
<SECTION>
<FILE>gtktreemodel</FILE>
<TITLE>GtkTreeModel</TITLE>
@@ -4551,6 +4705,7 @@ gtk_widget_get_allocation
gtk_widget_get_allocated_baseline
gtk_widget_get_width
gtk_widget_get_height
gtk_widget_get_size
gtk_widget_compute_bounds
gtk_widget_compute_transform
gtk_widget_compute_point
+3 -14
View File
@@ -2055,20 +2055,9 @@ get_key_repeat (GdkWaylandSeat *seat,
}
else
{
GSettings *keyboard_settings = get_keyboard_settings (seat);
if (keyboard_settings)
{
repeat = g_settings_get_boolean (keyboard_settings, "repeat");
*delay = g_settings_get_uint (keyboard_settings, "delay");
*interval = g_settings_get_uint (keyboard_settings, "repeat-interval");
}
else
{
repeat = TRUE;
*delay = 400;
*interval = 80;
}
repeat = TRUE;
*delay = 400;
*interval = 80;
}
return repeat;
-3
View File
@@ -45,9 +45,6 @@ typedef struct _GdkWaylandSurfaceClass GdkWaylandSurfaceClass;
GDK_AVAILABLE_IN_ALL
GType gdk_wayland_surface_get_type (void);
GDK_AVAILABLE_IN_ALL
GdkSurface * gdk_wayland_surface_new_subsurface (GdkDisplay *display,
const GdkRectangle *position);
GDK_AVAILABLE_IN_ALL
struct wl_surface *gdk_wayland_surface_get_wl_surface (GdkSurface *surface);
+2
View File
@@ -92,6 +92,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureSingle, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureSwipe, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureZoom, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGrid, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGridView, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkHeaderBar, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkIMContext, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkIMContextSimple, g_object_unref)
@@ -104,6 +105,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkInfoBar, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLevelBar, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLinkButton, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListStore, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListView, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLockButton, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkMenuBar, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkMenuButton, g_object_unref)
+12
View File
@@ -55,6 +55,7 @@
#include <gtk/gtkbox.h>
#include <gtk/gtkbuildable.h>
#include <gtk/gtkbuilder.h>
#include <gtk/gtkbuilderlistitemfactory.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkcalendar.h>
#include <gtk/gtkcellarea.h>
@@ -81,15 +82,19 @@
#include <gtk/gtkcolorchooserdialog.h>
#include <gtk/gtkcolorchooserwidget.h>
#include <gtk/gtkcolorutils.h>
#include <gtk/gtkcolumnview.h>
#include <gtk/gtkcolumnviewcolumn.h>
#include <gtk/gtkcombobox.h>
#include <gtk/gtkcomboboxtext.h>
#include <gtk/gtkconstraintlayout.h>
#include <gtk/gtkconstraint.h>
#include <gtk/gtkcontainer.h>
#include <gtk/gtkcoverflow.h>
#include <gtk/gtkcssprovider.h>
#include <gtk/gtkcustomlayout.h>
#include <gtk/gtkdebug.h>
#include <gtk/gtkdialog.h>
#include <gtk/gtkdirectorylist.h>
#include <gtk/gtkdnd.h>
#include <gtk/gtkdragdest.h>
#include <gtk/gtkdragsource.h>
@@ -121,6 +126,7 @@
#include <gtk/gtkfontchooserdialog.h>
#include <gtk/gtkfontchooserwidget.h>
#include <gtk/gtkframe.h>
#include <gtk/gtkfunctionslistitemfactory.h>
#include <gtk/gtkgesture.h>
#include <gtk/gtkgestureclick.h>
#include <gtk/gtkgesturedrag.h>
@@ -134,6 +140,7 @@
#include <gtk/gtkglarea.h>
#include <gtk/gtkgrid.h>
#include <gtk/gtkgridlayout.h>
#include <gtk/gtkgridview.h>
#include <gtk/gtkheaderbar.h>
#include <gtk/gtkicontheme.h>
#include <gtk/gtkiconview.h>
@@ -146,9 +153,13 @@
#include <gtk/gtklayoutmanager.h>
#include <gtk/gtklayoutchild.h>
#include <gtk/gtklevelbar.h>
#include <gtk/gtklistbase.h>
#include <gtk/gtklinkbutton.h>
#include <gtk/gtklistbox.h>
#include <gtk/gtklistitem.h>
#include <gtk/gtklistitemfactory.h>
#include <gtk/gtkliststore.h>
#include <gtk/gtklistview.h>
#include <gtk/gtklockbutton.h>
#include <gtk/gtkmain.h>
#include <gtk/gtkmaplistmodel.h>
@@ -243,6 +254,7 @@
#include <gtk/gtktooltip.h>
#include <gtk/gtktestutils.h>
#include <gtk/gtktreednd.h>
#include <gtk/gtktreeexpander.h>
#include <gtk/gtktreelistmodel.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtktreemodelfilter.h>
+44 -22
View File
@@ -209,6 +209,7 @@
#include "gtkbuildable.h"
#include "gtkbuilderprivate.h"
#include "gtkdebug.h"
#include "gtkexpressionprivate.h"
#include "gtkmain.h"
#include "gtkintl.h"
#include "gtkprivate.h"
@@ -992,19 +993,9 @@ gtk_builder_apply_delayed_properties (GtkBuilder *builder)
g_slist_free (props);
}
static inline void
free_binding_info (gpointer data,
gpointer user)
{
BindingInfo *info = data;
g_free (info->source);
g_free (info->source_property);
g_slice_free (BindingInfo, data);
}
static inline void
gtk_builder_create_bindings (GtkBuilder *builder)
static inline gboolean
gtk_builder_create_bindings (GtkBuilder *builder,
GError **error)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
GSList *l;
@@ -1014,24 +1005,51 @@ gtk_builder_create_bindings (GtkBuilder *builder)
BindingInfo *info = l->data;
GObject *source;
source = _gtk_builder_lookup_object (builder, info->source, info->line, info->col);
if (source)
g_object_bind_property (source, info->source_property,
info->target, info->target_pspec->name,
info->flags);
if (info->tag_type == TAG_BINDING)
{
source = _gtk_builder_lookup_object (builder, info->source, info->line, info->col);
if (source)
g_object_bind_property (source, info->source_property,
info->target, info->target_pspec->name,
info->flags);
}
else
{
GtkExpression *expression, *assign;
GValue value = G_VALUE_INIT;
free_binding_info (info, NULL);
expression = gtk_expression_parse (builder, info->source, error);
if (expression == NULL)
{
g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col);
goto fail;
}
assign = gtk_expression_new_assign (info->target, info->target_pspec->name, expression);
if (gtk_expression_evaluate (assign, &value))
g_value_unset (&value);
gtk_expression_unref (assign);
}
_free_binding_info (info, NULL);
}
fail:
for (; l; l = l->next)
{
_free_binding_info (l->data, NULL);
}
g_slist_free (priv->bindings);
priv->bindings = NULL;
return TRUE;
}
void
_gtk_builder_finish (GtkBuilder *builder)
gboolean
_gtk_builder_finish (GtkBuilder *builder,
GError **error)
{
gtk_builder_apply_delayed_properties (builder);
gtk_builder_create_bindings (builder);
return gtk_builder_create_bindings (builder, error);
}
/**
@@ -2095,6 +2113,10 @@ gtk_builder_value_from_string_type (GtkBuilder *builder,
gchar **vector = g_strsplit (string, "\n", 0);
g_value_take_boxed (value, vector);
}
else if (G_VALUE_HOLDS (value, G_TYPE_BYTES))
{
g_value_take_boxed (value, g_bytes_new (string, strlen (string)));
}
else
{
g_set_error (error,
+282
View File
@@ -0,0 +1,282 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkbuilderlistitemfactory.h"
#include "gtkbuilder.h"
#include "gtkintl.h"
#include "gtklistitemfactoryprivate.h"
#include "gtklistitemprivate.h"
struct _GtkBuilderListItemFactory
{
GtkListItemFactory parent_instance;
GBytes *bytes;
char *resource;
};
struct _GtkBuilderListItemFactoryClass
{
GtkListItemFactoryClass parent_class;
};
enum {
PROP_0,
PROP_BYTES,
PROP_RESOURCE,
N_PROPS
};
G_DEFINE_TYPE (GtkBuilderListItemFactory, gtk_builder_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_builder_list_item_factory_setup (GtkListItemFactory *factory,
GtkListItem *list_item)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (factory);
GtkBuilder *builder;
GError *error = NULL;
GTK_LIST_ITEM_FACTORY_CLASS (gtk_builder_list_item_factory_parent_class)->setup (factory, list_item);
builder = gtk_builder_new ();
if (!gtk_builder_extend_with_template (builder, GTK_WIDGET (list_item), G_OBJECT_TYPE (list_item),
(const gchar *)g_bytes_get_data (self->bytes, NULL),
g_bytes_get_size (self->bytes),
&error))
{
g_critical ("Error building template for list item: %s", error->message);
g_error_free (error);
/* This should never happen, if the template XML cannot be built
* then it is a critical programming error.
*/
g_object_unref (builder);
return;
}
gtk_builder_connect_signals (builder, list_item);
g_object_unref (builder);
}
static void
gtk_builder_list_item_factory_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
switch (property_id)
{
case PROP_BYTES:
g_value_set_boxed (value, self->bytes);
break;
case PROP_RESOURCE:
g_value_set_string (value, self->resource);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gboolean
gtk_builder_list_item_factory_set_bytes (GtkBuilderListItemFactory *self,
GBytes *bytes)
{
if (bytes == NULL)
return FALSE;
if (self->bytes)
{
g_critical ("Data for GtkBuilderListItemFactory has already been set.");
return FALSE;
}
self->bytes = g_bytes_ref (bytes);
return TRUE;
}
static void
gtk_builder_list_item_factory_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
switch (property_id)
{
case PROP_BYTES:
gtk_builder_list_item_factory_set_bytes (self, g_value_get_boxed (value));
break;
case PROP_RESOURCE:
{
GError *error = NULL;
GBytes *bytes;
const char *resource;
resource = g_value_get_string (value);
if (resource == NULL)
break;
bytes = g_resources_lookup_data (resource, 0, &error);
if (bytes)
{
if (gtk_builder_list_item_factory_set_bytes (self, bytes))
self->resource = g_strdup (resource);
g_bytes_unref (bytes);
}
else
{
g_critical ("Unable to load resource for list item template: %s", error->message);
g_error_free (error);
}
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_builder_list_item_factory_finalize (GObject *object)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
g_bytes_unref (self->bytes);
g_free (self->resource);
G_OBJECT_CLASS (gtk_builder_list_item_factory_parent_class)->finalize (object);
}
static void
gtk_builder_list_item_factory_class_init (GtkBuilderListItemFactoryClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
gobject_class->finalize = gtk_builder_list_item_factory_finalize;
gobject_class->get_property = gtk_builder_list_item_factory_get_property;
gobject_class->set_property = gtk_builder_list_item_factory_set_property;
factory_class->setup = gtk_builder_list_item_factory_setup;
/**
* GtkBuilderListItemFactory:bytes:
*
* bytes containing the UI definition
*/
properties[PROP_BYTES] =
g_param_spec_boxed ("bytes",
P_("Bytes"),
P_("bytes containing the UI definition"),
G_TYPE_BYTES,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
/**
* GtkBuilderListItemFactory:resource:
*
* resource containing the UI definition
*/
properties[PROP_RESOURCE] =
g_param_spec_string ("resource",
P_("Resource"),
P_("resource containing the UI definition"),
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_builder_list_item_factory_init (GtkBuilderListItemFactory *self)
{
}
GtkListItemFactory *
gtk_builder_list_item_factory_new_from_bytes (GBytes *bytes)
{
g_return_val_if_fail (bytes != NULL, NULL);
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
"bytes", bytes,
NULL);
}
GtkListItemFactory *
gtk_builder_list_item_factory_new_from_resource (const char *resource_path)
{
g_return_val_if_fail (resource_path != NULL, NULL);
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
"resource", resource_path,
NULL);
}
/**
* gtk_builder_list_item_factory_get_bytes:
* @self: a #GtkBuilderListItemFactory
*
* Gets the data used as the #GtkBuilder UI template for constructing
* listitems.
*
* Returns: (transfer none): The GtkBuilder data
*
**/
GBytes *
gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self)
{
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
return self->bytes;
}
/**
* gtk_builder_list_item_factory_get_resource:
* @self: a #GtkBuilderListItemFactory
*
* If the data references a resource, gets the path of that resource.
*
* Returns: (transfer none) (nullable): The path to the resource or %NULL
* if none
**/
const char *
gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self)
{
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
return self->resource;
}
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_BUILDER_LIST_ITEM_FACTORY_H__
#define __GTK_BUILDER_LIST_ITEM_FACTORY_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtklistitemfactory.h>
G_BEGIN_DECLS
#define GTK_TYPE_BUILDER_LIST_ITEM_FACTORY (gtk_builder_list_item_factory_get_type ())
#define GTK_BUILDER_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactory))
#define GTK_BUILDER_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactoryClass))
#define GTK_IS_BUILDER_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
#define GTK_IS_BUILDER_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
#define GTK_BUILDER_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactoryClass))
typedef struct _GtkBuilderListItemFactory GtkBuilderListItemFactory;
typedef struct _GtkBuilderListItemFactoryClass GtkBuilderListItemFactoryClass;
GDK_AVAILABLE_IN_ALL
GType gtk_builder_list_item_factory_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkListItemFactory * gtk_builder_list_item_factory_new_from_bytes (GBytes *bytes);
GDK_AVAILABLE_IN_ALL
GtkListItemFactory * gtk_builder_list_item_factory_new_from_resource (const char *resource_path);
GDK_AVAILABLE_IN_ALL
GBytes * gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
const char * gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self) G_GNUC_PURE;
G_END_DECLS
#endif /* __GTK_BUILDER_LIST_ITEM_FACTORY_H__ */
+134 -2
View File
@@ -928,7 +928,8 @@ parse_property (ParserData *data,
{
BindingInfo *binfo;
binfo = g_slice_new (BindingInfo);
binfo = g_slice_new0 (BindingInfo);
binfo->tag_type = TAG_BINDING;
binfo->target = NULL;
binfo->target_pspec = pspec;
binfo->source = g_strdup (bind_source);
@@ -960,6 +961,87 @@ parse_property (ParserData *data,
state_push (data, info);
}
static void
parse_binding (ParserData *data,
const gchar *element_name,
const gchar **names,
const gchar **values,
GError **error)
{
BindingInfo *info;
const gchar *name = NULL;
const gchar *context = NULL;
gboolean translatable = FALSE;
ObjectInfo *object_info;
GParamSpec *pspec = NULL;
gint line, col;
object_info = state_peek_info (data, ObjectInfo);
if (!object_info ||
!(object_info->tag_type == TAG_OBJECT ||
object_info->tag_type == TAG_TEMPLATE))
{
error_invalid_tag (data, element_name, NULL, error);
return;
}
if (!g_markup_collect_attributes (element_name, names, values, error,
G_MARKUP_COLLECT_STRING, "name", &name,
G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable,
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &context,
G_MARKUP_COLLECT_INVALID))
{
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
pspec = g_object_class_find_property (object_info->oclass, name);
if (!pspec)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_PROPERTY,
"Invalid property: %s.%s",
g_type_name (object_info->type), name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
else if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_PROPERTY,
"%s.%s is a construct-only property",
g_type_name (object_info->type), name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
else if (!(pspec->flags & G_PARAM_WRITABLE))
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_PROPERTY,
"%s.%s is a non-writable property",
g_type_name (object_info->type), name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
gtk_buildable_parse_context_get_position (&data->ctx, &line, &col);
info = g_slice_new0 (BindingInfo);
info->tag_type = TAG_EXPRESSION;
info->target = NULL;
info->target_pspec = pspec;
info->source = NULL;
info->flags = 0;
info->line = line;
info->col = col;
state_push (data, info);
}
static void
free_property_info (PropertyInfo *info)
{
@@ -1052,6 +1134,16 @@ _free_signal_info (SignalInfo *info,
g_slice_free (SignalInfo, info);
}
void
_free_binding_info (BindingInfo *info,
gpointer user)
{
g_free (info->source);
g_free (info->source_property);
g_slice_free (BindingInfo, info);
}
static void
free_requires_info (RequiresInfo *info,
gpointer user_data)
@@ -1292,6 +1384,8 @@ start_element (GtkBuildableParseContext *context,
}
else if (strcmp (element_name, "property") == 0)
parse_property (data, element_name, names, values, error);
else if (strcmp (element_name, "binding") == 0)
parse_binding (data, element_name, names, values, error);
else if (strcmp (element_name, "child") == 0)
parse_child (data, element_name, names, values, error);
else if (strcmp (element_name, "signal") == 0)
@@ -1377,6 +1471,23 @@ end_element (GtkBuildableParseContext *context,
else
g_assert_not_reached ();
}
else if (strcmp (element_name, "binding") == 0)
{
BindingInfo *binfo = state_pop_info (data, BindingInfo);
CommonInfo *info = state_peek_info (data, CommonInfo);
g_assert (info != NULL);
/* Normal properties */
if (info->tag_type == TAG_OBJECT ||
info->tag_type == TAG_TEMPLATE)
{
ObjectInfo *object_info = (ObjectInfo*)info;
object_info->bindings = g_slist_prepend (object_info->bindings, binfo);
}
else
g_assert_not_reached ();
}
else if (strcmp (element_name, "object") == 0 ||
strcmp (element_name, "template") == 0)
{
@@ -1517,6 +1628,22 @@ text (GtkBuildableParseContext *context,
g_string_append_len (prop_info->text, text, text_len);
}
else if (strcmp (gtk_buildable_parse_context_get_element (context), "binding") == 0)
{
BindingInfo *binfo = (BindingInfo *) info;
if (binfo->source == NULL)
{
binfo->source = g_strndup (text, text_len);
}
else
{
char *s;
s = g_strdup_printf ("%s%*s", binfo->source, (guint) text_len, text);
g_free (binfo->source);
binfo->source = s;
}
}
}
static void
@@ -1531,6 +1658,10 @@ free_info (CommonInfo *info)
case TAG_CHILD:
free_child_info ((ChildInfo *)info);
break;
case TAG_BINDING:
case TAG_EXPRESSION:
_free_binding_info ((BindingInfo *)info, NULL);
break;
case TAG_PROPERTY:
free_property_info ((PropertyInfo *)info);
break;
@@ -1594,7 +1725,8 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
if (!gtk_buildable_parse_context_parse (&data.ctx, buffer, length, error))
goto out;
_gtk_builder_finish (builder);
if (!_gtk_builder_finish (builder, error))
goto out;
if (_gtk_builder_lookup_failed (builder, error))
goto out;
+7 -2
View File
@@ -24,7 +24,8 @@
enum {
TAG_PROPERTY,
TAG_MENU,
TAG_BINDING,
TAG_EXPRESSION,
TAG_REQUIRES,
TAG_OBJECT,
TAG_CHILD,
@@ -84,6 +85,7 @@ typedef struct {
typedef struct
{
guint tag_type;
GObject *target;
GParamSpec *target_pspec;
gchar *source;
@@ -175,9 +177,12 @@ void _gtk_builder_add (GtkBuilder *builder,
ChildInfo *child_info);
void _gtk_builder_add_signals (GtkBuilder *builder,
GSList *signals);
void _gtk_builder_finish (GtkBuilder *builder);
gboolean _gtk_builder_finish (GtkBuilder *builder,
GError **error);
void _free_signal_info (SignalInfo *info,
gpointer user_data);
void _free_binding_info (BindingInfo *info,
gpointer user_data);
/* Internal API which might be made public at some point */
gboolean _gtk_builder_boolean_from_string (const gchar *string,
+356
View File
@@ -0,0 +1,356 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcolumnview.h"
#include "gtkcolumnviewcolumn.h"
#include "gtkintl.h"
#include "gtklistview.h"
#include "gtkmain.h"
#include "gtkprivate.h"
#include "gtkwidgetprivate.h"
/**
* SECTION:gtkcolumnview
* @title: GtkColumnView
* @short_description: A widget for displaying lists in multiple columns
* @see_also: #GtkColumnViewColumn, #GtkTreeView
*
* GtkColumnView is a widget to present a view into a large dynamic list of items
* using multiple columns.
*/
struct _GtkColumnView
{
GtkWidget parent_instance;
GListStore *columns;
GtkListView *listview;
};
struct _GtkColumnViewClass
{
GtkWidgetClass parent_class;
};
enum
{
PROP_0,
PROP_COLUMNS,
PROP_MODEL,
PROP_SHOW_SEPARATORS,
N_PROPS
};
enum {
ACTIVATE,
LAST_SIGNAL
};
G_DEFINE_TYPE (GtkColumnView, gtk_column_view, GTK_TYPE_WIDGET)
static GParamSpec *properties[N_PROPS] = { NULL, };
static guint signals[LAST_SIGNAL] = { 0 };
static void
gtk_column_view_activate_cb (GtkListView *listview,
guint pos,
GtkColumnView *self)
{
g_signal_emit (self, signals[ACTIVATE], 0, pos);
}
static void
gtk_column_view_dispose (GObject *object)
{
GtkColumnView *self = GTK_COLUMN_VIEW (object);
g_list_store_remove_all (self->columns);
g_clear_pointer ((GtkWidget **) &self->listview, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_column_view_parent_class)->dispose (object);
}
static void
gtk_column_view_finalize (GObject *object)
{
GtkColumnView *self = GTK_COLUMN_VIEW (object);
g_object_unref (self->columns);
G_OBJECT_CLASS (gtk_column_view_parent_class)->finalize (object);
}
static void
gtk_column_view_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkColumnView *self = GTK_COLUMN_VIEW (object);
switch (property_id)
{
case PROP_COLUMNS:
g_value_set_object (value, self->columns);
break;
case PROP_MODEL:
g_value_set_object (value, gtk_list_view_get_model (self->listview));
break;
case PROP_SHOW_SEPARATORS:
g_value_set_boolean (value, gtk_list_view_get_show_separators (self->listview));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_column_view_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkColumnView *self = GTK_COLUMN_VIEW (object);
switch (property_id)
{
case PROP_MODEL:
gtk_column_view_set_model (self, g_value_get_object (value));
break;
case PROP_SHOW_SEPARATORS:
gtk_column_view_set_show_separators (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_column_view_class_init (GtkColumnViewClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = gtk_column_view_dispose;
gobject_class->finalize = gtk_column_view_finalize;
gobject_class->get_property = gtk_column_view_get_property;
gobject_class->set_property = gtk_column_view_set_property;
/**
* GtkColumnView:columns:
*
* The list of columns
*/
properties[PROP_COLUMNS] =
g_param_spec_object ("columns",
P_("Columns"),
P_("List of columns"),
G_TYPE_LIST_MODEL,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnView:model:
*
* Model for the items displayed
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("Model for the items displayed"),
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnView:show-separators:
*
* Show separators between rows
*/
properties[PROP_SHOW_SEPARATORS] =
g_param_spec_boolean ("show-separators",
P_("Show separators"),
P_("Show separators between rows"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
/**
* GtkColumnView::activate:
* @self: The #GtkColumnView
* @position: position of item to activate
*
* The ::activate signal is emitted when a row has been activated by the user,
* usually via activating the GtkListBase|list.activate-item action.
*
* This allows for a convenient way to handle activation in a columnview.
* See gtk_list_item_set_activatable() for details on how to use this signal.
*/
signals[ACTIVATE] =
g_signal_new (I_("activate"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
g_signal_set_va_marshaller (signals[ACTIVATE],
G_TYPE_FROM_CLASS (gobject_class),
g_cclosure_marshal_VOID__UINTv);
gtk_widget_class_set_css_name (widget_class, I_("columnview"));
}
static void
gtk_column_view_init (GtkColumnView *self)
{
self->columns = g_list_store_new (GTK_TYPE_COLUMN_VIEW_COLUMN);
self->listview = GTK_LIST_VIEW (gtk_list_view_new ());
g_signal_connect (self->listview, "activate", G_CALLBACK (gtk_column_view_activate_cb), self);
gtk_widget_set_parent (GTK_WIDGET (self->listview), GTK_WIDGET (self));
}
/**
* gtk_column_view_new:
*
* Creates a new empty #GtkColumnView.
*
* You most likely want to call gtk_column_view_set_factory() to
* set up a way to map its items to widgets and gtk_column_view_set_model()
* to set a model to provide items next.
*
* Returns: a new #GtkColumnView
**/
GtkWidget *
gtk_column_view_new (void)
{
return g_object_new (GTK_TYPE_COLUMN_VIEW, NULL);
}
/**
* gtk_column_view_get_model:
* @self: a #GtkColumnView
*
* Gets the model that's currently used to read the items displayed.
*
* Returns: (nullable) (transfer none): The model in use
**/
GListModel *
gtk_column_view_get_model (GtkColumnView *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
return gtk_list_view_get_model (self->listview);
}
/**
* gtk_column_view_set_model:
* @self: a #GtkColumnView
* @model: (allow-none) (transfer none): the model to use or %NULL for none
*
* Sets the #GListModel to use.
*
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
* Otherwise, @self creates a #GtkSingleSelection for the selection.
**/
void
gtk_column_view_set_model (GtkColumnView *self,
GListModel *model)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (gtk_list_view_get_model (self->listview) == model)
return;
gtk_list_view_set_model (self->listview, model);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_column_view_get_columns:
* @self: a #GtkColumnView
*
* Gets the list of columns in this column view. This list is constant over
* the lifetime of @self and can be used to monitor changes to the columns
* of @self by connecting to the GListModel:items-changed signal.
*
* Returns: (transfer none): The list managing the columns
**/
GListModel *
gtk_column_view_get_columns (GtkColumnView *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
return G_LIST_MODEL (self->columns);
}
/**
* gtk_column_view_set_show_separators:
* @self: a #GtkColumnView
* @show_separators: %TRUE to show separators
*
* Sets whether the list should show separators
* between rows.
*/
void
gtk_column_view_set_show_separators (GtkColumnView *self,
gboolean show_separators)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
if (gtk_list_view_get_show_separators (self->listview) == show_separators)
return;
gtk_list_view_set_show_separators (self->listview, show_separators);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SEPARATORS]);
}
/**
* gtk_column_view_get_show_separators:
* @self: a #GtkColumnView
*
* Returns whether the list box should show separators
* between rows.
*
* Returns: %TRUE if the list box shows separators
*/
gboolean
gtk_column_view_get_show_separators (GtkColumnView *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), FALSE);
return gtk_list_view_get_show_separators (self->listview);
}
+69
View File
@@ -0,0 +1,69 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COLUMN_VIEW_H__
#define __GTK_COLUMN_VIEW_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtklistbase.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLUMN_VIEW (gtk_column_view_get_type ())
#define GTK_COLUMN_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW, GtkColumnView))
#define GTK_COLUMN_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW, GtkColumnViewClass))
#define GTK_IS_COLUMN_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW))
#define GTK_IS_COLUMN_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW))
#define GTK_COLUMN_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW, GtkColumnViewClass))
/**
* GtkColumnView:
*
* GtkColumnView is the simple list implementation for GTK's list widgets.
*/
typedef struct _GtkColumnView GtkColumnView;
typedef struct _GtkColumnViewClass GtkColumnViewClass;
/* forward declaration */
typedef struct _GtkColumnViewColumn GtkColumnViewColumn;
GDK_AVAILABLE_IN_ALL
GType gtk_column_view_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_column_view_new (void);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_column_view_get_columns (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_column_view_get_model (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_set_model (GtkColumnView *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_get_show_separators (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_set_show_separators (GtkColumnView *self,
gboolean show_Separators);
G_END_DECLS
#endif /* __GTK_COLUMN_VIEW_H__ */
+338
View File
@@ -0,0 +1,338 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcolumnviewcolumn.h"
#include "gtkintl.h"
#include "gtklistbaseprivate.h"
#include "gtklistitemmanagerprivate.h"
#include "gtkmain.h"
#include "gtkprivate.h"
#include "gtkrbtreeprivate.h"
#include "gtkstylecontext.h"
#include "gtkwidgetprivate.h"
/**
* SECTION:gtkcolumnviewcolumn
* @title: GtkColumnViewColumn
* @short_description: The column added to GtkColumnView
* @see_also: #GtkColumnView
*
* GtkColumnViewColumn represents the columns being added to #GtkColumnView.
*/
struct _GtkColumnViewColumn
{
GObject parent_instance;
GtkListItemFactory *factory;
char *title;
/* data for the view */
GtkColumnView *view;
};
struct _GtkColumnViewColumnClass
{
GObjectClass parent_class;
};
enum
{
PROP_0,
PROP_COLUMN_VIEW,
PROP_FACTORY,
PROP_TITLE,
N_PROPS
};
G_DEFINE_TYPE (GtkColumnViewColumn, gtk_column_view_column, G_TYPE_OBJECT)
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_column_view_column_dispose (GObject *object)
{
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
g_assert (self->view == NULL); /* would hold a ref otherwise */
g_clear_object (&self->factory);
g_clear_pointer (&self->title, g_free);
G_OBJECT_CLASS (gtk_column_view_column_parent_class)->dispose (object);
}
static void
gtk_column_view_column_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
switch (property_id)
{
case PROP_COLUMN_VIEW:
g_value_set_object (value, self->view);
break;
case PROP_FACTORY:
g_value_set_object (value, self->factory);
break;
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_column_view_column_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
switch (property_id)
{
case PROP_FACTORY:
gtk_column_view_column_set_factory (self, g_value_get_object (value));
break;
case PROP_TITLE:
gtk_column_view_column_set_title (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = gtk_column_view_column_dispose;
gobject_class->get_property = gtk_column_view_column_get_property;
gobject_class->set_property = gtk_column_view_column_set_property;
/**
* GtkColumnViewColumn:column-view:
*
* #GtkColumnView this column is a part of
*/
properties[PROP_COLUMN_VIEW] =
g_param_spec_object ("column-view",
P_("Column view"),
P_("Column view this column is a part of"),
G_TYPE_LIST_MODEL,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnViewColumn:factory:
*
* Factory for populating list items
*/
properties[PROP_FACTORY] =
g_param_spec_object ("factory",
P_("Factory"),
P_("Factory for populating list items"),
GTK_TYPE_LIST_ITEM_FACTORY,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnViewColumn:title:
*
* Title displayed in the header
*/
properties[PROP_TITLE] =
g_param_spec_string ("title",
P_("Title"),
P_("Title displayed in the header"),
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_column_view_column_init (GtkColumnViewColumn *self)
{
}
/**
* gtk_column_view_column_new:
* @title: (nullable): Title to use for this column
*
* Creates a new #GtkColumnViewColumn.
*
* You most likely want to call gtk_column_add_column() next.
*
* Returns: a new #GtkColumnViewColumn
**/
GtkColumnViewColumn *
gtk_column_view_column_new (const char *title)
{
return g_object_new (GTK_TYPE_COLUMN_VIEW_COLUMN,
"title", title,
NULL);
}
/**
* gtk_column_view_column_new_with_factory:
* @title: (nullable): Title to use for this column
* @factory: (transfer full): The factory to populate items with
*
* Creates a new #GtkColumnViewColumn that uses the given @factory for
* mapping items to widgets.
*
* You most likely want to call gtk_column_add_column() next.
*
* The function takes ownership of the
* argument, so you can write code like
* ```
* column = gtk_column_view_column_new_with_factory (_("Name"),
* gtk_builder_list_item_factory_new_from_resource ("/name.ui"));
* ```
*
* Returns: a new #GtkColumnViewColumn using the given @factory
**/
GtkColumnViewColumn *
gtk_column_view_column_new_with_factory (const char *title,
GtkListItemFactory *factory)
{
GtkColumnViewColumn *result;
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
result = g_object_new (GTK_TYPE_COLUMN_VIEW_COLUMN,
"factory", factory,
"title", title,
NULL);
g_object_unref (factory);
return result;
}
/**
* gtk_column_view_column_get_column_view:
* @self: a #GtkColumnViewColumn
*
* Gets the column view that's currently displaying this column.
*
* If @self has not been added to a column view yet, %NULL is returned.
*
* Returns: (nullable) (transfer none): The column view displaying @self.
**/
GtkColumnView *
gtk_column_view_column_get_column_view (GtkColumnViewColumn *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
return self->view;
}
/**
* gtk_column_view_column_get_factory:
* @self: a #GtkColumnViewColumn
*
* Gets the factory that's currently used to populate list items for
* this column.
*
* Returns: (nullable) (transfer none): The factory in use
**/
GtkListItemFactory *
gtk_column_view_column_get_factory (GtkColumnViewColumn *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
return self->factory;
}
/**
* gtk_column_view_column_set_factory:
* @self: a #GtkColumnViewColumn
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
*
* Sets the #GtkListItemFactory to use for populating list items for this
* column.
**/
void
gtk_column_view_column_set_factory (GtkColumnViewColumn *self,
GtkListItemFactory *factory)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
if (!g_set_object (&self->factory, factory))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
}
/**
* gtk_column_view_column_set_title:
* @self: a #GtkColumnViewColumn
* @title: (nullable): Title to use for this column
*
* Sets the title of this column. The title is displayed in the header of a
* #GtkColumnView for this column and is therefor user-facing text that should
* be translated.
*/
void
gtk_column_view_column_set_title (GtkColumnViewColumn *self,
const char *title)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
if (g_strcmp0 (self->title, title) == 0)
return;
g_free (self->title);
self->title = g_strdup (title);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
}
/**
* gtk_column_view_column_get_title:
* @self: a #GtkColumnViewColumn
*
* Returns the title set with gtk_column_view_column_set_title().
*
* Returns: (nullable) The column's title
*/
const char *
gtk_column_view_column_get_title (GtkColumnViewColumn *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), FALSE);
return self->title;
}
+70
View File
@@ -0,0 +1,70 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COLUMN_VIEW_COLUMN_H__
#define __GTK_COLUMN_VIEW_COLUMN_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkcolumnview.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLUMN_VIEW_COLUMN (gtk_column_view_column_get_type ())
#define GTK_COLUMN_VIEW_COLUMN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumn))
#define GTK_COLUMN_VIEW_COLUMN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumnClass))
#define GTK_IS_COLUMN_VIEW_COLUMN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_COLUMN))
#define GTK_IS_COLUMN_VIEW_COLUMN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_COLUMN))
#define GTK_COLUMN_VIEW_COLUMN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumnClass))
/**
* GtkColumnViewColumn:
*
* GtkColumnViewColumns are added to #GtkColumnViews.
*/
typedef struct _GtkColumnViewColumnClass GtkColumnViewColumnClass;
GDK_AVAILABLE_IN_ALL
GType gtk_column_view_column_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkColumnViewColumn * gtk_column_view_column_new (const char *title);
GDK_AVAILABLE_IN_ALL
GtkColumnViewColumn * gtk_column_view_column_new_with_factory (const char *title,
GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GtkColumnView * gtk_column_view_column_get_column_view (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_factory (GtkColumnViewColumn *self,
GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GtkListItemFactory * gtk_column_view_column_get_factory (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_title (GtkColumnViewColumn *self,
const char *title);
GDK_AVAILABLE_IN_ALL
const char * gtk_column_view_column_get_title (GtkColumnViewColumn *self);
G_END_DECLS
#endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */
+700
View File
@@ -0,0 +1,700 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcoverflow.h"
#include "gtkintl.h"
#include "gtklistbaseprivate.h"
#include "gtkmain.h"
#include "gtkprivate.h"
#include "gtkrbtreeprivate.h"
#include "gtkwidgetprivate.h"
/* Extra items to display at most above + below the central item */
#define GTK_COVER_FLOW_DISPLAY_ITEMS 5
/* Number of extra space we leave around the covers */
#define GTK_COVER_FLOW_SCALE_ALONG 3
#define GTK_COVER_FLOW_SCALE_ACROSS 2
/**
* SECTION:gtkcoverflow
* @title: GtkCoverFlow
* @short_description: A coverflow list widget
* @see_also: #GListModel
*
* GtkCoverFlow is a widget to present a coverflow list
*/
struct _GtkCoverFlow
{
GtkListBase parent_instance;
int size_across;
int size_along;
};
struct _GtkCoverFlowClass
{
GtkListBaseClass parent_class;
};
enum
{
PROP_0,
PROP_FACTORY,
PROP_MODEL,
N_PROPS
};
enum {
ACTIVATE,
LAST_SIGNAL
};
G_DEFINE_TYPE (GtkCoverFlow, gtk_cover_flow, GTK_TYPE_LIST_BASE)
static GParamSpec *properties[N_PROPS] = { NULL, };
static guint signals[LAST_SIGNAL] = { 0 };
static gboolean
gtk_cover_flow_get_allocation_along (GtkListBase *base,
guint pos,
int *offset,
int *size)
{
GtkCoverFlow *self = GTK_COVER_FLOW (base);
if (offset)
*offset = pos * self->size_along;
if (size)
*size = self->size_along;
return TRUE;
}
static gboolean
gtk_cover_flow_get_allocation_across (GtkListBase *base,
guint pos,
int *offset,
int *size)
{
GtkCoverFlow *self = GTK_COVER_FLOW (base);
if (offset)
*offset = pos * self->size_across;
if (size)
*size = self->size_across;
return TRUE;
}
static guint
gtk_cover_flow_move_focus_along (GtkListBase *base,
guint pos,
int steps)
{
if (steps < 0)
return pos - MIN (pos, -steps);
else
{
pos += MIN (gtk_list_base_get_n_items (base) - pos - 1, steps);
}
return pos;
}
static guint
gtk_cover_flow_move_focus_across (GtkListBase *base,
guint pos,
int steps)
{
return pos;
}
static gboolean
gtk_cover_flow_get_position_from_allocation (GtkListBase *base,
int across,
int along,
guint *pos,
cairo_rectangle_int_t *area)
{
GtkCoverFlow *self = GTK_COVER_FLOW (base);
if (across >= self->size_across ||
along >= self->size_along * gtk_list_base_get_n_items (base))
return FALSE;
*pos = along / self->size_along;
if (area)
{
area->x = 0;
area->width = self->size_across;
area->y = *pos * self->size_along;
area->height = self->size_along;
}
return TRUE;
}
static void
gtk_cover_flow_measure_children (GtkCoverFlow *self,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural)
{
GtkListItemManagerItem *item;
int min, nat, child_min, child_nat;
min = 0;
nat = 0;
for (item = gtk_list_item_manager_get_first (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
item != NULL;
item = gtk_rb_tree_node_get_next (item))
{
/* ignore unavailable items */
if (item->widget == NULL)
continue;
gtk_widget_measure (item->widget,
orientation, for_size,
&child_min, &child_nat, NULL, NULL);
min = MAX (min, child_min);
nat = MAX (nat, child_nat);
}
*minimum = min;
*natural = nat;
}
static void
gtk_cover_flow_measure_across (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
if (for_size > 0)
for_size /= GTK_COVER_FLOW_SCALE_ALONG;
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
*minimum *= GTK_COVER_FLOW_SCALE_ACROSS;
*natural *= GTK_COVER_FLOW_SCALE_ACROSS;
}
static void
gtk_cover_flow_measure_along (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
if (for_size > 0)
for_size /= GTK_COVER_FLOW_SCALE_ACROSS;
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
*minimum *= GTK_COVER_FLOW_SCALE_ALONG;
*natural *= GTK_COVER_FLOW_SCALE_ALONG;
}
static void
gtk_cover_flow_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
if (orientation == gtk_list_base_get_orientation (GTK_LIST_BASE (self)))
gtk_cover_flow_measure_along (widget, orientation, for_size, minimum, natural);
else
gtk_cover_flow_measure_across (widget, orientation, for_size, minimum, natural);
}
static GskTransform *
transform_translate_oriented (GskTransform *transform,
GtkOrientation orientation,
GtkTextDirection dir,
float across,
float along)
{
if (orientation == GTK_ORIENTATION_VERTICAL)
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (across, along));
else if (dir == GTK_TEXT_DIR_LTR)
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (along, across));
else
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-along, across));
}
static void
gtk_cover_flow_size_allocate_child (GtkWidget *child,
GtkOrientation orientation,
GtkTextDirection dir,
GskTransform *transform,
int width,
int height)
{
if (orientation == GTK_ORIENTATION_VERTICAL)
{
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-width / 2, -height / 2));
gtk_widget_allocate (child, width, height, -1, transform);
}
else
{
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-height / 2, -width / 2));
gtk_widget_allocate (child, height, width, -1, transform);
}
}
static void
gtk_cover_flow_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
GtkOrientation orientation, opposite_orientation;
GtkTextDirection dir;
GtkListItemManagerItem *item;
GtkListItemManager *manager;
guint i, pos;
int x, y, along, across;
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
/* step 0: exit early if list is empty */
if (gtk_list_item_manager_get_root (manager) == NULL)
return;
/* step 1: determine size of children */
along = orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
across = opposite_orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
self->size_along = along / GTK_COVER_FLOW_SCALE_ALONG;
self->size_across = across / GTK_COVER_FLOW_SCALE_ACROSS;
/* step 2: update the adjustments */
gtk_list_base_update_adjustments (GTK_LIST_BASE (self),
self->size_across,
self->size_along * gtk_list_base_get_n_items (GTK_LIST_BASE (self)),
self->size_across,
self->size_along,
&x, &y);
pos = gtk_list_base_get_anchor (GTK_LIST_BASE (self));
/* step 4: actually allocate the widgets */
dir = _gtk_widget_get_direction (widget);
i = 0;
for (item = gtk_list_item_manager_get_first (manager);
item != NULL;
item = gtk_rb_tree_node_get_next (item))
{
if (item->widget)
{
/* start at the center */
GskTransform *transform = transform_translate_oriented (NULL, orientation, dir, across / 2, along / 2);
if (i == pos)
{
/* nothing to do, we're already centered */
}
else if (MAX (pos, i) - MIN (pos, i) < GTK_COVER_FLOW_DISPLAY_ITEMS) /* ABS() doesn't work with guint */
{
int diff = i - pos;
transform = gsk_transform_perspective (transform, MAX (width, height) * 2);
transform = transform_translate_oriented (transform,
orientation, dir,
0,
diff * self->size_along / 4);
transform = transform_translate_oriented (transform,
orientation, dir,
0,
(diff < 0 ? -1 : 1) * self->size_along / 2);
if (orientation == GTK_ORIENTATION_VERTICAL)
transform = gsk_transform_rotate_3d (transform,
diff > 0 ? 60 : -60,
graphene_vec3_x_axis ());
else
transform = gsk_transform_rotate_3d (transform,
diff < 0 ? 60 : -60,
graphene_vec3_y_axis ());
transform = transform_translate_oriented (transform,
orientation, dir,
0,
(diff < 0 ? 1 : -1) * self->size_along / 4);
}
else
{
transform = transform_translate_oriented (transform,
orientation, dir,
- 2 * self->size_across,
- 2 * self->size_along);
}
gtk_cover_flow_size_allocate_child (item->widget,
orientation, dir,
transform,
self->size_across,
self->size_along);
}
i += item->n_items;
}
}
static void
gtk_cover_flow_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkListBase *base = GTK_LIST_BASE (object);
switch (property_id)
{
case PROP_FACTORY:
g_value_set_object (value, gtk_list_item_manager_get_factory (gtk_list_base_get_manager (base)));
break;
case PROP_MODEL:
g_value_set_object (value, gtk_list_base_get_model (base));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_cover_flow_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkWidget *child;
GtkListItemManagerItem *item;
item = gtk_list_item_manager_get_nth (gtk_list_base_get_manager (GTK_LIST_BASE (widget)),
gtk_list_base_get_anchor (GTK_LIST_BASE (widget)),
NULL);
if (item == NULL || item->widget == NULL)
{
GTK_WIDGET_CLASS (gtk_cover_flow_parent_class)->snapshot (widget, snapshot);
return;
}
for (child = _gtk_widget_get_first_child (widget);
child != item->widget;
child = _gtk_widget_get_next_sibling (child))
gtk_widget_snapshot_child (widget, child, snapshot);
for (child = _gtk_widget_get_last_child (widget);
child != item->widget;
child = _gtk_widget_get_prev_sibling (child))
gtk_widget_snapshot_child (widget, child, snapshot);
gtk_widget_snapshot_child (widget, item->widget, snapshot);
}
static void
gtk_cover_flow_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkCoverFlow *self = GTK_COVER_FLOW (object);
switch (property_id)
{
case PROP_FACTORY:
gtk_cover_flow_set_factory (self, g_value_get_object (value));
break;
case PROP_MODEL:
gtk_cover_flow_set_model (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_cover_flow_activate_item (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
guint pos;
if (!g_variant_check_format_string (parameter, "u", FALSE))
return;
g_variant_get (parameter, "u", &pos);
if (pos >= gtk_list_base_get_n_items (GTK_LIST_BASE (self)))
return;
g_signal_emit (widget, signals[ACTIVATE], 0, pos);
}
static void
gtk_cover_flow_class_init (GtkCoverFlowClass *klass)
{
GtkListBaseClass *list_base_class = GTK_LIST_BASE_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
list_base_class->list_item_name = "cover";
list_base_class->list_item_size = sizeof (GtkListItemManagerItem);
list_base_class->list_item_augment_size = sizeof (GtkListItemManagerItemAugment);
list_base_class->list_item_augment_func = gtk_list_item_manager_augment_node;
list_base_class->get_allocation_along = gtk_cover_flow_get_allocation_along;
list_base_class->get_allocation_across = gtk_cover_flow_get_allocation_across;
list_base_class->get_position_from_allocation = gtk_cover_flow_get_position_from_allocation;
list_base_class->move_focus_along = gtk_cover_flow_move_focus_along;
list_base_class->move_focus_across = gtk_cover_flow_move_focus_across;
widget_class->measure = gtk_cover_flow_measure;
widget_class->size_allocate = gtk_cover_flow_size_allocate;
widget_class->snapshot = gtk_cover_flow_snapshot;
gobject_class->get_property = gtk_cover_flow_get_property;
gobject_class->set_property = gtk_cover_flow_set_property;
/**
* GtkCoverFlow:factory:
*
* Factory for populating list items
*/
properties[PROP_FACTORY] =
g_param_spec_object ("factory",
P_("Factory"),
P_("Factory for populating list items"),
GTK_TYPE_LIST_ITEM_FACTORY,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkCoverFlow:model:
*
* Model for the items displayed
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("Model for the items displayed"),
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
/**
* GtkCoverFlow::activate:
* @self: The #GtkCoverFlow
* @position: position of item to activate
*
* The ::activate signal is emitted when an item has been activated by the user,
* usually via activating the GtkCoverFlow|list.activate-item action.
*
* This allows for a convenient way to handle activation in a listview.
* See gtk_list_item_set_activatable() for details on how to use this signal.
*/
signals[ACTIVATE] =
g_signal_new (I_("activate"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
g_signal_set_va_marshaller (signals[ACTIVATE],
G_TYPE_FROM_CLASS (gobject_class),
g_cclosure_marshal_VOID__UINTv);
/**
* GtkCoverFlow|list.activate-item:
* @position: position of item to activate
*
* Activates the item given in @position by emitting the GtkCoverFlow::activate
* signal.
*/
gtk_widget_class_install_action (widget_class,
"list.activate-item",
"u",
gtk_cover_flow_activate_item);
gtk_widget_class_set_css_name (widget_class, I_("coverflow"));
}
static void
gtk_cover_flow_init (GtkCoverFlow *self)
{
gtk_list_base_set_anchor_max_widgets (GTK_LIST_BASE (self),
0,
GTK_COVER_FLOW_DISPLAY_ITEMS);
}
/**
* gtk_cover_flow_new:
*
* Creates a new empty #GtkCoverFlow.
*
* You most likely want to call gtk_cover_flow_set_factory() to
* set up a way to map its items to widgets and gtk_cover_flow_set_model()
* to set a model to provide items next.
*
* Returns: a new #GtkCoverFlow
**/
GtkWidget *
gtk_cover_flow_new (void)
{
return g_object_new (GTK_TYPE_COVER_FLOW, NULL);
}
/**
* gtk_cover_flow_new_with_factory:
* @factory: (transfer full): The factory to populate items with
*
* Creates a new #GtkCoverFlow that uses the given @factory for
* mapping items to widgets.
*
* You most likely want to call gtk_cover_flow_set_model() to set
* a model next.
*
* The function takes ownership of the
* argument, so you can write code like
* ```
* cover_flow = gtk_cover_flow_new_with_factory (
* gtk_builder_list_item_factory_newfrom_resource ("/resource.ui"));
* ```
*
* Returns: a new #GtkCoverFlow using the given @factory
**/
GtkWidget *
gtk_cover_flow_new_with_factory (GtkListItemFactory *factory)
{
GtkWidget *result;
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
result = g_object_new (GTK_TYPE_COVER_FLOW,
"factory", factory,
NULL);
g_object_unref (factory);
return result;
}
/**
* gtk_cover_flow_get_model:
* @self: a #GtkCoverFlow
*
* Gets the model that's currently used to read the items displayed.
*
* Returns: (nullable) (transfer none): The model in use
**/
GListModel *
gtk_cover_flow_get_model (GtkCoverFlow *self)
{
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
return gtk_list_base_get_model (GTK_LIST_BASE (self));
}
/**
* gtk_cover_flow_set_model:
* @self: a #GtkCoverFlow
* @model: (allow-none) (transfer none): the model to use or %NULL for none
*
* Sets the #GListModel to use.
*
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
* Otherwise, @self creates a #GtkSingleSelection for the selection.
**/
void
gtk_cover_flow_set_model (GtkCoverFlow *self,
GListModel *model)
{
g_return_if_fail (GTK_IS_COVER_FLOW (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (!gtk_list_base_set_model (GTK_LIST_BASE (self), model))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_cover_flow_get_factory:
* @self: a #GtkCoverFlow
*
* Gets the factory that's currently used to populate list items.
*
* Returns: (nullable) (transfer none): The factory in use
**/
GtkListItemFactory *
gtk_cover_flow_get_factory (GtkCoverFlow *self)
{
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
return gtk_list_item_manager_get_factory (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
}
/**
* gtk_cover_flow_set_factory:
* @self: a #GtkCoverFlow
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
*
* Sets the #GtkListItemFactory to use for populating list items.
**/
void
gtk_cover_flow_set_factory (GtkCoverFlow *self,
GtkListItemFactory *factory)
{
GtkListItemManager *manager;
g_return_if_fail (GTK_IS_COVER_FLOW (self));
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
if (factory == gtk_list_item_manager_get_factory (manager))
return;
gtk_list_item_manager_set_factory (manager, factory);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
}
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COVER_FLOW_H__
#define __GTK_COVER_FLOW_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtklistbase.h>
G_BEGIN_DECLS
#define GTK_TYPE_COVER_FLOW (gtk_cover_flow_get_type ())
#define GTK_COVER_FLOW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COVER_FLOW, GtkCoverFlow))
#define GTK_COVER_FLOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COVER_FLOW, GtkCoverFlowClass))
#define GTK_IS_COVER_FLOW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COVER_FLOW))
#define GTK_IS_COVER_FLOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COVER_FLOW))
#define GTK_COVER_FLOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COVER_FLOW, GtkCoverFlowClass))
/**
* GtkCoverFlow:
*
* GtkCoverFlow is the simple list implementation for GTK's list widgets.
*/
typedef struct _GtkCoverFlow GtkCoverFlow;
typedef struct _GtkCoverFlowClass GtkCoverFlowClass;
GDK_AVAILABLE_IN_ALL
GType gtk_cover_flow_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_cover_flow_new (void);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_cover_flow_new_with_factory (GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_cover_flow_get_model (GtkCoverFlow *self);
GDK_AVAILABLE_IN_ALL
void gtk_cover_flow_set_model (GtkCoverFlow *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
void gtk_cover_flow_set_factory (GtkCoverFlow *self,
GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GtkListItemFactory *
gtk_cover_flow_get_factory (GtkCoverFlow *self);
G_END_DECLS
#endif /* __GTK_COVER_FLOW_H__ */
+681
View File
@@ -0,0 +1,681 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkdirectorylist.h"
#include "gtkintl.h"
#include "gtkprivate.h"
/**
* SECTION:gtkdirectorylist
* @title: GtkDirectoryList
* @short_description: A list model for directory listings
* @see_also: #GListModel, g_file_enumerate_children()
*
* #GtkDirectoryList is a list model that wraps g_file_enumerate_children_async().
* It presents a #GListModel and fills it asynchronously with the #GFileInfos
* returned from that function.
*
* Enumeration will start automatically when a the GtkDirectoryList:file property
* is set.
*
* While the #GtkDirectoryList is being filled, the GtkDirectoryList:loading
* property will be set to %TRUE. You can listen to that property if you want
* to show information like a #GtkSpinner or a "Loading..." text.
* If loading fails at any point, the GtkDirectoryList:error property will be set
* to give more indication about the failure.
*
* The #GFileInfos returned from a #GtkDirectoryList have the "standard::file"
* attribute set to the #GFile they refer to. This way you can get at the file
* that is referred to in the same way you would via g_file_enumerator_get_child().
* This means you do not need access to the #GtkDirectoryList but can access
* the #GFile directly from the #GFileInfo when operating with a #GtkListView or
* similar.
*/
/* random number that everyone else seems to use, too */
#define FILES_PER_QUERY 100
enum {
PROP_0,
PROP_ATTRIBUTES,
PROP_ERROR,
PROP_FILE,
PROP_IO_PRIORITY,
PROP_ITEM_TYPE,
PROP_LOADING,
NUM_PROPERTIES
};
struct _GtkDirectoryList
{
GObject parent_instance;
char *attributes;
int io_priority;
GFile *file;
GCancellable *cancellable;
GError *error; /* Error while loading */
GSequence *items; /* Use GPtrArray or GListStore here? */
};
struct _GtkDirectoryListClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static GType
gtk_directory_list_get_item_type (GListModel *list)
{
return G_TYPE_FILE_INFO;
}
static guint
gtk_directory_list_get_n_items (GListModel *list)
{
GtkDirectoryList *self = GTK_DIRECTORY_LIST (list);
return g_sequence_get_length (self->items);
}
static gpointer
gtk_directory_list_get_item (GListModel *list,
guint position)
{
GtkDirectoryList *self = GTK_DIRECTORY_LIST (list);
GSequenceIter *iter;
iter = g_sequence_get_iter_at_pos (self->items, position);
if (g_sequence_iter_is_end (iter))
return NULL;
else
return g_object_ref (g_sequence_get (iter));
}
static void
gtk_directory_list_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_directory_list_get_item_type;
iface->get_n_items = gtk_directory_list_get_n_items;
iface->get_item = gtk_directory_list_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkDirectoryList, gtk_directory_list, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_directory_list_model_init))
static void
gtk_directory_list_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkDirectoryList *self = GTK_DIRECTORY_LIST (object);
switch (prop_id)
{
case PROP_ATTRIBUTES:
gtk_directory_list_set_attributes (self, g_value_get_string (value));
break;
case PROP_FILE:
gtk_directory_list_set_file (self, g_value_get_object (value));
break;
case PROP_IO_PRIORITY:
gtk_directory_list_set_io_priority (self, g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_directory_list_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkDirectoryList *self = GTK_DIRECTORY_LIST (object);
switch (prop_id)
{
case PROP_ATTRIBUTES:
g_value_set_string (value, self->attributes);
break;
case PROP_ERROR:
g_value_set_boxed (value, self->error);
break;
case PROP_FILE:
g_value_set_object (value, self->file);
break;
case PROP_IO_PRIORITY:
g_value_set_int (value, self->io_priority);
break;
case PROP_ITEM_TYPE:
g_value_set_gtype (value, G_TYPE_FILE_INFO);
break;
case PROP_LOADING:
g_value_set_boolean (value, gtk_directory_list_is_loading (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
gtk_directory_list_stop_loading (GtkDirectoryList *self)
{
if (self->cancellable == NULL)
return FALSE;
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
return TRUE;
}
static void
gtk_directory_list_dispose (GObject *object)
{
GtkDirectoryList *self = GTK_DIRECTORY_LIST (object);
gtk_directory_list_stop_loading (self);
g_clear_object (&self->file);
g_clear_pointer (&self->attributes, g_free);
g_clear_error (&self->error);
g_clear_pointer (&self->items, g_sequence_free);
G_OBJECT_CLASS (gtk_directory_list_parent_class)->dispose (object);
}
static void
gtk_directory_list_class_init (GtkDirectoryListClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_directory_list_set_property;
gobject_class->get_property = gtk_directory_list_get_property;
gobject_class->dispose = gtk_directory_list_dispose;
/**
* GtkDirectoryList:attributes:
*
* The attributes to query
*/
properties[PROP_ATTRIBUTES] =
g_param_spec_string ("attributes",
P_("attributes"),
P_("Attributes to query"),
NULL,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDirectoryList:error:
*
* Error encountered while loading files
*/
properties[PROP_ERROR] =
g_param_spec_boxed ("error",
P_("error"),
P_("Error encountered while loading files"),
G_TYPE_ERROR,
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDirectoryList:file:
*
* File to query
*/
properties[PROP_FILE] =
g_param_spec_object ("file",
P_("File"),
P_("The file to query"),
G_TYPE_FILE,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDirectoryList:io-priority:
*
* Priority used when loading
*/
properties[PROP_IO_PRIORITY] =
g_param_spec_int ("io-priority",
P_("IO priority"),
P_("Priority used when loading"),
-G_MAXINT, G_MAXINT, G_PRIORITY_DEFAULT,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDirectoryList:item-type:
*
* The #GType for elements of this object
*/
properties[PROP_ITEM_TYPE] =
g_param_spec_gtype ("item-type",
P_("Item type"),
P_("The type of elements of this object"),
G_TYPE_FILE_INFO,
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDirectoryList:loading:
*
* %TRUE if files are being loaded
*/
properties[PROP_LOADING] =
g_param_spec_boolean ("loading",
P_("loading"),
P_("TRUE if files are being loaded"),
FALSE,
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
static void
gtk_directory_list_init (GtkDirectoryList *self)
{
self->items = g_sequence_new (g_object_unref);
self->io_priority = G_PRIORITY_DEFAULT;
}
/**
* gtk_directory_list_new:
* @file: (allow-none): The file to query
* @attributes: (allow-none): The attributes to query with
*
* Creates a new #GtkDirectoryList querying the given @file with the given
* @attributes.
*
* Returns: a new #GtkDirectoryList
**/
GtkDirectoryList *
gtk_directory_list_new (const char *attributes,
GFile *file)
{
g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
return g_object_new (GTK_TYPE_DIRECTORY_LIST,
"attributes", attributes,
"file", file,
NULL);
}
static void
gtk_directory_list_clear_items (GtkDirectoryList *self)
{
guint n_items;
n_items = g_sequence_get_length (self->items);
if (n_items > 0)
{
g_sequence_remove_range (g_sequence_get_begin_iter (self->items),
g_sequence_get_end_iter (self->items));
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, 0);
}
if (self->error)
{
g_clear_error (&self->error);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ERROR]);
}
}
static void
gtk_directory_list_enumerator_closed_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_file_enumerator_close_finish (G_FILE_ENUMERATOR (source), res, NULL);
}
static void
gtk_directory_list_got_files_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
GtkDirectoryList *self = user_data; /* invalid if cancelled */
GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source);
GError *error = NULL;
GList *l, *files;
guint n;
files = g_file_enumerator_next_files_finish (enumerator, res, &error);
if (files == NULL)
{
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_clear_error (&error);
return;
}
g_file_enumerator_close_async (enumerator,
self->io_priority,
NULL,
gtk_directory_list_enumerator_closed_cb,
NULL);
g_object_freeze_notify (G_OBJECT (self));
g_clear_object (&self->cancellable);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
if (error)
{
self->error = error;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ERROR]);
}
g_object_thaw_notify (G_OBJECT (self));
return;
}
n = 0;
for (l = files; l; l = l->next)
{
GFileInfo *info;
GFile *file;
info = l->data;
file = g_file_enumerator_get_child (enumerator, info);
g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file));
g_object_unref (file);
g_sequence_append (self->items, info);
n++;
}
g_list_free (files);
g_file_enumerator_next_files_async (enumerator,
g_file_is_native (self->file) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
self->io_priority,
self->cancellable,
gtk_directory_list_got_files_cb,
self);
if (n > 0)
g_list_model_items_changed (G_LIST_MODEL (self), g_sequence_get_length (self->items) - n, 0, n);
}
static void
gtk_directory_list_got_enumerator_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
GtkDirectoryList *self = user_data; /* invalid if cancelled */
GFile *file = G_FILE (source);
GFileEnumerator *enumerator;
GError *error = NULL;
enumerator = g_file_enumerate_children_finish (file, res, &error);
if (enumerator == NULL)
{
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_clear_error (&error);
return;
}
g_object_freeze_notify (G_OBJECT (self));
self->error = error;
g_clear_object (&self->cancellable);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ERROR]);
g_object_thaw_notify (G_OBJECT (self));
return;
}
g_file_enumerator_next_files_async (enumerator,
g_file_is_native (file) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
self->io_priority,
self->cancellable,
gtk_directory_list_got_files_cb,
self);
g_object_unref (enumerator);
}
static void
gtk_directory_list_start_loading (GtkDirectoryList *self)
{
gboolean was_loading;
was_loading = gtk_directory_list_stop_loading (self);
gtk_directory_list_clear_items (self);
if (self->file == NULL)
{
if (was_loading)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
return;
}
self->cancellable = g_cancellable_new ();
g_file_enumerate_children_async (self->file,
self->attributes,
G_FILE_QUERY_INFO_NONE,
self->io_priority,
self->cancellable,
gtk_directory_list_got_enumerator_cb,
self);
if (!was_loading)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
}
/**
* gtk_directory_list_set_file:
* @self: a #GtkDirectoryList
* @file: (allow-none): the #GFile to be enumerated
*
* Sets the @file to be enumerated and starts the enumeration.
*
* If @file is %NULL, the result will be an empty list.
*/
void
gtk_directory_list_set_file (GtkDirectoryList *self,
GFile *file)
{
g_return_if_fail (GTK_IS_DIRECTORY_LIST (self));
g_return_if_fail (file == NULL || G_IS_FILE (file));
if (self->file == file ||
(self->file && file && g_file_equal (self->file, file)))
return;
g_object_freeze_notify (G_OBJECT (self));
g_set_object (&self->file, file);
gtk_directory_list_start_loading (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILE]);
g_object_thaw_notify (G_OBJECT (self));
}
/**
* gtk_directory_list_get_file:
* @self: a #GtkDirectoryList
*
* Gets the file whose children are currently enumerated.
*
* Returns: (nullable) (transfer none): The file whose children are enumerated
**/
GFile *
gtk_directory_list_get_file (GtkDirectoryList *self)
{
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), NULL);
return self->file;
}
/**
* gtk_directory_list_set_attributes:
* @self: a #GtkDirectoryList
* @attributes: (allow-none): the attributes to enumerate
*
* Sets the @attributes to be enumerated and starts the enumeration.
*
* If @attributes is %NULL, no attributes will be queried, but a list
* of #GFileInfos will still be created.
*/
void
gtk_directory_list_set_attributes (GtkDirectoryList *self,
const char *attributes)
{
g_return_if_fail (GTK_IS_DIRECTORY_LIST (self));
if (self->attributes == attributes)
return;
g_object_freeze_notify (G_OBJECT (self));
g_free (self->attributes);
self->attributes = g_strdup (attributes);
gtk_directory_list_start_loading (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ATTRIBUTES]);
g_object_thaw_notify (G_OBJECT (self));
}
/**
* gtk_directory_list_get_attributes:
* @self: a #GtkDirectoryList
*
* Gets the attributes queried on the children.
*
* Returns: (nullable) (transfer none): The queried attributes
*/
const char *
gtk_directory_list_get_attributes (GtkDirectoryList *self)
{
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), NULL);
return self->attributes;
}
/**
* gtk_directory_list_set_io_priority:
* @self: a #GtkDirectoryList
* @io_priority: IO priority to use
*
* Sets the IO priority to use while loading directories.
*
* Setting the priority while @self is loading will reprioritize the
* ongoing load as soon as possible.
*
* The default IO priority is %G_PRIORITY_DEFAULT, which is higher than
* the GTK redraw priority. If you are loading a lot of directories in
* parrallel, lowering it to something like %G_PRIORITY_DEFAULT_IDLE
* may increase responsiveness.
*/
void
gtk_directory_list_set_io_priority (GtkDirectoryList *self,
int io_priority)
{
g_return_if_fail (GTK_IS_DIRECTORY_LIST (self));
if (self->io_priority == io_priority)
return;
self->io_priority = io_priority;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_IO_PRIORITY]);
}
/**
* gtk_directory_list_get_io_priority:
* @self: a #GtkDirectoryList
*
* Gets the IO priority set via gtk_directory_list_set_io_priority().
*
* Returns: The IO priority.
*/
int
gtk_directory_list_get_io_priority (GtkDirectoryList *self)
{
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), G_PRIORITY_DEFAULT);
return self->io_priority;
}
/**
* gtk_directory_list_is_loading:
* @self: a #GtkDirectoryList
*
* Returns %TRUE if the children enumeration is currently in
* progress.
* Files will be added to @self from time to time while loading is
* going on. The order in which are added is undefined and may change
* inbetween runs.
*
* Returns: %TRUE if @self is loading
*/
gboolean
gtk_directory_list_is_loading (GtkDirectoryList *self)
{
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), FALSE);
return self->cancellable != NULL;
}
/**
* gtk_directory_list_get_error:
* @self: a #GtkDirectoryList
*
* Gets the loading error, if any.
*
* If an error occurs during the loading process, the loading process
* will finish and this property allows querying the error that happened.
* This error will persist until a file is loaded again.
*
* An error being set does not mean that no files were loaded, and all
* successfully queried files will remain in the list.
*
* Returns: (nullable) (transfer none): The loading error or %NULL if
* loading finished successfully.
*/
const GError *
gtk_directory_list_get_error (GtkDirectoryList *self)
{
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), FALSE);
return self->error;
}
+67
View File
@@ -0,0 +1,67 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_DIRECTORY_LIST_H__
#define __GTK_DIRECTORY_LIST_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gio/gio.h>
/* for GDK_AVAILABLE_IN_ALL */
#include <gdk/gdk.h>
G_BEGIN_DECLS
#define GTK_TYPE_DIRECTORY_LIST (gtk_directory_list_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkDirectoryList, gtk_directory_list, GTK, DIRECTORY_LIST, GObject)
GDK_AVAILABLE_IN_ALL
GtkDirectoryList * gtk_directory_list_new (const char *attributes,
GFile *file);
GDK_AVAILABLE_IN_ALL
void gtk_directory_list_set_file (GtkDirectoryList *self,
GFile *file);
GDK_AVAILABLE_IN_ALL
GFile * gtk_directory_list_get_file (GtkDirectoryList *self);
GDK_AVAILABLE_IN_ALL
void gtk_directory_list_set_attributes (GtkDirectoryList *self,
const char *attributes);
GDK_AVAILABLE_IN_ALL
const char * gtk_directory_list_get_attributes (GtkDirectoryList *self);
GDK_AVAILABLE_IN_ALL
void gtk_directory_list_set_io_priority (GtkDirectoryList *self,
int io_priority);
GDK_AVAILABLE_IN_ALL
int gtk_directory_list_get_io_priority (GtkDirectoryList *self);
GDK_AVAILABLE_IN_ALL
gboolean gtk_directory_list_is_loading (GtkDirectoryList *self);
GDK_AVAILABLE_IN_ALL
const GError * gtk_directory_list_get_error (GtkDirectoryList *self);
G_END_DECLS
#endif /* __GTK_DIRECTORY_LIST_H__ */
+1699
View File
File diff suppressed because it is too large Load Diff
+54
View File
@@ -0,0 +1,54 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_EXPRESSION_PRIVATE_H__
#define __GTK_EXPRESSION_PRIVATE_H__
#include <gtk/gtktypes.h>
G_BEGIN_DECLS
typedef struct _GtkExpression GtkExpression;
#define GTK_IS_EXPRESSION(expr) ((expr) != NULL)
GtkExpression * gtk_expression_new_assign (GObject *object,
const char *property,
GtkExpression *expr);
GtkExpression * gtk_expression_ref (GtkExpression *self);
void gtk_expression_unref (GtkExpression *self);
void gtk_expression_print (GtkExpression *self,
GString *string);
char * gtk_expression_to_string (GtkExpression *self);
GtkExpression * gtk_expression_parse (GtkBuilder *scope,
const char *string,
GError **error);
GType gtk_expression_get_value_type (GtkExpression *self);
gboolean gtk_expression_evaluate (GtkExpression *self,
GValue *value);
char * gtk_expression_value_to_string (const GValue *value);
gboolean gtk_expression_value_to_boolean (const GValue *value);
G_END_DECLS
#endif /* __GTK_EXPRESSION_PRIVATE_H__ */
+135
View File
@@ -0,0 +1,135 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkfunctionslistitemfactory.h"
#include "gtklistitemfactoryprivate.h"
#include "gtklistitemprivate.h"
struct _GtkFunctionsListItemFactory
{
GtkListItemFactory parent_instance;
GtkListItemSetupFunc setup_func;
GtkListItemBindFunc bind_func;
gpointer user_data;
GDestroyNotify user_destroy;
};
struct _GtkFunctionsListItemFactoryClass
{
GtkListItemFactoryClass parent_class;
};
G_DEFINE_TYPE (GtkFunctionsListItemFactory, gtk_functions_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
static void
gtk_functions_list_item_factory_setup (GtkListItemFactory *factory,
GtkListItem *list_item)
{
GtkFunctionsListItemFactory *self = GTK_FUNCTIONS_LIST_ITEM_FACTORY (factory);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_functions_list_item_factory_parent_class)->setup (factory, list_item);
if (self->setup_func)
self->setup_func (list_item, self->user_data);
}
static void
gtk_functions_list_item_factory_bind (GtkListItemFactory *factory,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected)
{
GtkFunctionsListItemFactory *self = GTK_FUNCTIONS_LIST_ITEM_FACTORY (factory);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_functions_list_item_factory_parent_class)->bind (factory, list_item, position, item, selected);
if (self->bind_func)
self->bind_func (list_item, self->user_data);
}
static void
gtk_functions_list_item_factory_rebind (GtkListItemFactory *factory,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected)
{
GtkFunctionsListItemFactory *self = GTK_FUNCTIONS_LIST_ITEM_FACTORY (factory);
GTK_LIST_ITEM_FACTORY_CLASS (gtk_functions_list_item_factory_parent_class)->bind (factory, list_item, position, item, selected);
if (self->bind_func)
self->bind_func (list_item, self->user_data);
}
static void
gtk_functions_list_item_factory_finalize (GObject *object)
{
GtkFunctionsListItemFactory *self = GTK_FUNCTIONS_LIST_ITEM_FACTORY (object);
if (self->user_destroy)
self->user_destroy (self->user_data);
G_OBJECT_CLASS (gtk_functions_list_item_factory_parent_class)->finalize (object);
}
static void
gtk_functions_list_item_factory_class_init (GtkFunctionsListItemFactoryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
object_class->finalize = gtk_functions_list_item_factory_finalize;
factory_class->setup = gtk_functions_list_item_factory_setup;
factory_class->bind = gtk_functions_list_item_factory_bind;
factory_class->rebind = gtk_functions_list_item_factory_rebind;
}
static void
gtk_functions_list_item_factory_init (GtkFunctionsListItemFactory *self)
{
}
GtkListItemFactory *
gtk_functions_list_item_factory_new (GtkListItemSetupFunc setup_func,
GtkListItemBindFunc bind_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
GtkFunctionsListItemFactory *self;
g_return_val_if_fail (setup_func || bind_func, NULL);
g_return_val_if_fail (user_data != NULL || user_destroy == NULL, NULL);
self = g_object_new (GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, NULL);
self->setup_func = setup_func;
self->bind_func = bind_func;
self->user_data = user_data;
self->user_destroy = user_destroy;
return GTK_LIST_ITEM_FACTORY (self);
}
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_FUNCTIONS_LIST_ITEM_FACTORY_H__
#define __GTK_FUNCTIONS_LIST_ITEM_FACTORY_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtklistitemfactory.h>
#include <gtk/gtklistitem.h>
G_BEGIN_DECLS
#define GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY (gtk_functions_list_item_factory_get_type ())
#define GTK_FUNCTIONS_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, GtkFunctionsListItemFactory))
#define GTK_FUNCTIONS_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, GtkFunctionsListItemFactoryClass))
#define GTK_IS_FUNCTIONS_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY))
#define GTK_IS_FUNCTIONS_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY))
#define GTK_FUNCTIONS_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, GtkFunctionsListItemFactoryClass))
typedef struct _GtkFunctionsListItemFactory GtkFunctionsListItemFactory;
typedef struct _GtkFunctionsListItemFactoryClass GtkFunctionsListItemFactoryClass;
/**
* GtkListItemSetupFunc:
* @item: the #GtkListItem to set up
* @user_data: (closure): user data
*
* Called whenever a new list item needs to be setup for managing a row in
* the list.
*
* At this point, the list item is not bound yet, so gtk_list_item_get_item()
* will return %NULL.
* The list item will later be bound to an item via the #GtkListItemBindFunc.
*/
typedef void (* GtkListItemSetupFunc) (GtkListItem *item, gpointer user_data);
/**
* GtkListItemBindFunc:
* @item: the #GtkListItem to bind
* @user_data: (closure): user data
*
* Binds a#GtkListItem previously set up via a #GtkListItemSetupFunc to
* an @item.
*
* Rebinding a @item to different @items is supported as well as
* unbinding it by setting @item to %NULL.
*/
typedef void (* GtkListItemBindFunc) (GtkListItem *item,
gpointer user_data);
GDK_AVAILABLE_IN_ALL
GType gtk_functions_list_item_factory_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkListItemFactory * gtk_functions_list_item_factory_new (GtkListItemSetupFunc setup_func,
GtkListItemBindFunc bind_func,
gpointer user_data,
GDestroyNotify user_destroy);
G_END_DECLS
#endif /* __GTK_FUNCTIONS_LIST_ITEM_FACTORY_H__ */
+1292
View File
File diff suppressed because it is too large Load Diff
+80
View File
@@ -0,0 +1,80 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_GRID_VIEW_H__
#define __GTK_GRID_VIEW_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtklistbase.h>
G_BEGIN_DECLS
#define GTK_TYPE_GRID_VIEW (gtk_grid_view_get_type ())
#define GTK_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_GRID_VIEW, GtkGridView))
#define GTK_GRID_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_GRID_VIEW, GtkGridViewClass))
#define GTK_IS_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_GRID_VIEW))
#define GTK_IS_GRID_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_GRID_VIEW))
#define GTK_GRID_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_GRID_VIEW, GtkGridViewClass))
/**
* GtkGridView:
*
* GtkGridView is a list widget implementation that arranges its items in
* a grid.
*/
typedef struct _GtkGridView GtkGridView;
typedef struct _GtkGridViewClass GtkGridViewClass;
GDK_AVAILABLE_IN_ALL
GType gtk_grid_view_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_grid_view_new (void);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_grid_view_new_with_factory (GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_grid_view_get_model (GtkGridView *self);
GDK_AVAILABLE_IN_ALL
void gtk_grid_view_set_model (GtkGridView *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
void gtk_grid_view_set_factory (GtkGridView *self,
GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GtkListItemFactory *
gtk_grid_view_get_factory (GtkGridView *self);
GDK_AVAILABLE_IN_ALL
guint gtk_grid_view_get_min_columns (GtkGridView *self);
GDK_AVAILABLE_IN_ALL
void gtk_grid_view_set_min_columns (GtkGridView *self,
guint min_columns);
GDK_AVAILABLE_IN_ALL
guint gtk_grid_view_get_max_columns (GtkGridView *self);
GDK_AVAILABLE_IN_ALL
void gtk_grid_view_set_max_columns (GtkGridView *self,
guint max_columns);
G_END_DECLS
#endif /* __GTK_GRID_VIEW_H__ */
+1542
View File
File diff suppressed because it is too large Load Diff
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_LIST_BASE_H__
#define __GTK_LIST_BASE_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkwidget.h>
#include <gtk/gtklistitem.h>
G_BEGIN_DECLS
#define GTK_TYPE_LIST_BASE (gtk_list_base_get_type ())
#define GTK_LIST_BASE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_BASE, GtkListBase))
#define GTK_LIST_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_BASE, GtkListBaseClass))
#define GTK_IS_LIST_BASE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_BASE))
#define GTK_IS_LIST_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_BASE))
#define GTK_LIST_BASE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_BASE, GtkListBaseClass))
/**
* GtkListBase:
*
* GtkListBase is the abstract base class for GTK's list widgets.
*/
typedef struct _GtkListBase GtkListBase;
typedef struct _GtkListBaseClass GtkListBaseClass;
GDK_AVAILABLE_IN_ALL
GType gtk_list_base_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __GTK_LIST_BASE_H__ */
+103
View File
@@ -0,0 +1,103 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_LIST_BASE_PRIVATE_H__
#define __GTK_LIST_BASE_PRIVATE_H__
#include "gtklistbase.h"
#include "gtklistitemmanagerprivate.h"
#include "gtkprivate.h"
struct _GtkListBase
{
GtkWidget parent_instance;
};
struct _GtkListBaseClass
{
GtkWidgetClass parent_class;
const char * list_item_name;
gsize list_item_size;
gsize list_item_augment_size;
GtkRbTreeAugmentFunc list_item_augment_func;
void (* adjustment_value_changed) (GtkListBase *self,
GtkOrientation orientation);
gboolean (* get_allocation_along) (GtkListBase *self,
guint pos,
int *offset,
int *size);
gboolean (* get_allocation_across) (GtkListBase *self,
guint pos,
int *offset,
int *size);
gboolean (* get_position_from_allocation) (GtkListBase *self,
int across,
int along,
guint *pos,
cairo_rectangle_int_t *area);
guint (* move_focus_along) (GtkListBase *self,
guint pos,
int steps);
guint (* move_focus_across) (GtkListBase *self,
guint pos,
int steps);
};
GtkOrientation gtk_list_base_get_orientation (GtkListBase *self);
#define gtk_list_base_get_opposite_orientation(self) OPPOSITE_ORIENTATION(gtk_list_base_get_orientation(self))
guint gtk_list_base_get_focus_position (GtkListBase *self);
GtkListItemManager * gtk_list_base_get_manager (GtkListBase *self);
GtkScrollablePolicy gtk_list_base_get_scroll_policy (GtkListBase *self,
GtkOrientation orientation);
guint gtk_list_base_get_n_items (GtkListBase *self);
GListModel * gtk_list_base_get_model (GtkListBase *self);
gboolean gtk_list_base_set_model (GtkListBase *self,
GListModel *model);
void gtk_list_base_update_adjustments (GtkListBase *self,
int total_across,
int total_along,
int page_across,
int page_along,
int *across,
int *along);
guint gtk_list_base_get_anchor (GtkListBase *self);
void gtk_list_base_set_anchor (GtkListBase *self,
guint anchor_pos,
double anchor_align_across,
GtkPackType anchor_side_across,
double anchor_align_along,
GtkPackType anchor_side_along);
void gtk_list_base_set_anchor_max_widgets (GtkListBase *self,
guint n_center,
guint n_above_below);
void gtk_list_base_select_item (GtkListBase *self,
guint pos,
gboolean modify,
gboolean extend);
gboolean gtk_list_base_grab_focus_on_item (GtkListBase *self,
guint pos,
gboolean select,
gboolean modify,
gboolean extend);
#endif /* __GTK_LIST_BASE_PRIVATE_H__ */
+727
View File
@@ -0,0 +1,727 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtklistitemprivate.h"
#include "gtkbindings.h"
#include "gtkcssnodeprivate.h"
#include "gtkeventcontrollerkey.h"
#include "gtkgestureclick.h"
#include "gtkintl.h"
#include "gtkmain.h"
#include "gtkwidgetprivate.h"
/**
* SECTION:gtklistitem
* @title: GtkListItem
* @short_description: Widget used to represent items of a ListModel
* @see_also: #GtkListView, #GListModel
*
* #GtkListItem is the widget that GTK list-handling containers such
* as #GtkListView create to represent items in a #GListModel.
* They are managed by the container and cannot be created by application
* code.
*
* #GtkListIems are container widgets that need to be populated by
* application code. The container provides functions to do that.
*
* #GtkListItems exist in 2 stages:
*
* 1. The unbound stage where the listitem is not currently connected to
* an item in the list. In that case, the GtkListItem:item property is
* set to %NULL.
*
* 2. The bound stage where the listitem references an item from the list.
* The GtkListItem:item property is not %NULL.
*/
struct _GtkListItem
{
GtkBin parent_instance;
GObject *item;
guint position;
guint activatable : 1;
guint selectable : 1;
guint selected : 1;
};
struct _GtkListItemClass
{
GtkBinClass parent_class;
void (* activate_signal) (GtkListItem *self);
};
enum
{
PROP_0,
PROP_ACTIVATABLE,
PROP_ITEM,
PROP_POSITION,
PROP_SELECTABLE,
PROP_SELECTED,
N_PROPS
};
enum
{
ACTIVATE_SIGNAL,
LAST_SIGNAL
};
G_DEFINE_TYPE (GtkListItem, gtk_list_item, GTK_TYPE_BIN)
static GParamSpec *properties[N_PROPS] = { NULL, };
static guint signals[LAST_SIGNAL] = { 0 };
static void
gtk_list_item_activate_signal (GtkListItem *self)
{
if (!self->activatable)
return;
gtk_widget_activate_action (GTK_WIDGET (self),
"list.activate-item",
"u",
self->position);
}
static gboolean
gtk_list_item_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkListItem *self = GTK_LIST_ITEM (widget);
GtkWidget *child;
/* The idea of this function is the following:
* 1. If any child can take focus, do not ever attempt
* to take focus.
* 2. Otherwise, if this item is selectable or activatable,
* allow focusing this widget.
*
* This makes sure every item in a list is focusable for
* activation and selection handling, but no useless widgets
* get focused and moving focus is as fast as possible.
*/
child = gtk_bin_get_child (GTK_BIN (self));
if (child)
{
if (gtk_widget_get_focus_child (widget))
return FALSE;
if (gtk_widget_child_focus (child, direction))
return TRUE;
}
if (gtk_widget_is_focus (widget))
return FALSE;
if (!gtk_widget_get_can_focus (widget) ||
!self->selectable)
return FALSE;
return gtk_widget_grab_focus (widget);
}
static gboolean
gtk_list_item_grab_focus (GtkWidget *widget)
{
GtkWidget *child;
child = gtk_bin_get_child (GTK_BIN (widget));
if (child && gtk_widget_grab_focus (child))
return TRUE;
return GTK_WIDGET_CLASS (gtk_list_item_parent_class)->grab_focus (widget);
}
static void
gtk_list_item_dispose (GObject *object)
{
GtkListItem *self = GTK_LIST_ITEM (object);
g_assert (self->item == NULL);
G_OBJECT_CLASS (gtk_list_item_parent_class)->dispose (object);
}
static void
gtk_list_item_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkListItem *self = GTK_LIST_ITEM (object);
switch (property_id)
{
case PROP_ACTIVATABLE:
g_value_set_boolean (value, self->activatable);
break;
case PROP_ITEM:
g_value_set_object (value, self->item);
break;
case PROP_POSITION:
g_value_set_uint (value, self->position);
break;
case PROP_SELECTABLE:
g_value_set_boolean (value, self->selectable);
break;
case PROP_SELECTED:
g_value_set_boolean (value, self->selected);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_list_item_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkListItem *self = GTK_LIST_ITEM (object);
switch (property_id)
{
case PROP_ACTIVATABLE:
gtk_list_item_set_activatable (self, g_value_get_boolean (value));
break;
case PROP_SELECTABLE:
gtk_list_item_set_selectable (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_list_item_select_action (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
{
GtkListItem *self = GTK_LIST_ITEM (widget);
gboolean modify, extend;
if (!self->selectable)
return;
g_variant_get (parameter, "(bb)", &modify, &extend);
gtk_widget_activate_action (GTK_WIDGET (self),
"list.select-item",
"(ubb)",
self->position, modify, extend);
}
static void
gtk_list_item_class_init (GtkListItemClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkBindingSet *binding_set;
klass->activate_signal = gtk_list_item_activate_signal;
widget_class->focus = gtk_list_item_focus;
widget_class->grab_focus = gtk_list_item_grab_focus;
gobject_class->dispose = gtk_list_item_dispose;
gobject_class->get_property = gtk_list_item_get_property;
gobject_class->set_property = gtk_list_item_set_property;
/**
* GtkListItem:activatable:
*
* If the item can be activated by the user
*/
properties[PROP_ACTIVATABLE] =
g_param_spec_boolean ("activatable",
P_("Activatable"),
P_("If the item can be activated by the user"),
TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkListItem:item:
*
* Displayed item
*/
properties[PROP_ITEM] =
g_param_spec_object ("item",
P_("Item"),
P_("Displayed item"),
G_TYPE_OBJECT,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkListItem:position:
*
* Position in the item
*/
properties[PROP_POSITION] =
g_param_spec_uint ("position",
P_("Position"),
P_("Position of the item"),
0, G_MAXUINT, 0,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkListItem:selectable:
*
* If the item can be selected by the user
*/
properties[PROP_SELECTABLE] =
g_param_spec_boolean ("selectable",
P_("Selectable"),
P_("If the item can be selected by the user"),
TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkListItem:selected:
*
* If the item is currently selected
*/
properties[PROP_SELECTED] =
g_param_spec_boolean ("selected",
P_("Selected"),
P_("If the item is currently selected"),
FALSE,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
/**
* GtkListItem::activate-signal:
*
* This is a keybinding signal, which will cause this row to be activated.
*
* Do not use it, it is an implementation detail.
*
* If you want to be notified when the user activates a listitem (by key or not),
* look at the list widget this item is contained in.
*/
signals[ACTIVATE_SIGNAL] =
g_signal_new (I_("activate-keybinding"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkListItemClass, activate_signal),
NULL, NULL,
NULL,
G_TYPE_NONE, 0);
widget_class->activate_signal = signals[ACTIVATE_SIGNAL];
/**
* GtkListItem|listitem.select:
* @modify: %TRUE to toggle the existing selection, %FALSE to select
* @extend: %TRUE to extend the selection
*
* Changes selection if the item is selectable.
* If the item is not selectable, nothing happens.
*
* This function will emit the list.select-item action and the resulting
* behavior, in particular the interpretation of @modify and @extend
* depends on the view containing this listitem. See for example
* GtkListView|list.select-item or GtkGridView|list.select-item.
*/
gtk_widget_class_install_action (widget_class,
"listitem.select",
"(bb)",
gtk_list_item_select_action);
binding_set = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0,
"activate-keybinding", 0);
gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0,
"activate-keybinding", 0);
gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0,
"activate-keybinding", 0);
/* note that some of these may get overwritten by child widgets,
* such as GtkTreeExpander */
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, 0,
"listitem.select", "(bb)", TRUE, FALSE);
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
"listitem.select", "(bb)", TRUE, FALSE);
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, GDK_SHIFT_MASK,
"listitem.select", "(bb)", TRUE, FALSE);
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
"listitem.select", "(bb)", TRUE, FALSE);
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, 0,
"listitem.select", "(bb)", TRUE, FALSE);
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
"listitem.select", "(bb)", TRUE, FALSE);
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, GDK_SHIFT_MASK,
"listitem.select", "(bb)", TRUE, FALSE);
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
"listitem.select", "(bb)", TRUE, FALSE);
/* This gets overwritten by gtk_list_item_new() but better safe than sorry */
gtk_widget_class_set_css_name (widget_class, I_("row"));
}
static void
gtk_list_item_click_gesture_pressed (GtkGestureClick *gesture,
int n_press,
double x,
double y,
GtkListItem *self)
{
GtkWidget *widget = GTK_WIDGET (self);
if (!self->selectable && !self->activatable)
{
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
return;
}
if (self->selectable)
{
GdkModifierType state;
GdkModifierType mask;
gboolean extend = FALSE, modify = FALSE;
if (gtk_get_current_event_state (&state))
{
mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION);
if ((state & mask) == mask)
modify = TRUE;
mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION);
if ((state & mask) == mask)
extend = TRUE;
}
gtk_widget_activate_action (GTK_WIDGET (self),
"list.select-item",
"(ubb)",
self->position, modify, extend);
}
if (self->activatable)
{
if (n_press == 2)
{
gtk_widget_activate_action (GTK_WIDGET (self),
"list.activate-item",
"u",
self->position);
}
}
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE);
if (gtk_widget_get_focus_on_click (widget))
gtk_widget_grab_focus (widget);
}
static void
gtk_list_item_focus_changed_cb (GtkEventControllerKey *controller,
GParamSpec *psepc,
GtkListItem *self)
{
GtkWidget *widget = GTK_WIDGET (self);
if (gtk_event_controller_key_contains_focus (controller))
{
gtk_widget_activate_action (widget,
"list.scroll-to-item",
"u",
self->position);
}
}
static void
gtk_list_item_click_gesture_released (GtkGestureClick *gesture,
int n_press,
double x,
double y,
GtkListItem *self)
{
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
}
static void
gtk_list_item_click_gesture_canceled (GtkGestureClick *gesture,
GdkEventSequence *sequence,
GtkListItem *self)
{
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
}
static void
gtk_list_item_init (GtkListItem *self)
{
GtkEventController *controller;
GtkGesture *gesture;
self->selectable = TRUE;
self->activatable = TRUE;
gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
gesture = gtk_gesture_click_new ();
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
GTK_PHASE_BUBBLE);
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture),
FALSE);
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
GDK_BUTTON_PRIMARY);
g_signal_connect (gesture, "pressed",
G_CALLBACK (gtk_list_item_click_gesture_pressed), self);
g_signal_connect (gesture, "released",
G_CALLBACK (gtk_list_item_click_gesture_released), self);
g_signal_connect (gesture, "cancel",
G_CALLBACK (gtk_list_item_click_gesture_canceled), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
controller = gtk_event_controller_key_new ();
g_signal_connect (controller, "notify::contains-focus", G_CALLBACK (gtk_list_item_focus_changed_cb), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
}
GtkListItem *
gtk_list_item_new (const char *css_name)
{
GtkListItem *result;
g_return_val_if_fail (css_name != NULL, NULL);
result = g_object_new (GTK_TYPE_LIST_ITEM,
"css-name", css_name,
NULL);
return result;
}
/**
* gtk_list_item_get_item:
* @self: a #GtkListItem
*
* Gets the item that is currently displayed in model that @self is
* currently bound to or %NULL if @self is unbound.
*
* Returns: (nullable) (transfer none) (type GObject): The item displayed
**/
gpointer
gtk_list_item_get_item (GtkListItem *self)
{
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), NULL);
return self->item;
}
/**
* gtk_list_item_get_position:
* @self: a #GtkListItem
*
* Gets the position in the model that @self currently displays.
* If @self is unbound, 0 is returned.
*
* Returns: The position of this item
**/
guint
gtk_list_item_get_position (GtkListItem *self)
{
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), 0);
return self->position;
}
void
gtk_list_item_set_item (GtkListItem *self,
gpointer item)
{
g_return_if_fail (GTK_IS_LIST_ITEM (self));
g_return_if_fail (item == NULL || G_IS_OBJECT (item));
if (self->item == item)
return;
g_clear_object (&self->item);
if (item)
self->item = g_object_ref (item);
gtk_css_node_invalidate (gtk_widget_get_css_node (GTK_WIDGET (self)), GTK_CSS_CHANGE_ANIMATIONS);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
}
void
gtk_list_item_set_position (GtkListItem *self,
guint position)
{
g_return_if_fail (GTK_IS_LIST_ITEM (self));
if (self->position == position)
return;
self->position = position;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_POSITION]);
}
/**
* gtk_list_item_get_selected:
* @self: a #GtkListItem
*
* Checks if the item is displayed as selected. The selected state is
* maintained by the container and its list model and cannot be set
* otherwise.
*
* Returns: %TRUE if the item is selected.
**/
gboolean
gtk_list_item_get_selected (GtkListItem *self)
{
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
return self->selected;
}
void
gtk_list_item_set_selected (GtkListItem *self,
gboolean selected)
{
g_return_if_fail (GTK_IS_LIST_ITEM (self));
if (self->selected == selected)
return;
self->selected = selected;
if (selected)
gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED, FALSE);
else
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
}
/**
* gtk_list_item_get_selectable:
* @self: a #GtkListItem
*
* Checks if a list item has been set to be selectable via
* gtk_list_item_set_selectable().
*
* Do not confuse this function with gtk_list_item_get_selected().
*
* Returns: %TRUE if the item is selectable
**/
gboolean
gtk_list_item_get_selectable (GtkListItem *self)
{
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
return self->selectable;
}
/**
* gtk_list_item_set_selectable:
* @self: a #GtkListItem
* @selectable: if the item should be selectable
*
* Sets @self to be selectable. If an item is selectable, clicking
* on the item or using the keyboard will try to select or unselect
* the item. If this succeeds is up to the model to determine, as
* it is managing the selected state.
*
* Note that this means that making an item non-selectable has no
* influence on the selected state at all. A non-selectable item
* may still be selected.
*
* By default, list items are selectable. When rebinding them to
* a new item, they will also be reset to be selectable by GTK.
**/
void
gtk_list_item_set_selectable (GtkListItem *self,
gboolean selectable)
{
g_return_if_fail (GTK_IS_LIST_ITEM (self));
if (self->selectable == selectable)
return;
self->selectable = selectable;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTABLE]);
}
/**
* gtk_list_item_get_activatable:
* @self: a #GtkListItem
*
* Checks if a list item has been set to be activatable via
* gtk_list_item_set_activatable().
*
* Returns: %TRUE if the item is activatable
**/
gboolean
gtk_list_item_get_activatable (GtkListItem *self)
{
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
return self->activatable;
}
/**
* gtk_list_item_set_activatable:
* @self: a #GtkListItem
* @activatable: if the item should be activatable
*
* Sets @self to be activatable.
*
* If an item is activatable, double-clicking on the item, using
* the <Return> key or calling gtk_widget_activate() will activate
* the item. Activating instructs the containing view to handle
* activation. #GtkListView for example will be emitting the
* GtkListView::activate signal.
*
* By default, list items are activatable
**/
void
gtk_list_item_set_activatable (GtkListItem *self,
gboolean activatable)
{
g_return_if_fail (GTK_IS_LIST_ITEM (self));
if (self->activatable == activatable)
return;
self->activatable = activatable;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVATABLE]);
}
+63
View File
@@ -0,0 +1,63 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_LIST_ITEM_H__
#define __GTK_LIST_ITEM_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkbin.h>
G_BEGIN_DECLS
#define GTK_TYPE_LIST_ITEM (gtk_list_item_get_type ())
#define GTK_LIST_ITEM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM, GtkListItem))
#define GTK_LIST_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM, GtkListItemClass))
#define GTK_IS_LIST_ITEM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM))
#define GTK_IS_LIST_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM))
#define GTK_LIST_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM, GtkListItemClass))
typedef struct _GtkListItemClass GtkListItemClass;
GDK_AVAILABLE_IN_ALL
GType gtk_list_item_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
gpointer gtk_list_item_get_item (GtkListItem *self);
GDK_AVAILABLE_IN_ALL
guint gtk_list_item_get_position (GtkListItem *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
gboolean gtk_list_item_get_selected (GtkListItem *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
gboolean gtk_list_item_get_selectable (GtkListItem *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
void gtk_list_item_set_selectable (GtkListItem *self,
gboolean selectable);
GDK_AVAILABLE_IN_ALL
gboolean gtk_list_item_get_activatable (GtkListItem *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
void gtk_list_item_set_activatable (GtkListItem *self,
gboolean activatable);
G_END_DECLS
#endif /* __GTK_LIST_ITEM_H__ */
+187
View File
@@ -0,0 +1,187 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtklistitemfactoryprivate.h"
#include "gtklistitemprivate.h"
G_DEFINE_TYPE (GtkListItemFactory, gtk_list_item_factory, G_TYPE_OBJECT)
static void
gtk_list_item_factory_default_setup (GtkListItemFactory *self,
GtkListItem *list_item)
{
}
static void
gtk_list_item_factory_default_teardown (GtkListItemFactory *self,
GtkListItem *list_item)
{
GtkWidget *child;
child = gtk_bin_get_child (GTK_BIN (list_item));
if (child)
gtk_container_remove (GTK_CONTAINER (list_item), child);
gtk_list_item_set_selectable (list_item, TRUE);
}
static void
gtk_list_item_factory_default_bind (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected)
{
gtk_list_item_set_item (list_item, item);
gtk_list_item_set_position (list_item, position);
gtk_list_item_set_selected (list_item, selected);
}
static void
gtk_list_item_factory_default_rebind (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected)
{
gtk_list_item_set_item (list_item, item);
gtk_list_item_set_position (list_item, position);
gtk_list_item_set_selected (list_item, selected);
}
static void
gtk_list_item_factory_default_update (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gboolean selected)
{
gtk_list_item_set_position (list_item, position);
gtk_list_item_set_selected (list_item, selected);
}
static void
gtk_list_item_factory_default_unbind (GtkListItemFactory *self,
GtkListItem *list_item)
{
gtk_list_item_set_item (list_item, NULL);
gtk_list_item_set_position (list_item, 0);
gtk_list_item_set_selected (list_item, FALSE);
}
static void
gtk_list_item_factory_class_init (GtkListItemFactoryClass *klass)
{
klass->setup = gtk_list_item_factory_default_setup;
klass->teardown = gtk_list_item_factory_default_teardown;
klass->bind = gtk_list_item_factory_default_bind;
klass->rebind = gtk_list_item_factory_default_rebind;
klass->update = gtk_list_item_factory_default_update;
klass->unbind = gtk_list_item_factory_default_unbind;
}
static void
gtk_list_item_factory_init (GtkListItemFactory *self)
{
}
void
gtk_list_item_factory_setup (GtkListItemFactory *self,
GtkListItem *list_item)
{
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->setup (self, list_item);
}
void
gtk_list_item_factory_teardown (GtkListItemFactory *self,
GtkListItem *list_item)
{
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->teardown (self, list_item);
}
void
gtk_list_item_factory_bind (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected)
{
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
g_object_freeze_notify (G_OBJECT (list_item));
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->bind (self, list_item, position, item, selected);
g_object_thaw_notify (G_OBJECT (list_item));
}
void
gtk_list_item_factory_rebind (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected)
{
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
g_object_freeze_notify (G_OBJECT (list_item));
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->rebind (self, list_item, position, item, selected);
g_object_thaw_notify (G_OBJECT (list_item));
}
void
gtk_list_item_factory_update (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gboolean selected)
{
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
g_object_freeze_notify (G_OBJECT (list_item));
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->update (self, list_item, position, selected);
g_object_thaw_notify (G_OBJECT (list_item));
}
void
gtk_list_item_factory_unbind (GtkListItemFactory *self,
GtkListItem *list_item)
{
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
g_object_freeze_notify (G_OBJECT (list_item));
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->unbind (self, list_item);
g_object_thaw_notify (G_OBJECT (list_item));
}
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_LIST_ITEM_FACTORY_H__
#define __GTK_LIST_ITEM_FACTORY_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
typedef struct _GtkListItemFactoryClass GtkListItemFactoryClass;
#include <gdk/gdk.h>
#include <gtk/gtktypes.h>
G_BEGIN_DECLS
#define GTK_TYPE_LIST_ITEM_FACTORY (gtk_list_item_factory_get_type ())
#define GTK_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactory))
#define GTK_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactoryClass))
#define GTK_IS_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_FACTORY))
#define GTK_IS_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_FACTORY))
#define GTK_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactoryClass))
GDK_AVAILABLE_IN_ALL
GType gtk_list_item_factory_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __GTK_LIST_ITEM_FACTORY_H__ */
+93
View File
@@ -0,0 +1,93 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_LIST_ITEM_FACTORY_PRIVATE_H__
#define __GTK_LIST_ITEM_FACTORY_PRIVATE_H__
#include <gtk/gtklistitem.h>
#include <gtk/gtklistitemfactory.h>
#include <gtk/gtklistview.h>
G_BEGIN_DECLS
struct _GtkListItemFactory
{
GObject parent_instance;
};
struct _GtkListItemFactoryClass
{
GObjectClass parent_class;
/* setup @list_item so it can be bound */
void (* setup) (GtkListItemFactory *self,
GtkListItem *list_item);
/* undo the effects of GtkListItemFactoryClass::setup() */
void (* teardown) (GtkListItemFactory *self,
GtkListItem *list_item);
/* bind @list_item to the given @item, which is in @position and @selected state */
void (* bind) (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected);
/* unbind the current item and bind a new one */
void (* rebind) (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected);
/* like GtkListItemFactoryClass::rebind(), but the item didn't change */
void (* update) (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gboolean selected);
/* undo the effects of GtkListItemFactoryClass::bind() */
void (* unbind) (GtkListItemFactory *self,
GtkListItem *list_item);
};
void gtk_list_item_factory_setup (GtkListItemFactory *self,
GtkListItem *list_item);
void gtk_list_item_factory_teardown (GtkListItemFactory *self,
GtkListItem *list_item);
void gtk_list_item_factory_bind (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected);
void gtk_list_item_factory_rebind (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected);
void gtk_list_item_factory_update (GtkListItemFactory *self,
GtkListItem *list_item,
guint position,
gboolean selected);
void gtk_list_item_factory_unbind (GtkListItemFactory *self,
GtkListItem *list_item);
G_END_DECLS
#endif /* __GTK_LIST_ITEM_FACTORY_PRIVATE_H__ */
File diff suppressed because it is too large Load Diff
+105
View File
@@ -0,0 +1,105 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_LIST_ITEM_MANAGER_H__
#define __GTK_LIST_ITEM_MANAGER_H__
#include "gtk/gtktypes.h"
#include "gtk/gtklistitemfactoryprivate.h"
#include "gtk/gtkrbtreeprivate.h"
#include "gtk/gtkselectionmodel.h"
G_BEGIN_DECLS
#define GTK_TYPE_LIST_ITEM_MANAGER (gtk_list_item_manager_get_type ())
#define GTK_LIST_ITEM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManager))
#define GTK_LIST_ITEM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManagerClass))
#define GTK_IS_LIST_ITEM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_MANAGER))
#define GTK_IS_LIST_ITEM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_MANAGER))
#define GTK_LIST_ITEM_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManagerClass))
typedef struct _GtkListItemManager GtkListItemManager;
typedef struct _GtkListItemManagerClass GtkListItemManagerClass;
typedef struct _GtkListItemManagerItem GtkListItemManagerItem; /* sorry */
typedef struct _GtkListItemManagerItemAugment GtkListItemManagerItemAugment;
typedef struct _GtkListItemTracker GtkListItemTracker;
struct _GtkListItemManagerItem
{
GtkWidget *widget;
guint n_items;
};
struct _GtkListItemManagerItemAugment
{
guint n_items;
};
GType gtk_list_item_manager_get_type (void) G_GNUC_CONST;
GtkListItemManager * gtk_list_item_manager_new_for_size (GtkWidget *widget,
const char *item_css_name,
gsize element_size,
gsize augment_size,
GtkRbTreeAugmentFunc augment_func);
#define gtk_list_item_manager_new(widget, item_css_name, type, augment_type, augment_func) \
gtk_list_item_manager_new_for_size (widget, item_css_name, sizeof (type), sizeof (augment_type), (augment_func))
void gtk_list_item_manager_augment_node (GtkRbTree *tree,
gpointer node_augment,
gpointer node,
gpointer left,
gpointer right);
gpointer gtk_list_item_manager_get_root (GtkListItemManager *self);
gpointer gtk_list_item_manager_get_first (GtkListItemManager *self);
gpointer gtk_list_item_manager_get_nth (GtkListItemManager *self,
guint position,
guint *offset);
guint gtk_list_item_manager_get_item_position (GtkListItemManager *self,
gpointer item);
gpointer gtk_list_item_manager_get_item_augment (GtkListItemManager *self,
gpointer item);
void gtk_list_item_manager_set_factory (GtkListItemManager *self,
GtkListItemFactory *factory);
GtkListItemFactory * gtk_list_item_manager_get_factory (GtkListItemManager *self);
void gtk_list_item_manager_set_model (GtkListItemManager *self,
GtkSelectionModel *model);
GtkSelectionModel * gtk_list_item_manager_get_model (GtkListItemManager *self);
guint gtk_list_item_manager_get_size (GtkListItemManager *self);
GtkListItemTracker * gtk_list_item_tracker_new (GtkListItemManager *self);
void gtk_list_item_tracker_free (GtkListItemManager *self,
GtkListItemTracker *tracker);
void gtk_list_item_tracker_set_position (GtkListItemManager *self,
GtkListItemTracker *tracker,
guint position,
guint n_before,
guint n_after);
guint gtk_list_item_tracker_get_position (GtkListItemManager *self,
GtkListItemTracker *tracker);
G_END_DECLS
#endif /* __GTK_LIST_ITEM_MANAGER_H__ */
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_LIST_ITEM_PRIVATE_H__
#define __GTK_LIST_ITEM_PRIVATE_H__
#include "gtklistitem.h"
#include "gtklistitemmanagerprivate.h"
G_BEGIN_DECLS
GtkListItem * gtk_list_item_new (const char *css_name);
void gtk_list_item_set_item (GtkListItem *self,
gpointer item);
void gtk_list_item_set_position (GtkListItem *self,
guint position);
void gtk_list_item_set_selected (GtkListItem *self,
gboolean selected);
G_END_DECLS
#endif /* __GTK_LIST_ITEM_PRIVATE_H__ */
+47 -17
View File
@@ -206,15 +206,12 @@ gtk_list_list_model_new_with_size (GType item_type,
return result;
}
void
gtk_list_list_model_item_added (GtkListListModel *self,
gpointer item)
static guint
gtk_list_list_model_find (GtkListListModel *self,
gpointer item)
{
gpointer x;
guint position;
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
g_return_if_fail (item != NULL);
gpointer x;
position = 0;
for (x = self->get_first (self->data);
@@ -222,7 +219,17 @@ gtk_list_list_model_item_added (GtkListListModel *self,
x = self->get_next (x, self->data))
position++;
gtk_list_list_model_item_added_at (self, position);
return position;
}
void
gtk_list_list_model_item_added (GtkListListModel *self,
gpointer item)
{
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
g_return_if_fail (item != NULL);
gtk_list_list_model_item_added_at (self, gtk_list_list_model_find (self, item));
}
void
@@ -241,26 +248,49 @@ void
gtk_list_list_model_item_removed (GtkListListModel *self,
gpointer previous)
{
gpointer x;
guint position;
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
if (previous == NULL)
position = 0;
else
position = 1 + gtk_list_list_model_find (self, previous);
gtk_list_list_model_item_removed_at (self, position);
}
void
gtk_list_list_model_item_moved (GtkListListModel *self,
gpointer item,
gpointer previous_previous)
{
guint position, previous_position;
guint min, max;
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
g_return_if_fail (item != previous_previous);
position = gtk_list_list_model_find (self, item);
if (previous_previous == NULL)
{
position = 0;
previous_position = 0;
}
else
{
position = 1;
for (x = self->get_first (self->data);
x != previous;
x = self->get_next (x, self->data))
position++;
previous_position = gtk_list_list_model_find (self, previous_previous);
if (position > previous_position)
previous_position++;
}
gtk_list_list_model_item_removed_at (self, position);
/* item didn't move */
if (position == previous_position)
return;
min = MIN (position, previous_position);
max = MAX (position, previous_position) + 1;
g_list_model_items_changed (G_LIST_MODEL (self), min, max - min, max - min);
}
void
+3
View File
@@ -64,6 +64,9 @@ void gtk_list_list_model_item_removed (GtkListListMode
gpointer previous);
void gtk_list_list_model_item_removed_at (GtkListListModel *self,
guint position);
void gtk_list_list_model_item_moved (GtkListListModel *self,
gpointer item,
gpointer previous_previous);
void gtk_list_list_model_clear (GtkListListModel *self);
+975
View File
@@ -0,0 +1,975 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtklistview.h"
#include "gtkintl.h"
#include "gtklistbaseprivate.h"
#include "gtklistitemmanagerprivate.h"
#include "gtkmain.h"
#include "gtkprivate.h"
#include "gtkrbtreeprivate.h"
#include "gtkstylecontext.h"
#include "gtkwidgetprivate.h"
/* Maximum number of list items created by the listview.
* For debugging, you can set this to G_MAXUINT to ensure
* there's always a list item for every row.
*/
#define GTK_LIST_VIEW_MAX_LIST_ITEMS 200
/* Extra items to keep above + below every tracker */
#define GTK_LIST_VIEW_EXTRA_ITEMS 2
/**
* SECTION:gtklistview
* @title: GtkListView
* @short_description: A widget for displaying lists
* @see_also: #GListModel
*
* GtkListView is a widget to present a view into a large dynamic list of items.
*/
typedef struct _ListRow ListRow;
typedef struct _ListRowAugment ListRowAugment;
struct _GtkListView
{
GtkListBase parent_instance;
GtkListItemManager *item_manager;
gboolean show_separators;
int list_width;
};
struct _GtkListViewClass
{
GtkListBaseClass parent_class;
};
struct _ListRow
{
GtkListItemManagerItem parent;
guint height; /* per row */
};
struct _ListRowAugment
{
GtkListItemManagerItemAugment parent;
guint height; /* total */
};
enum
{
PROP_0,
PROP_FACTORY,
PROP_MODEL,
PROP_SHOW_SEPARATORS,
N_PROPS
};
enum {
ACTIVATE,
LAST_SIGNAL
};
G_DEFINE_TYPE (GtkListView, gtk_list_view, GTK_TYPE_LIST_BASE)
static GParamSpec *properties[N_PROPS] = { NULL, };
static guint signals[LAST_SIGNAL] = { 0 };
static void G_GNUC_UNUSED
dump (GtkListView *self)
{
ListRow *row;
guint n_widgets, n_list_rows;
n_widgets = 0;
n_list_rows = 0;
//g_print ("ANCHOR: %u - %u\n", self->anchor_start, self->anchor_end);
for (row = gtk_list_item_manager_get_first (self->item_manager);
row;
row = gtk_rb_tree_node_get_next (row))
{
if (row->parent.widget)
n_widgets++;
n_list_rows++;
g_print (" %4u%s (%upx)\n", row->parent.n_items, row->parent.widget ? " (widget)" : "", row->height);
}
g_print (" => %u widgets in %u list rows\n", n_widgets, n_list_rows);
}
static void
list_row_augment (GtkRbTree *tree,
gpointer node_augment,
gpointer node,
gpointer left,
gpointer right)
{
ListRow *row = node;
ListRowAugment *aug = node_augment;
gtk_list_item_manager_augment_node (tree, node_augment, node, left, right);
aug->height = row->height * row->parent.n_items;
if (left)
{
ListRowAugment *left_aug = gtk_rb_tree_get_augment (tree, left);
aug->height += left_aug->height;
}
if (right)
{
ListRowAugment *right_aug = gtk_rb_tree_get_augment (tree, right);
aug->height += right_aug->height;
}
}
static ListRow *
gtk_list_view_get_row_at_y (GtkListView *self,
int y,
int *offset)
{
ListRow *row, *tmp;
row = gtk_list_item_manager_get_root (self->item_manager);
while (row)
{
tmp = gtk_rb_tree_node_get_left (row);
if (tmp)
{
ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, tmp);
if (y < aug->height)
{
row = tmp;
continue;
}
y -= aug->height;
}
if (y < row->height * row->parent.n_items)
break;
y -= row->height * row->parent.n_items;
row = gtk_rb_tree_node_get_right (row);
}
if (offset)
*offset = row ? y : 0;
return row;
}
static int
list_row_get_y (GtkListView *self,
ListRow *row)
{
ListRow *parent, *left;
int y;
left = gtk_rb_tree_node_get_left (row);
if (left)
{
ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, left);
y = aug->height;
}
else
y = 0;
for (parent = gtk_rb_tree_node_get_parent (row);
parent != NULL;
parent = gtk_rb_tree_node_get_parent (row))
{
left = gtk_rb_tree_node_get_left (parent);
if (left != row)
{
if (left)
{
ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, left);
y += aug->height;
}
y += parent->height * parent->parent.n_items;
}
row = parent;
}
return y ;
}
static int
gtk_list_view_get_list_height (GtkListView *self)
{
ListRow *row;
ListRowAugment *aug;
row = gtk_list_item_manager_get_root (self->item_manager);
if (row == NULL)
return 0;
aug = gtk_list_item_manager_get_item_augment (self->item_manager, row);
return aug->height;
}
static gboolean
gtk_list_view_get_allocation_along (GtkListBase *base,
guint pos,
int *offset,
int *size)
{
GtkListView *self = GTK_LIST_VIEW (base);
ListRow *row;
guint skip;
int y;
row = gtk_list_item_manager_get_nth (self->item_manager, pos, &skip);
if (row == NULL)
{
if (offset)
*offset = 0;
if (size)
*size = 0;
return FALSE;
}
y = list_row_get_y (self, row);
y += skip * row->height;
if (offset)
*offset = y;
if (size)
*size = row->height;
return TRUE;
}
static gboolean
gtk_list_view_get_allocation_across (GtkListBase *base,
guint pos,
int *offset,
int *size)
{
GtkListView *self = GTK_LIST_VIEW (base);
if (offset)
*offset = 0;
if (size)
*size = self->list_width;
return TRUE;
}
static guint
gtk_list_view_move_focus_along (GtkListBase *base,
guint pos,
int steps)
{
if (steps < 0)
return pos - MIN (pos, -steps);
else
{
pos += MIN (gtk_list_base_get_n_items (base) - pos - 1, steps);
}
return pos;
}
static gboolean
gtk_list_view_get_position_from_allocation (GtkListBase *base,
int across,
int along,
guint *pos,
cairo_rectangle_int_t *area)
{
GtkListView *self = GTK_LIST_VIEW (base);
ListRow *row;
int remaining;
if (across >= self->list_width)
return FALSE;
row = gtk_list_view_get_row_at_y (self, along, &remaining);
if (row == NULL)
return FALSE;
*pos = gtk_list_item_manager_get_item_position (self->item_manager, row);
g_assert (remaining < row->height * row->parent.n_items);
*pos += remaining / row->height;
if (area)
{
area->x = 0;
area->width = self->list_width;
area->y = along - remaining % row->height;
area->height = row->height;
}
return TRUE;
}
static guint
gtk_list_view_move_focus_across (GtkListBase *base,
guint pos,
int steps)
{
return pos;
}
static int
compare_ints (gconstpointer first,
gconstpointer second)
{
return *(int *) first - *(int *) second;
}
static guint
gtk_list_view_get_unknown_row_height (GtkListView *self,
GArray *heights)
{
g_return_val_if_fail (heights->len > 0, 0);
/* return the median and hope rows are generally uniform with few outliers */
g_array_sort (heights, compare_ints);
return g_array_index (heights, int, heights->len / 2);
}
static void
gtk_list_view_measure_across (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural)
{
GtkListView *self = GTK_LIST_VIEW (widget);
ListRow *row;
int min, nat, child_min, child_nat;
/* XXX: Figure out how to split a given height into per-row heights.
* Good luck! */
for_size = -1;
min = 0;
nat = 0;
for (row = gtk_list_item_manager_get_first (self->item_manager);
row != NULL;
row = gtk_rb_tree_node_get_next (row))
{
/* ignore unavailable rows */
if (row->parent.widget == NULL)
continue;
gtk_widget_measure (row->parent.widget,
orientation, for_size,
&child_min, &child_nat, NULL, NULL);
min = MAX (min, child_min);
nat = MAX (nat, child_nat);
}
*minimum = min;
*natural = nat;
}
static void
gtk_list_view_measure_list (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural)
{
GtkListView *self = GTK_LIST_VIEW (widget);
ListRow *row;
int min, nat, child_min, child_nat;
GArray *min_heights, *nat_heights;
guint n_unknown;
min_heights = g_array_new (FALSE, FALSE, sizeof (int));
nat_heights = g_array_new (FALSE, FALSE, sizeof (int));
n_unknown = 0;
min = 0;
nat = 0;
for (row = gtk_list_item_manager_get_first (self->item_manager);
row != NULL;
row = gtk_rb_tree_node_get_next (row))
{
if (row->parent.widget)
{
gtk_widget_measure (row->parent.widget,
orientation, for_size,
&child_min, &child_nat, NULL, NULL);
g_array_append_val (min_heights, child_min);
g_array_append_val (nat_heights, child_nat);
min += child_min;
nat += child_nat;
}
else
{
n_unknown += row->parent.n_items;
}
}
if (n_unknown)
{
min += n_unknown * gtk_list_view_get_unknown_row_height (self, min_heights);
nat += n_unknown * gtk_list_view_get_unknown_row_height (self, nat_heights);
}
g_array_free (min_heights, TRUE);
g_array_free (nat_heights, TRUE);
*minimum = min;
*natural = nat;
}
static void
gtk_list_view_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkListView *self = GTK_LIST_VIEW (widget);
if (orientation == gtk_list_base_get_orientation (GTK_LIST_BASE (self)))
gtk_list_view_measure_list (widget, orientation, for_size, minimum, natural);
else
gtk_list_view_measure_across (widget, orientation, for_size, minimum, natural);
}
static void
gtk_list_view_size_allocate_child (GtkListView *self,
GtkWidget *child,
int x,
int y,
int width,
int height)
{
GtkAllocation child_allocation;
if (gtk_list_base_get_orientation (GTK_LIST_BASE (self)) == GTK_ORIENTATION_VERTICAL)
{
child_allocation.x = x;
child_allocation.y = y;
child_allocation.width = width;
child_allocation.height = height;
}
else if (_gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_LTR)
{
child_allocation.x = y;
child_allocation.y = x;
child_allocation.width = height;
child_allocation.height = width;
}
else
{
int mirror_point = gtk_widget_get_width (GTK_WIDGET (self));
child_allocation.x = mirror_point - y - height;
child_allocation.y = x;
child_allocation.width = height;
child_allocation.height = width;
}
gtk_widget_size_allocate (child, &child_allocation, -1);
}
static void
gtk_list_view_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkListView *self = GTK_LIST_VIEW (widget);
ListRow *row;
GArray *heights;
int min, nat, row_height;
int x, y;
GtkOrientation orientation, opposite_orientation;
GtkScrollablePolicy scroll_policy;
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
scroll_policy = gtk_list_base_get_scroll_policy (GTK_LIST_BASE (self), orientation);
/* step 0: exit early if list is empty */
if (gtk_list_item_manager_get_root (self->item_manager) == NULL)
return;
/* step 1: determine width of the list */
gtk_widget_measure (widget, opposite_orientation,
-1,
&min, &nat, NULL, NULL);
self->list_width = orientation == GTK_ORIENTATION_VERTICAL ? width : height;
if (scroll_policy == GTK_SCROLL_MINIMUM)
self->list_width = MAX (min, self->list_width);
else
self->list_width = MAX (nat, self->list_width);
/* step 2: determine height of known list items */
heights = g_array_new (FALSE, FALSE, sizeof (int));
for (row = gtk_list_item_manager_get_first (self->item_manager);
row != NULL;
row = gtk_rb_tree_node_get_next (row))
{
if (row->parent.widget == NULL)
continue;
gtk_widget_measure (row->parent.widget, orientation,
self->list_width,
&min, &nat, NULL, NULL);
if (scroll_policy == GTK_SCROLL_MINIMUM)
row_height = min;
else
row_height = nat;
if (row->height != row_height)
{
row->height = row_height;
gtk_rb_tree_node_mark_dirty (row);
}
g_array_append_val (heights, row_height);
}
/* step 3: determine height of unknown items */
row_height = gtk_list_view_get_unknown_row_height (self, heights);
g_array_free (heights, TRUE);
for (row = gtk_list_item_manager_get_first (self->item_manager);
row != NULL;
row = gtk_rb_tree_node_get_next (row))
{
if (row->parent.widget)
continue;
if (row->height != row_height)
{
row->height = row_height;
gtk_rb_tree_node_mark_dirty (row);
}
}
/* step 3: update the adjustments */
gtk_list_base_update_adjustments (GTK_LIST_BASE (self),
self->list_width,
gtk_list_view_get_list_height (self),
gtk_widget_get_size (widget, opposite_orientation),
gtk_widget_get_size (widget, orientation),
&x, &y);
x = -x;
y = -y;
/* step 4: actually allocate the widgets */
for (row = gtk_list_item_manager_get_first (self->item_manager);
row != NULL;
row = gtk_rb_tree_node_get_next (row))
{
if (row->parent.widget)
{
gtk_list_view_size_allocate_child (self,
row->parent.widget,
x,
y,
self->list_width,
row->height);
}
y += row->height * row->parent.n_items;
}
}
static void
gtk_list_view_dispose (GObject *object)
{
GtkListView *self = GTK_LIST_VIEW (object);
self->item_manager = NULL;
G_OBJECT_CLASS (gtk_list_view_parent_class)->dispose (object);
}
static void
gtk_list_view_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkListView *self = GTK_LIST_VIEW (object);
switch (property_id)
{
case PROP_FACTORY:
g_value_set_object (value, gtk_list_item_manager_get_factory (self->item_manager));
break;
case PROP_MODEL:
g_value_set_object (value, gtk_list_base_get_model (GTK_LIST_BASE (self)));
break;
case PROP_SHOW_SEPARATORS:
g_value_set_boolean (value, self->show_separators);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_list_view_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkListView *self = GTK_LIST_VIEW (object);
switch (property_id)
{
case PROP_FACTORY:
gtk_list_view_set_factory (self, g_value_get_object (value));
break;
case PROP_MODEL:
gtk_list_view_set_model (self, g_value_get_object (value));
break;
case PROP_SHOW_SEPARATORS:
gtk_list_view_set_show_separators (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_list_view_activate_item (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
{
GtkListView *self = GTK_LIST_VIEW (widget);
guint pos;
if (!g_variant_check_format_string (parameter, "u", FALSE))
return;
g_variant_get (parameter, "u", &pos);
if (pos >= gtk_list_base_get_n_items (GTK_LIST_BASE (self)))
return;
g_signal_emit (widget, signals[ACTIVATE], 0, pos);
}
static void
gtk_list_view_class_init (GtkListViewClass *klass)
{
GtkListBaseClass *list_base_class = GTK_LIST_BASE_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
list_base_class->list_item_name = "row";
list_base_class->list_item_size = sizeof (ListRow);
list_base_class->list_item_augment_size = sizeof (ListRowAugment);
list_base_class->list_item_augment_func = list_row_augment;
list_base_class->get_allocation_along = gtk_list_view_get_allocation_along;
list_base_class->get_allocation_across = gtk_list_view_get_allocation_across;
list_base_class->get_position_from_allocation = gtk_list_view_get_position_from_allocation;
list_base_class->move_focus_along = gtk_list_view_move_focus_along;
list_base_class->move_focus_across = gtk_list_view_move_focus_across;
widget_class->measure = gtk_list_view_measure;
widget_class->size_allocate = gtk_list_view_size_allocate;
gobject_class->dispose = gtk_list_view_dispose;
gobject_class->get_property = gtk_list_view_get_property;
gobject_class->set_property = gtk_list_view_set_property;
/**
* GtkListView:factory:
*
* Factory for populating list items
*/
properties[PROP_FACTORY] =
g_param_spec_object ("factory",
P_("Factory"),
P_("Factory for populating list items"),
GTK_TYPE_LIST_ITEM_FACTORY,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkListView:model:
*
* Model for the items displayed
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("Model for the items displayed"),
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkListView:show-separators:
*
* Show separators between rows
*/
properties[PROP_SHOW_SEPARATORS] =
g_param_spec_boolean ("show-separators",
P_("Show separators"),
P_("Show separators between rows"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
/**
* GtkListView::activate:
* @self: The #GtkListView
* @position: position of item to activate
*
* The ::activate signal is emitted when a row has been activated by the user,
* usually via activating the GtkListView|list.activate-item action.
*
* This allows for a convenient way to handle activation in a listview.
* See gtk_list_item_set_activatable() for details on how to use this signal.
*/
signals[ACTIVATE] =
g_signal_new (I_("activate"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
g_signal_set_va_marshaller (signals[ACTIVATE],
G_TYPE_FROM_CLASS (gobject_class),
g_cclosure_marshal_VOID__UINTv);
/**
* GtkListView|list.activate-item:
* @position: position of item to activate
*
* Activates the item given in @position by emitting the GtkListView::activate
* signal.
*/
gtk_widget_class_install_action (widget_class,
"list.activate-item",
"u",
gtk_list_view_activate_item);
gtk_widget_class_set_css_name (widget_class, I_("list"));
}
static void
gtk_list_view_init (GtkListView *self)
{
self->item_manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
gtk_list_base_set_anchor_max_widgets (GTK_LIST_BASE (self),
GTK_LIST_VIEW_MAX_LIST_ITEMS,
GTK_LIST_VIEW_EXTRA_ITEMS);
}
/**
* gtk_list_view_new:
*
* Creates a new empty #GtkListView.
*
* You most likely want to call gtk_list_view_set_factory() to
* set up a way to map its items to widgets and gtk_list_view_set_model()
* to set a model to provide items next.
*
* Returns: a new #GtkListView
**/
GtkWidget *
gtk_list_view_new (void)
{
return g_object_new (GTK_TYPE_LIST_VIEW, NULL);
}
/**
* gtk_list_view_new_with_factory:
* @factory: (transfer full): The factory to populate items with
*
* Creates a new #GtkListView that uses the given @factory for
* mapping items to widgets.
*
* You most likely want to call gtk_list_view_set_model() to set
* a model next.
*
* The function takes ownership of the
* argument, so you can write code like
* ```
* list_view = gtk_list_view_new_with_factory (
* gtk_builder_list_item_factory_newfrom_resource ("/resource.ui"));
* ```
*
* Returns: a new #GtkListView using the given @factory
**/
GtkWidget *
gtk_list_view_new_with_factory (GtkListItemFactory *factory)
{
GtkWidget *result;
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
result = g_object_new (GTK_TYPE_LIST_VIEW,
"factory", factory,
NULL);
g_object_unref (factory);
return result;
}
/**
* gtk_list_view_get_model:
* @self: a #GtkListView
*
* Gets the model that's currently used to read the items displayed.
*
* Returns: (nullable) (transfer none): The model in use
**/
GListModel *
gtk_list_view_get_model (GtkListView *self)
{
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), NULL);
return gtk_list_base_get_model (GTK_LIST_BASE (self));
}
/**
* gtk_list_view_set_model:
* @self: a #GtkListView
* @model: (allow-none) (transfer none): the model to use or %NULL for none
*
* Sets the #GListModel to use.
*
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
* Otherwise, @self creates a #GtkSingleSelection for the selection.
**/
void
gtk_list_view_set_model (GtkListView *self,
GListModel *model)
{
g_return_if_fail (GTK_IS_LIST_VIEW (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (!gtk_list_base_set_model (GTK_LIST_BASE (self), model))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_list_view_get_factory:
* @self: a #GtkListView
*
* Gets the factory that's currently used to populate list items.
*
* Returns: (nullable) (transfer none): The factory in use
**/
GtkListItemFactory *
gtk_list_view_get_factory (GtkListView *self)
{
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), NULL);
return gtk_list_item_manager_get_factory (self->item_manager);
}
/**
* gtk_list_view_set_factory:
* @self: a #GtkListView
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
*
* Sets the #GtkListItemFactory to use for populating list items.
**/
void
gtk_list_view_set_factory (GtkListView *self,
GtkListItemFactory *factory)
{
g_return_if_fail (GTK_IS_LIST_VIEW (self));
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
if (factory == gtk_list_item_manager_get_factory (self->item_manager))
return;
gtk_list_item_manager_set_factory (self->item_manager, factory);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
}
/**
* gtk_list_view_set_show_separators:
* @self: a #GtkListView
* @show_separators: %TRUE to show separators
*
* Sets whether the list box should show separators
* between rows.
*/
void
gtk_list_view_set_show_separators (GtkListView *self,
gboolean show_separators)
{
g_return_if_fail (GTK_IS_LIST_VIEW (self));
if (self->show_separators == show_separators)
return;
self->show_separators = show_separators;
if (show_separators)
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "separators");
else
gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "separators");
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SEPARATORS]);
}
/**
* gtk_list_view_get_show_separators:
* @self: a #GtkListView
*
* Returns whether the list box should show separators
* between rows.
*
* Returns: %TRUE if the list box shows separators
*/
gboolean
gtk_list_view_get_show_separators (GtkListView *self)
{
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), FALSE);
return self->show_separators;
}
+74
View File
@@ -0,0 +1,74 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_LIST_VIEW_H__
#define __GTK_LIST_VIEW_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtklistbase.h>
G_BEGIN_DECLS
#define GTK_TYPE_LIST_VIEW (gtk_list_view_get_type ())
#define GTK_LIST_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_VIEW, GtkListView))
#define GTK_LIST_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_VIEW, GtkListViewClass))
#define GTK_IS_LIST_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_VIEW))
#define GTK_IS_LIST_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_VIEW))
#define GTK_LIST_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_VIEW, GtkListViewClass))
/**
* GtkListView:
*
* GtkListView is the simple list implementation for GTK's list widgets.
*/
typedef struct _GtkListView GtkListView;
typedef struct _GtkListViewClass GtkListViewClass;
GDK_AVAILABLE_IN_ALL
GType gtk_list_view_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_list_view_new (void);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_list_view_new_with_factory (GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_list_view_get_model (GtkListView *self);
GDK_AVAILABLE_IN_ALL
void gtk_list_view_set_model (GtkListView *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
void gtk_list_view_set_factory (GtkListView *self,
GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GtkListItemFactory *
gtk_list_view_get_factory (GtkListView *self);
GDK_AVAILABLE_IN_ALL
void gtk_list_view_set_show_separators (GtkListView *self,
gboolean show_separators);
GDK_AVAILABLE_IN_ALL
gboolean gtk_list_view_get_show_separators (GtkListView *self);
G_END_DECLS
#endif /* __GTK_LIST_VIEW_H__ */
-8
View File
@@ -685,13 +685,6 @@ gtk_popover_unrealize (GtkWidget *widget)
g_clear_object (&priv->surface);
}
static void
gtk_popover_move_focus (GtkWidget *widget,
GtkDirectionType direction)
{
g_signal_emit_by_name (gtk_widget_get_root (widget), "move-focus", direction);
}
static void
gtk_popover_show (GtkWidget *widget)
{
@@ -1374,7 +1367,6 @@ gtk_popover_class_init (GtkPopoverClass *klass)
widget_class->measure = gtk_popover_measure;
widget_class->size_allocate = gtk_popover_size_allocate;
widget_class->snapshot = gtk_popover_snapshot;
widget_class->move_focus = gtk_popover_move_focus;
container_class->add = gtk_popover_add;
container_class->remove = gtk_popover_remove;
+5 -20
View File
@@ -27,7 +27,6 @@
#include "gtkradiobutton.h"
#include "gtkwidgetprivate.h"
#include "gtktogglebuttonprivate.h"
#include "gtkcheckbuttonprivate.h"
#include "gtklabel.h"
#include "gtkmarshalers.h"
@@ -242,7 +241,7 @@ gtk_radio_button_init (GtkRadioButton *radio_button)
gtk_widget_set_receives_default (GTK_WIDGET (radio_button), FALSE);
_gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button), TRUE);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button), TRUE);
priv->group = g_slist_prepend (NULL, radio_button);
@@ -729,9 +728,6 @@ gtk_radio_button_clicked (GtkButton *button)
GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
GtkToggleButton *tmp_button;
GSList *tmp_list;
gint toggled;
toggled = FALSE;
g_object_ref (GTK_WIDGET (button));
@@ -753,17 +749,13 @@ gtk_radio_button_clicked (GtkButton *button)
}
if (tmp_button)
{
toggled = TRUE;
_gtk_toggle_button_set_active (toggle_button,
!gtk_toggle_button_get_active (toggle_button));
}
gtk_toggle_button_set_active (toggle_button,
!gtk_toggle_button_get_active (toggle_button));
}
else
{
toggled = TRUE;
_gtk_toggle_button_set_active (toggle_button,
!gtk_toggle_button_get_active (toggle_button));
gtk_toggle_button_set_active (toggle_button,
!gtk_toggle_button_get_active (toggle_button));
tmp_list = priv->group;
while (tmp_list)
@@ -779,13 +771,6 @@ gtk_radio_button_clicked (GtkButton *button)
}
}
if (toggled)
{
gtk_toggle_button_toggled (toggle_button);
g_object_notify (G_OBJECT (toggle_button), "active");
}
gtk_widget_queue_draw (GTK_WIDGET (button));
g_object_unref (button);
+12
View File
@@ -33,6 +33,18 @@ G_BEGIN_DECLS
GDK_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (GtkSelectionModel, gtk_selection_model, GTK, SELECTION_MODEL, GListModel)
/**
* GTK_INVALID_LIST_POSITION:
*
* The value used to refer to a guaranteed invalid position in a #GListModel. This
* value may be returned from some functions, others may accept it as input.
* Its interpretion may differ for different functions.
*
* Refer to each function's documentation for if this value is allowed and what it
* does.
*/
#define GTK_INVALID_LIST_POSITION (G_MAXUINT)
/**
* GtkSelectionModelInterface:
* @is_selected: Return if the item at the given position is selected.
-12
View File
@@ -26,18 +26,6 @@ G_BEGIN_DECLS
#define GTK_TYPE_SINGLE_SELECTION (gtk_single_selection_get_type ())
/**
* GTK_INVALID_LIST_POSITION:
*
* The value used to refer to a guaranteed invalid position in a #GListModel. This
* value may be returned from some functions, others may accept it as input.
* Its interpretion may differ for different functions.
*
* Refer to each function's documentation for if this value is allowed and what it
* does.
*/
#define GTK_INVALID_LIST_POSITION (G_MAXUINT)
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkSingleSelection, gtk_single_selection, GTK, SINGLE_SELECTION, GObject)
+1
View File
@@ -1632,6 +1632,7 @@ gtk_text_view_init (GtkTextView *text_view)
priv->indent = 0;
priv->tabs = NULL;
priv->editable = TRUE;
priv->cursor_alpha = 1.0;
priv->scroll_after_paste = FALSE;
+10 -20
View File
@@ -24,7 +24,7 @@
#include "config.h"
#include "gtktogglebuttonprivate.h"
#include "gtktogglebutton.h"
#include "gtkbuttonprivate.h"
#include "gtkintl.h"
@@ -293,8 +293,9 @@ gtk_toggle_button_get_property (GObject *object,
*
* Sets the status of the toggle button. Set to %TRUE if you want the
* GtkToggleButton to be pressed in, and %FALSE to raise it.
* This action causes the #GtkToggleButton::toggled signal and the
* #GtkButton::clicked signal to be emitted.
*
* If the status of the button changes, this action causes the
* #GtkToggleButton::toggled signal to be emitted.
*/
void
gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
@@ -306,18 +307,8 @@ gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
is_active = is_active != FALSE;
if (priv->active != is_active)
{
g_signal_emit_by_name (toggle_button, "clicked");
g_object_notify_by_pspec (G_OBJECT (toggle_button), toggle_button_props[PROP_ACTIVE]);
}
}
void
_gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
gboolean is_active)
{
GtkToggleButtonPrivate *priv = gtk_toggle_button_get_instance_private (toggle_button);
if (priv->active == is_active)
return;
priv->active = is_active;
@@ -326,6 +317,9 @@ _gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
else
gtk_widget_unset_state_flags (GTK_WIDGET (toggle_button), GTK_STATE_FLAG_CHECKED);
gtk_toggle_button_toggled (toggle_button);
g_object_notify_by_pspec (G_OBJECT (toggle_button), toggle_button_props[PROP_ACTIVE]);
}
/**
@@ -387,11 +381,7 @@ gtk_toggle_button_clicked (GtkButton *button)
GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
GtkToggleButtonPrivate *priv = gtk_toggle_button_get_instance_private (toggle_button);
_gtk_toggle_button_set_active (toggle_button, !priv->active);
gtk_toggle_button_toggled (toggle_button);
g_object_notify_by_pspec (G_OBJECT (toggle_button), toggle_button_props[PROP_ACTIVE]);
gtk_toggle_button_set_active (toggle_button, !priv->active);
if (GTK_BUTTON_CLASS (gtk_toggle_button_parent_class)->clicked)
GTK_BUTTON_CLASS (gtk_toggle_button_parent_class)->clicked (button);
-40
View File
@@ -1,40 +0,0 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#ifndef __GTK_TOGGLE_BUTTON_PRIVATE_H__
#define __GTK_TOGGLE_BUTTON_PRIVATE_H__
#include <gtk/gtktogglebutton.h>
G_BEGIN_DECLS
void _gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
gboolean is_active);
G_END_DECLS
#endif /* __GTK_TOGGLE_BUTTON_PRIVATE_H__ */
+699
View File
@@ -0,0 +1,699 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtktreeexpander.h"
#include "gtkbindings.h"
#include "gtkboxlayout.h"
#include "gtkgestureclick.h"
#include "gtkiconprivate.h"
#include "gtkintl.h"
#include "gtktreelistmodel.h"
/**
* SECTION:gtktreeexpander
* @title: GtkTreeExpander
* @short_description: An indenting expander button for use in a tree list
* @see_also: #GtkTreeListModel
*
* GtkTreeExpander is a widget that provides an expander for a list.
*
* It is typically placed as a bottommost child into a #GtkListView to allow
* users to expand and collapse children in a list with a #GtkTreeListModel.
* It will provide the common UI elements, gestures and keybindings for this
* purpose.
*
* On top of this, the "listitem.expand", "listitem.collapse" and
* "listitem.toggle-expand" actions are provided to allow adding custom UI
* for managing expanded state.
*
* The #GtkTreeListModel must be set to not be passthrough. Then it will provide
* #GtkTreeListRow items which can be set via gtk_tree_expander_set_list_row()
* on the expander. The expander will then watch that row item automatically.
* gtk_tree_expander_set_child() sets the widget that displays the actual row
* contents.
*
* # CSS nodes
*
* |[<!-- language="plain" -->
* treeexpander
* [indent]*
* [expander]
* <child>
* ]|
*
* GtkTreeExpander has zero or one CSS nodes with the name "expander" that should
* display the expander icon. The node will be `:checked` when it is expanded.
* If the node is not expandable, an "indent" node will be displayed instead.
*
* For every level of depth, another "indent" node is prepended.
*/
struct _GtkTreeExpander
{
GtkWidget parent_instance;
GtkTreeListRow *list_row;
GtkWidget *child;
GtkWidget *expander;
guint notify_handler;
};
enum
{
PROP_0,
PROP_CHILD,
PROP_ITEM,
PROP_LIST_ROW,
N_PROPS
};
G_DEFINE_TYPE (GtkTreeExpander, gtk_tree_expander, GTK_TYPE_WIDGET)
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_tree_expander_click_gesture_pressed (GtkGestureClick *gesture,
int n_press,
double x,
double y,
gpointer unused)
{
GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
gtk_widget_activate_action (widget, "listitem.toggle-expand", NULL);
gtk_widget_set_state_flags (widget,
GTK_STATE_FLAG_ACTIVE,
FALSE);
}
static void
gtk_tree_expander_click_gesture_released (GtkGestureClick *gesture,
int n_press,
double x,
double y,
gpointer unused)
{
gtk_widget_unset_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)),
GTK_STATE_FLAG_ACTIVE);
}
static void
gtk_tree_expander_click_gesture_canceled (GtkGestureClick *gesture,
GdkEventSequence *sequence,
gpointer unused)
{
gtk_widget_unset_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)),
GTK_STATE_FLAG_ACTIVE);
}
static void
gtk_tree_expander_update_for_list_row (GtkTreeExpander *self)
{
if (self->list_row == NULL)
{
GtkWidget *child;
for (child = gtk_widget_get_first_child (GTK_WIDGET (self));
child != self->child;
child = gtk_widget_get_first_child (GTK_WIDGET (self)))
{
gtk_widget_unparent (child);
}
self->expander = NULL;
}
else
{
GtkWidget *child;
guint i, depth;
depth = gtk_tree_list_row_get_depth (self->list_row);
if (gtk_tree_list_row_is_expandable (self->list_row))
{
if (self->expander == NULL)
{
GtkGesture *gesture;
self->expander = gtk_icon_new ("expander");
gesture = gtk_gesture_click_new ();
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
GTK_PHASE_BUBBLE);
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture),
FALSE);
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
GDK_BUTTON_PRIMARY);
g_signal_connect (gesture, "pressed",
G_CALLBACK (gtk_tree_expander_click_gesture_pressed), NULL);
g_signal_connect (gesture, "released",
G_CALLBACK (gtk_tree_expander_click_gesture_released), NULL);
g_signal_connect (gesture, "cancel",
G_CALLBACK (gtk_tree_expander_click_gesture_canceled), NULL);
gtk_widget_add_controller (self->expander, GTK_EVENT_CONTROLLER (gesture));
gtk_widget_insert_before (self->expander,
GTK_WIDGET (self),
self->child);
}
if (gtk_tree_list_row_get_expanded (self->list_row))
gtk_widget_set_state_flags (self->expander, GTK_STATE_FLAG_CHECKED, FALSE);
else
gtk_widget_unset_state_flags (self->expander, GTK_STATE_FLAG_CHECKED);
child = gtk_widget_get_prev_sibling (self->expander);
}
else
{
g_clear_pointer (&self->expander, gtk_widget_unparent);
depth++;
if (self->child)
child = gtk_widget_get_prev_sibling (self->child);
else
child = gtk_widget_get_last_child (GTK_WIDGET (self));
}
for (i = 0; i < depth; i++)
{
if (child)
child = gtk_widget_get_prev_sibling (child);
else
gtk_widget_insert_after (gtk_icon_new ("indent"), GTK_WIDGET (self), NULL);
}
while (child)
{
GtkWidget *prev = gtk_widget_get_prev_sibling (child);
gtk_widget_unparent (child);
child = prev;
}
}
}
static void
gtk_tree_expander_list_row_notify_cb (GtkTreeListRow *list_row,
GParamSpec *pspec,
GtkTreeExpander *self)
{
if (pspec->name == g_intern_static_string ("expanded"))
{
if (self->expander)
{
if (gtk_tree_list_row_get_expanded (list_row))
gtk_widget_set_state_flags (self->expander, GTK_STATE_FLAG_CHECKED, FALSE);
else
gtk_widget_unset_state_flags (self->expander, GTK_STATE_FLAG_CHECKED);
}
}
else if (pspec->name == g_intern_static_string ("item"))
{
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
}
else
{
/* can this happen other than when destroying the row? */
gtk_tree_expander_update_for_list_row (self);
}
}
static gboolean
gtk_tree_expander_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
/* The idea of this function is the following:
* 1. If any child can take focus, do not ever attempt
* to take focus.
* 2. Otherwise, if this item is selectable or activatable,
* allow focusing this widget.
*
* This makes sure every item in a list is focusable for
* activation and selection handling, but no useless widgets
* get focused and moving focus is as fast as possible.
*/
if (self->child)
{
if (gtk_widget_get_focus_child (widget))
return FALSE;
if (gtk_widget_child_focus (self->child, direction))
return TRUE;
}
if (gtk_widget_is_focus (widget))
return FALSE;
if (!gtk_widget_get_can_focus (widget))
return FALSE;
gtk_widget_grab_focus (widget);
return TRUE;
}
static gboolean
gtk_tree_expander_grab_focus (GtkWidget *widget)
{
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
if (self->child && gtk_widget_grab_focus (self->child))
return TRUE;
return GTK_WIDGET_CLASS (gtk_tree_expander_parent_class)->grab_focus (widget);
}
static void
gtk_tree_expander_clear_list_row (GtkTreeExpander *self)
{
if (self->list_row == NULL)
return;
g_signal_handler_disconnect (self->list_row, self->notify_handler);
self->notify_handler = 0;
g_clear_object (&self->list_row);
}
static void
gtk_tree_expander_dispose (GObject *object)
{
GtkTreeExpander *self = GTK_TREE_EXPANDER (object);
gtk_tree_expander_clear_list_row (self);
gtk_tree_expander_update_for_list_row (self);
g_clear_pointer (&self->child, gtk_widget_unparent);
g_assert (self->expander == NULL);
G_OBJECT_CLASS (gtk_tree_expander_parent_class)->dispose (object);
}
static void
gtk_tree_expander_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkTreeExpander *self = GTK_TREE_EXPANDER (object);
switch (property_id)
{
case PROP_CHILD:
g_value_set_object (value, self->child);
break;
case PROP_ITEM:
g_value_set_object (value, gtk_tree_expander_get_item (self));
break;
case PROP_LIST_ROW:
g_value_set_object (value, self->list_row);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_tree_expander_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkTreeExpander *self = GTK_TREE_EXPANDER (object);
switch (property_id)
{
case PROP_CHILD:
gtk_tree_expander_set_child (self, g_value_get_object (value));
break;
case PROP_LIST_ROW:
gtk_tree_expander_set_list_row (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_tree_expander_expand (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
{
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
if (self->list_row == NULL)
return;
gtk_tree_list_row_set_expanded (self->list_row, TRUE);
}
static void
gtk_tree_expander_collapse (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
{
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
if (self->list_row == NULL)
return;
gtk_tree_list_row_set_expanded (self->list_row, FALSE);
}
static void
gtk_tree_expander_toggle_expand (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
{
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
if (self->list_row == NULL)
return;
gtk_tree_list_row_set_expanded (self->list_row, !gtk_tree_list_row_get_expanded (self->list_row));
}
static void
expand_collapse_right (GtkWidget *widget,
GVariant *args,
gpointer unused)
{
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
if (self->list_row == NULL)
return;
gtk_tree_list_row_set_expanded (self->list_row, gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL);
}
static void
expand_collapse_left (GtkWidget *widget,
GVariant *args,
gpointer unused)
{
GtkTreeExpander *self = GTK_TREE_EXPANDER (widget);
if (self->list_row == NULL)
return;
gtk_tree_list_row_set_expanded (self->list_row, gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
}
static void
gtk_tree_expander_class_init (GtkTreeExpanderClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkBindingSet *binding_set;
widget_class->focus = gtk_tree_expander_focus;
widget_class->grab_focus = gtk_tree_expander_grab_focus;
gobject_class->dispose = gtk_tree_expander_dispose;
gobject_class->get_property = gtk_tree_expander_get_property;
gobject_class->set_property = gtk_tree_expander_set_property;
/**
* GtkTreeExpander:child:
*
* The child widget with the actual contents
*/
properties[PROP_CHILD] =
g_param_spec_object ("child",
P_("Child"),
P_("The child widget with the actual contents"),
GTK_TYPE_WIDGET,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkTreeExpander:item:
*
* The item held by this expander's row
*/
properties[PROP_ITEM] =
g_param_spec_object ("item",
P_("Item"),
P_("The item held by this expander's row"),
G_TYPE_OBJECT,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkTreeExpander:list-row:
*
* The list row to track for expander state
*/
properties[PROP_LIST_ROW] =
g_param_spec_object ("list-row",
P_("List row"),
P_("The list row to track for expander state"),
GTK_TYPE_TREE_LIST_ROW,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
/**
* GtkTreeExpander|listitem.expand:
*
* Expands the expander if it can be expanded.
*/
gtk_widget_class_install_action (widget_class,
"listitem.expand",
NULL,
gtk_tree_expander_expand);
/**
* GtkTreeExpander|listitem.collapse:
*
* Collapses the expander.
*/
gtk_widget_class_install_action (widget_class,
"listitem.collapse",
NULL,
gtk_tree_expander_collapse);
/**
* GtkTreeExpander|listitem.toggle-expand:
*
* Tries to expand the expander if it was collapsed or collapses it if
* it was expanded.
*/
gtk_widget_class_install_action (widget_class,
"listitem.toggle-expand",
NULL,
gtk_tree_expander_toggle_expand);
binding_set = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_action (binding_set, GDK_KEY_plus, 0,
"listitem.expand", NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Add, 0,
"listitem.expand", NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_asterisk, 0,
"listitem.expand", NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Multiply, 0,
"listitem.expand", NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_minus, 0,
"listitem.collapse", NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Subtract, 0,
"listitem.collapse", NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_slash, 0,
"listitem.collapse", NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Divide, 0,
"listitem.collapse", NULL);
gtk_binding_entry_add_callback (binding_set, GDK_KEY_Right, GDK_SHIFT_MASK,
expand_collapse_right, NULL, NULL, NULL);
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Right, GDK_SHIFT_MASK,
expand_collapse_right, NULL, NULL, NULL);
gtk_binding_entry_add_callback (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
expand_collapse_right, NULL, NULL, NULL);
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
expand_collapse_right, NULL, NULL, NULL);
gtk_binding_entry_add_callback (binding_set, GDK_KEY_Left, GDK_SHIFT_MASK,
expand_collapse_left, NULL, NULL, NULL);
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Left, GDK_SHIFT_MASK,
expand_collapse_left, NULL, NULL, NULL);
gtk_binding_entry_add_callback (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
expand_collapse_left, NULL, NULL, NULL);
gtk_binding_entry_add_callback (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
expand_collapse_left, NULL, NULL, NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
"listitem.toggle-expand", NULL);
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
"listitem.toggle-expand", NULL);
#if 0
/* These can't be implementes yet. */
gtk_binding_entry_add_callback (binding_set, GDK_KEY_BackSpace, 0, go_to_parent_row, NULL, NULL);
gtk_binding_entry_add_callback (binding_set, GDK_KEY_BackSpace, GDK_CONTROL_MASK, go_to_parent_row, NULL, NULL);
#endif
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
gtk_widget_class_set_css_name (widget_class, I_("treeexpander"));
}
static void
gtk_tree_expander_init (GtkTreeExpander *self)
{
gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
}
/**
* gtk_tree_expander_new:
*
* Creates a new #GtkTreeExpander
*
* Returns: a new #GtkTreeExpander
**/
GtkWidget *
gtk_tree_expander_new (void)
{
return g_object_new (GTK_TYPE_TREE_EXPANDER,
NULL);
}
/**
* gtk_tree_expander_get_child
* @self: a #GtkTreeExpander
*
* Gets the child widget displayed by @self.
*
* Returns: (nullable) (transfer none): The child displayed by @self
**/
GtkWidget *
gtk_tree_expander_get_child (GtkTreeExpander *self)
{
g_return_val_if_fail (GTK_IS_TREE_EXPANDER (self), NULL);
return self->child;
}
/**
* gtk_tree_expander_set_child:
* @self: a #GtkTreeExpander widget
* @child: (nullable): a #GtkWidget, or %NULL
*
* Sets the content widget to display.
*/
void
gtk_tree_expander_set_child (GtkTreeExpander *self,
GtkWidget *child)
{
g_return_if_fail (GTK_IS_TREE_EXPANDER (self));
g_return_if_fail (child == NULL || GTK_IS_WIDGET (child));
if (self->child == child)
return;
g_clear_pointer (&self->child, gtk_widget_unparent);
if (child)
{
self->child = child;
gtk_widget_set_parent (child, GTK_WIDGET (self));
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHILD]);
}
/**
* gtk_tree_expander_get_item
* @self: a #GtkTreeExpander
*
* Forwards the item set on the #GtkTreeListRow that @self is managing.
*
* This call is essentially equivalent to calling
* `gtk_tree_list_row_get_item (gtk_tree_expander_get_list_row (@self))`.
*
* Returns: (nullable) (transfer none): The item of the row
**/
gpointer
gtk_tree_expander_get_item (GtkTreeExpander *self)
{
g_return_val_if_fail (GTK_IS_TREE_EXPANDER (self), NULL);
if (self->list_row == NULL)
return NULL;
return gtk_tree_list_row_get_item (self->list_row);
}
/**
* gtk_tree_expander_get_list_row
* @self: a #GtkTreeExpander
*
* Gets the list row managed by @self.
*
* Returns: (nullable) (transfer none): The list row displayed by @self
**/
GtkTreeListRow *
gtk_tree_expander_get_list_row (GtkTreeExpander *self)
{
g_return_val_if_fail (GTK_IS_TREE_EXPANDER (self), NULL);
return self->list_row;
}
/**
* gtk_tree_expander_set_list_row:
* @self: a #GtkTreeExpander widget
* @list_row: (nullable): a #GtkTreeListRow, or %NULL
*
* Sets the tree list row that this expander should manage.
*/
void
gtk_tree_expander_set_list_row (GtkTreeExpander *self,
GtkTreeListRow *list_row)
{
g_return_if_fail (GTK_IS_TREE_EXPANDER (self));
g_return_if_fail (list_row == NULL || GTK_IS_TREE_LIST_ROW (list_row));
if (self->list_row == list_row)
return;
g_object_freeze_notify (G_OBJECT (self));
gtk_tree_expander_clear_list_row (self);
if (list_row)
{
self->list_row = g_object_ref (list_row);
self->notify_handler = g_signal_connect (list_row,
"notify",
G_CALLBACK (gtk_tree_expander_list_row_notify_cb),
self);
}
gtk_tree_expander_update_for_list_row (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LIST_ROW]);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
g_object_thaw_notify (G_OBJECT (self));
}
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_TREE_EXPANDER_H__
#define __GTK_TREE_EXPANDER_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtktreelistmodel.h>
#include <gtk/gtkwidget.h>
G_BEGIN_DECLS
#define GTK_TYPE_TREE_EXPANDER (gtk_tree_expander_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkTreeExpander, gtk_tree_expander, GTK, TREE_EXPANDER, GtkWidget)
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_tree_expander_new (void);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_tree_expander_get_child (GtkTreeExpander *self);
GDK_AVAILABLE_IN_ALL
void gtk_tree_expander_set_child (GtkTreeExpander *self,
GtkWidget *child);
GDK_AVAILABLE_IN_ALL
gpointer gtk_tree_expander_get_item (GtkTreeExpander *self);
GDK_AVAILABLE_IN_ALL
GtkTreeListRow * gtk_tree_expander_get_list_row (GtkTreeExpander *self);
GDK_AVAILABLE_IN_ALL
void gtk_tree_expander_set_list_row (GtkTreeExpander *self,
GtkTreeListRow *list_row);
G_END_DECLS
#endif /* __GTK_TREE_EXPANDER_H__ */
+2
View File
@@ -39,6 +39,8 @@ typedef struct _GtkClipboard GtkClipboard;
typedef struct _GtkEventController GtkEventController;
typedef struct _GtkGesture GtkGesture;
typedef struct _GtkLayoutManager GtkLayoutManager;
typedef struct _GtkListItem GtkListItem;
typedef struct _GtkListItemFactory GtkListItemFactory;
typedef struct _GtkNative GtkNative;
typedef struct _GtkRequisition GtkRequisition;
typedef struct _GtkRoot GtkRoot;
+26 -1
View File
@@ -6549,7 +6549,7 @@ gtk_widget_reposition_after (GtkWidget *widget,
if (parent->priv->children_observer)
{
if (prev_previous)
g_warning ("oops");
gtk_list_list_model_item_moved (parent->priv->children_observer, widget, prev_previous);
else
gtk_list_list_model_item_added (parent->priv->children_observer, widget);
}
@@ -13402,6 +13402,31 @@ gtk_widget_get_height (GtkWidget *widget)
return priv->height;
}
/**
* gtk_widget_get_size:
* @widget: a #GtkWidget
* @orientation: the orientation to query
*
* Returns the content width or height of the widget, depending on @orientation.
* This is equivalent to calling gtk_widget_get_width() for %GTK_ORIENTATION_HORIZONTAL
* or gtk_widget_get_height() for %GTK_ORIENTATION_VERTICAL, but can be used when
* writing orientation-independent code, such as when implementing #GtkOrientable
* widgets.
*
* Returns: The size of @widget in @orientation.
*/
int
gtk_widget_get_size (GtkWidget *widget,
GtkOrientation orientation)
{
g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
return gtk_widget_get_width (widget);
else
return gtk_widget_get_height (widget);
}
/**
* gtk_widget_class_set_layout_manager_type:
* @widget_class: class to set the layout manager type for
+3
View File
@@ -576,6 +576,9 @@ GDK_AVAILABLE_IN_ALL
int gtk_widget_get_width (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
int gtk_widget_get_height (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
int gtk_widget_get_size (GtkWidget *widget,
GtkOrientation orientation);
GDK_AVAILABLE_IN_ALL
gboolean gtk_widget_child_focus (GtkWidget *widget,
+7 -30
View File
@@ -46,6 +46,7 @@
#include "gtksizegroup.h"
#include "gtktextview.h"
#include "gtktogglebutton.h"
#include "gtktreeexpander.h"
#include "gtktreelistmodel.h"
#include "gtktreeview.h"
#include "gtktreeselection.h"
@@ -1019,7 +1020,6 @@ gtk_inspector_object_tree_create_list_widget (gpointer row_item,
GtkInspectorObjectTree *wt = user_data;
gpointer item;
GtkWidget *row, *box, *column, *child;
guint depth;
item = gtk_tree_list_row_get_item (row_item);
@@ -1036,37 +1036,14 @@ gtk_inspector_object_tree_create_list_widget (gpointer row_item,
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_container_add (GTK_CONTAINER (row), box);
column = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_size_group_add_widget (wt->priv->type_size_group, column);
gtk_container_add (GTK_CONTAINER (box), column);
/* expander */
depth = gtk_tree_list_row_get_depth (row_item);
if (depth > 0)
{
child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_size_request (child, 16 * depth, 0);
gtk_container_add (GTK_CONTAINER (column), child);
}
if (gtk_tree_list_row_is_expandable (row_item))
{
GtkWidget *title, *arrow;
child = gtk_tree_expander_new ();
gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (child), row_item);
gtk_size_group_add_widget (wt->priv->type_size_group, child);
gtk_container_add (GTK_CONTAINER (box), child);
child = g_object_new (GTK_TYPE_BOX, "css-name", "expander", NULL);
title = g_object_new (GTK_TYPE_TOGGLE_BUTTON, "css-name", "title", NULL);
gtk_button_set_relief (GTK_BUTTON (title), GTK_RELIEF_NONE);
g_object_bind_property (row_item, "expanded", title, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_container_add (GTK_CONTAINER (child), title);
arrow = gtk_icon_new ("arrow");
gtk_container_add (GTK_CONTAINER (title), arrow);
}
else
{
child = gtk_image_new (); /* empty whatever */
}
gtk_container_add (GTK_CONTAINER (column), child);
column = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (child), column);
/* 1st column: type name */
child = gtk_label_new (G_OBJECT_TYPE_NAME (item));
+74 -78
View File
@@ -21,12 +21,16 @@
#include <gtk/gtkbox.h>
#include <gtk/gtkfilechooserdialog.h>
#include <gtk/gtkfunctionslistitemfactory.h>
#include <gtk/gtklabel.h>
#include <gtk/gtklistbox.h>
#include <gtk/gtklistview.h>
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkpicture.h>
#include <gtk/gtkpopover.h>
#include <gtk/gtksingleselection.h>
#include <gtk/gtktogglebutton.h>
#include <gtk/gtktreeexpander.h>
#include <gtk/gtktreelistmodel.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtktreeview.h>
@@ -49,6 +53,8 @@ struct _GtkInspectorRecorderPrivate
{
GListModel *recordings;
GtkTreeListModel *render_node_model;
GListStore *render_node_root_model;
GtkSingleSelection *render_node_selection;
GtkWidget *recordings_list;
GtkWidget *render_node_view;
@@ -293,64 +299,56 @@ node_name (GskRenderNode *node)
}
}
static GtkWidget *
create_widget_for_render_node (gpointer row_item,
gpointer unused)
static void
setup_widget_for_render_node (GtkListItem *list_item,
gpointer unused)
{
GdkPaintable *paintable;
GskRenderNode *node;
GtkWidget *row, *box, *child;
char *name;
guint depth;
paintable = gtk_tree_list_row_get_item (row_item);
node = gtk_render_node_paintable_get_render_node (GTK_RENDER_NODE_PAINTABLE (paintable));
row = gtk_list_box_row_new ();
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
gtk_container_add (GTK_CONTAINER (row), box);
GtkWidget *expander, *box, *child;
/* expander */
depth = gtk_tree_list_row_get_depth (row_item);
if (depth > 0)
{
child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_size_request (child, 16 * depth, 0);
gtk_container_add (GTK_CONTAINER (box), child);
}
if (gtk_tree_list_row_is_expandable (row_item))
{
GtkWidget *title, *arrow;
expander = gtk_tree_expander_new ();
gtk_container_add (GTK_CONTAINER (list_item), expander);
child = g_object_new (GTK_TYPE_BOX, "css-name", "expander", NULL);
title = g_object_new (GTK_TYPE_TOGGLE_BUTTON, "css-name", "title", NULL);
gtk_button_set_relief (GTK_BUTTON (title), GTK_RELIEF_NONE);
g_object_bind_property (row_item, "expanded", title, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_container_add (GTK_CONTAINER (child), title);
g_object_set_data_full (G_OBJECT (row), "make-sure-its-not-unreffed", g_object_ref (row_item), g_object_unref);
arrow = gtk_icon_new ("arrow");
gtk_container_add (GTK_CONTAINER (title), arrow);
}
else
{
child = gtk_image_new (); /* empty whatever */
}
gtk_container_add (GTK_CONTAINER (box), child);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), box);
/* icon */
child = gtk_image_new_from_paintable (paintable);
child = gtk_image_new ();
gtk_container_add (GTK_CONTAINER (box), child);
/* name */
name = node_name (node);
child = gtk_label_new (name);
g_free (name);
child = gtk_label_new (NULL);
gtk_container_add (GTK_CONTAINER (box), child);
}
g_object_unref (paintable);
static void
bind_widget_for_render_node (GtkListItem *list_item,
gpointer unused)
{
GdkPaintable *paintable;
GskRenderNode *node;
GtkTreeListRow *row_item;
GtkWidget *expander, *box, *child;
char *name;
return row;
row_item = gtk_list_item_get_item (list_item);
paintable = gtk_tree_list_row_get_item (row_item);
node = gtk_render_node_paintable_get_render_node (GTK_RENDER_NODE_PAINTABLE (paintable));
/* expander */
expander = gtk_bin_get_child (GTK_BIN (list_item));
gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (expander), row_item);
box = gtk_tree_expander_get_child (GTK_TREE_EXPANDER (expander));
/* icon */
child = gtk_widget_get_first_child (box);
gtk_image_set_from_paintable (GTK_IMAGE (child), paintable);
/* name */
name = node_name (node);
child = gtk_widget_get_last_child (box);
gtk_label_set_label (GTK_LABEL (child), name);
g_free (name);
}
static void
@@ -366,11 +364,8 @@ recordings_list_row_selected (GtkListBox *box,
else
recording = NULL;
g_clear_object (&priv->render_node_model);
if (GTK_INSPECTOR_IS_RENDER_RECORDING (recording))
{
GListStore *root_model;
graphene_rect_t bounds;
GskRenderNode *node;
GdkPaintable *paintable;
@@ -380,14 +375,10 @@ recordings_list_row_selected (GtkListBox *box,
paintable = gtk_render_node_paintable_new (node, &bounds);
gtk_picture_set_paintable (GTK_PICTURE (priv->render_node_view), paintable);
root_model = g_list_store_new (GDK_TYPE_PAINTABLE);
g_list_store_append (root_model, paintable);
priv->render_node_model = gtk_tree_list_model_new (FALSE,
G_LIST_MODEL (root_model),
TRUE,
create_list_model_for_render_node_paintable,
NULL, NULL);
g_object_unref (root_model);
g_list_store_splice (priv->render_node_root_model,
0, g_list_model_get_n_items (G_LIST_MODEL (priv->render_node_root_model)),
(gpointer[1]) { paintable },
1);
g_object_unref (paintable);
g_print ("%u render nodes\n", g_list_model_get_n_items (G_LIST_MODEL (priv->render_node_model)));
@@ -395,14 +386,9 @@ recordings_list_row_selected (GtkListBox *box,
else
{
gtk_picture_set_paintable (GTK_PICTURE (priv->render_node_view), NULL);
g_list_store_remove_all (priv->render_node_root_model);
}
gtk_list_box_bind_model (GTK_LIST_BOX (priv->render_node_list),
G_LIST_MODEL (priv->render_node_model),
create_widget_for_render_node,
NULL, NULL);
if (recording)
g_object_unref (recording);
}
@@ -919,20 +905,16 @@ get_selected_node (GtkInspectorRecorder *recorder)
{
GtkInspectorRecorderPrivate *priv = gtk_inspector_recorder_get_instance_private (recorder);
GtkTreeListRow *row_item;
GtkListBoxRow *row;
GdkPaintable *paintable;
GskRenderNode *node;
row = gtk_list_box_get_selected_row (GTK_LIST_BOX (priv->render_node_list));
if (row == NULL)
row_item = gtk_single_selection_get_selected_item (priv->render_node_selection);
if (row_item == NULL)
return NULL;
row_item = g_list_model_get_item (G_LIST_MODEL (priv->render_node_model),
gtk_list_box_row_get_index (row));
paintable = gtk_tree_list_row_get_item (row_item);
node = gtk_render_node_paintable_get_render_node (GTK_RENDER_NODE_PAINTABLE (paintable));
g_object_unref (paintable);
g_object_unref (row_item);
return node;
}
@@ -947,14 +929,10 @@ render_node_list_selection_changed (GtkListBox *list,
GdkPaintable *paintable;
GtkTreeListRow *row_item;
if (row == NULL)
{
gtk_widget_set_sensitive (priv->render_node_save_button, FALSE);
return;
}
row_item = gtk_single_selection_get_selected_item (priv->render_node_selection);
if (row_item == NULL)
return;
row_item = g_list_model_get_item (G_LIST_MODEL (priv->render_node_model),
gtk_list_box_row_get_index (row));
paintable = gtk_tree_list_row_get_item (row_item);
gtk_widget_set_sensitive (priv->render_node_save_button, TRUE);
@@ -963,7 +941,6 @@ render_node_list_selection_changed (GtkListBox *list,
populate_render_node_properties (GTK_LIST_STORE (priv->render_node_properties), node);
g_object_unref (paintable);
g_object_unref (row_item);
}
static void
@@ -1233,6 +1210,8 @@ gtk_inspector_recorder_dispose (GObject *object)
GtkInspectorRecorderPrivate *priv = gtk_inspector_recorder_get_instance_private (recorder);
g_clear_object (&priv->render_node_model);
g_clear_object (&priv->render_node_root_model);
g_clear_object (&priv->render_node_selection);
G_OBJECT_CLASS (gtk_inspector_recorder_parent_class)->dispose (object);
}
@@ -1273,7 +1252,6 @@ gtk_inspector_recorder_class_init (GtkInspectorRecorderClass *klass)
gtk_widget_class_bind_template_callback (widget_class, recordings_clear_all);
gtk_widget_class_bind_template_callback (widget_class, recordings_list_row_selected);
gtk_widget_class_bind_template_callback (widget_class, render_node_list_selection_changed);
gtk_widget_class_bind_template_callback (widget_class, render_node_save);
gtk_widget_class_bind_template_callback (widget_class, node_property_activated);
}
@@ -1282,6 +1260,7 @@ static void
gtk_inspector_recorder_init (GtkInspectorRecorder *recorder)
{
GtkInspectorRecorderPrivate *priv = gtk_inspector_recorder_get_instance_private (recorder);
GtkListItemFactory *factory;
gtk_widget_init_template (GTK_WIDGET (recorder));
@@ -1291,6 +1270,23 @@ gtk_inspector_recorder_init (GtkInspectorRecorder *recorder)
recorder,
NULL);
priv->render_node_root_model = g_list_store_new (GDK_TYPE_PAINTABLE);
priv->render_node_model = gtk_tree_list_model_new (FALSE,
G_LIST_MODEL (priv->render_node_root_model),
TRUE,
create_list_model_for_render_node_paintable,
NULL, NULL);
priv->render_node_selection = gtk_single_selection_new (G_LIST_MODEL (priv->render_node_model));
g_signal_connect (priv->render_node_selection, "notify::selected-item", G_CALLBACK (render_node_list_selection_changed), recorder);
factory = gtk_functions_list_item_factory_new (setup_widget_for_render_node,
bind_widget_for_render_node,
NULL, NULL);
gtk_list_view_set_factory (GTK_LIST_VIEW (priv->render_node_list), factory);
g_object_unref (factory);
gtk_list_view_set_model (GTK_LIST_VIEW (priv->render_node_list),
G_LIST_MODEL (priv->render_node_selection));
priv->render_node_properties = GTK_TREE_MODEL (gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, GDK_TYPE_TEXTURE));
gtk_tree_view_set_model (GTK_TREE_VIEW (priv->node_property_tree), priv->render_node_properties);
g_object_unref (priv->render_node_properties);
+2 -3
View File
@@ -73,15 +73,14 @@
<object class="GtkFrame">
<child>
<object class="GtkScrolledWindow">
<property name="hscrollbar-policy">never</property>
<property name="propagate-natural-width">1</property>
<style>
<class name="sidebar"/>
</style>
<child>
<object class="GtkListBox" id="render_node_list">
<object class="GtkListView" id="render_node_list">
<property name="vexpand">1</property>
<signal name="row-selected" handler="render_node_list_selection_changed"/>
<property name="hexpand">1</property>
</object>
</child>
</object>
+26
View File
@@ -177,6 +177,7 @@ gtk_public_sources = files([
'gtkbuildable.c',
'gtkbuilder.c',
'gtkbuilderparser.c',
'gtkbuilderlistitemfactory.c',
'gtkbutton.c',
'gtkcalendar.c',
'gtkcellarea.c',
@@ -203,6 +204,8 @@ gtk_public_sources = files([
'gtkcolorchooserdialog.c',
'gtkcolorchooserwidget.c',
'gtkcolorutils.c',
'gtkcolumnview.c',
'gtkcolumnviewcolumn.c',
'gtkcombobox.c',
'gtkcomboboxtext.c',
'gtkcomposetable.c',
@@ -210,8 +213,10 @@ gtk_public_sources = files([
'gtkconstraintlayout.c',
'gtkconstraint.c',
'gtkcontainer.c',
'gtkcoverflow.c',
'gtkcssprovider.c',
'gtkdialog.c',
'gtkdirectorylist.c',
'gtkdnd.c',
'gtkdragdest.c',
'gtkdragsource.c',
@@ -228,6 +233,7 @@ gtk_public_sources = files([
'gtkeventcontrollermotion.c',
'gtkeventcontrollerscroll.c',
'gtkexpander.c',
'gtkexpression.c',
'gtkfilechooser.c',
'gtkfilechooserbutton.c',
'gtkfilechooserdialog.c',
@@ -245,6 +251,7 @@ gtk_public_sources = files([
'gtkfontchooserutils.c',
'gtkfontchooserwidget.c',
'gtkframe.c',
'gtkfunctionslistitemfactory.c',
'gtkgesture.c',
'gtkgesturedrag.c',
'gtkgesturelongpress.c',
@@ -258,6 +265,7 @@ gtk_public_sources = files([
'gtkglarea.c',
'gtkgrid.c',
'gtkgridlayout.c',
'gtkgridview.c',
'gtkheaderbar.c',
'gtkicontheme.c',
'gtkiconview.c',
@@ -272,10 +280,15 @@ gtk_public_sources = files([
'gtklayoutchild.c',
'gtklayoutmanager.c',
'gtklevelbar.c',
'gtklistbase.c',
'gtklinkbutton.c',
'gtklistbox.c',
'gtklistitem.c',
'gtklistitemfactory.c',
'gtklistitemmanager.c',
'gtklistlistmodel.c',
'gtkliststore.c',
'gtklistview.c',
'gtklockbutton.c',
'gtkmain.c',
'gtkmaplistmodel.c',
@@ -390,6 +403,7 @@ gtk_public_sources = files([
'gtktooltip.c',
'gtktooltipwindow.c',
'gtktreednd.c',
'gtktreeexpander.c',
'gtktreelistmodel.c',
'gtktreemenu.c',
'gtktreemodel.c',
@@ -442,6 +456,7 @@ gtk_public_headers = files([
'gtkboxlayout.h',
'gtkbuildable.h',
'gtkbuilder.h',
'gtkbuilderlistitemfactory.h',
'gtkbutton.h',
'gtkcalendar.h',
'gtkcenterbox.h',
@@ -468,16 +483,20 @@ gtk_public_headers = files([
'gtkcolorchooserdialog.h',
'gtkcolorchooserwidget.h',
'gtkcolorutils.h',
'gtkcolumnview.h',
'gtkcolumnviewcolumn.h',
'gtkcombobox.h',
'gtkcomboboxtext.h',
'gtkconstraintguide.h',
'gtkconstraintlayout.h',
'gtkconstraint.h',
'gtkcontainer.h',
'gtkcoverflow.h',
'gtkcssprovider.h',
'gtkcustomlayout.h',
'gtkdebug.h',
'gtkdialog.h',
'gtkdirectorylist.h',
'gtkdnd.h',
'gtkdragdest.h',
'gtkdragsource.h',
@@ -509,6 +528,7 @@ gtk_public_headers = files([
'gtkfontchooserdialog.h',
'gtkfontchooserwidget.h',
'gtkframe.h',
'gtkfunctionslistitemfactory.h',
'gtkgesture.h',
'gtkgesturedrag.h',
'gtkgesturelongpress.h',
@@ -522,6 +542,7 @@ gtk_public_headers = files([
'gtkglarea.h',
'gtkgrid.h',
'gtkgridlayout.h',
'gtkgridview.h',
'gtkheaderbar.h',
'gtkicontheme.h',
'gtkiconview.h',
@@ -536,8 +557,12 @@ gtk_public_headers = files([
'gtklayoutmanager.h',
'gtklevelbar.h',
'gtklinkbutton.h',
'gtklistbase.h',
'gtklistbox.h',
'gtklistitem.h',
'gtklistitemfactory.h',
'gtkliststore.h',
'gtklistview.h',
'gtklockbutton.h',
'gtkmain.h',
'gtkmaplistmodel.h',
@@ -632,6 +657,7 @@ gtk_public_headers = files([
'gtktoolshell.h',
'gtktooltip.h',
'gtktreednd.h',
'gtktreeexpander.h',
'gtktreelistmodel.h',
'gtktreemodel.h',
'gtktreemodelfilter.h',
+10 -1
View File
@@ -160,6 +160,12 @@ flowbox {
}
}
coverflow cover {
color: $text_color;
background-color: $base_color;
border: 1px solid black;
}
.content-view .tile {
margin: 2px;
background-color: if($variant=='light', transparent, black);
@@ -3730,7 +3736,10 @@ list {
row.expander { padding: 0px; }
row.expander .row-header { padding: 2px; }
&.separators row:not(:first-child) {
&.separators.horizontal row:not(:first-child) {
border-left: 1px solid $borders_color;
}
&.separators:not(.horizontal) row:not(:first-child) {
border-top: 1px solid $borders_color;
}
}
+47 -45
View File
@@ -48,6 +48,8 @@ flowbox flowboxchild { padding: 3px; }
flowbox flowboxchild:selected { outline-offset: -2px; }
coverflow cover { color: white; background-color: #2d2d2d; }
.content-view .tile { margin: 2px; background-color: black; border-radius: 0; padding: 0; }
.content-view .tile:backdrop { background-color: #232323; }
@@ -82,9 +84,9 @@ assistant .sidebar label { padding: 6px 12px; }
assistant .sidebar label.highlight { background-color: #5a5a59; }
.csd popover.background > contents.touch-selection, .csd popover.background > contents.magnifier, popover.background > contents.touch-selection, popover.background > contents.magnifier, .csd popover.background > contents.osd, popover.background > contents.osd, .app-notification, .app-notification.frame, .osd .scale-popup, .osd { color: #eeeeec; border: none; background-color: rgba(37, 37, 38, 0.7); background-clip: padding-box; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
.osd popover.background > arrow, .osd popover.background > contents, popover.background.touch-selection > arrow, popover.background.touch-selection > contents, popover.background.magnifier > arrow, popover.background.magnifier > contents, .app-notification, .app-notification.frame, .osd .scale-popup, .osd { color: #eeeeec; border: none; background-color: rgba(38, 38, 38, 0.7); background-clip: padding-box; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
.csd popover.background > contents.touch-selection:backdrop, .csd popover.background > contents.magnifier:backdrop, popover.background > contents.touch-selection:backdrop, popover.background > contents.magnifier:backdrop, .csd popover.background > contents.osd:backdrop, popover.background > contents.osd:backdrop, .app-notification:backdrop, .osd .scale-popup:backdrop, .osd:backdrop { text-shadow: none; -gtk-icon-shadow: none; }
.osd popover.background > arrow:backdrop, .osd popover.background > contents:backdrop, popover.background.touch-selection > arrow:backdrop, popover.background.touch-selection > contents:backdrop, popover.background.magnifier > arrow:backdrop, popover.background.magnifier > contents:backdrop, .app-notification:backdrop, .osd .scale-popup:backdrop, .osd:backdrop { text-shadow: none; -gtk-icon-shadow: none; }
/********************* Spinner Animation * */
@keyframes spin { to { -gtk-icon-transform: rotate(1turn); } }
@@ -267,7 +269,7 @@ row:selected button.sidebar-button:not(:active):not(:checked):not(:hover):not(di
row:selected button.sidebar-button:not(:active):not(:checked):not(:hover):not(disabled):backdrop, row:selected button.flat:not(:active):not(:checked):not(:hover):not(disabled):backdrop { color: #919190; }
button.osd { min-width: 26px; min-height: 32px; color: #eeeeec; border-radius: 5px; color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); border: none; box-shadow: none; }
button.osd { min-width: 26px; min-height: 32px; color: #eeeeec; border-radius: 5px; color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); border: none; box-shadow: none; }
button.osd.image-button { min-width: 30px; }
@@ -279,27 +281,27 @@ button.osd:active, button.osd:checked { color: white; border-color: rgba(0, 0, 0
button.osd:disabled:backdrop, button.osd:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; border: none; }
button.osd:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; border: none; }
button.osd:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; border: none; }
.csd popover.background > contents.touch-selection button, .csd popover.background > contents.magnifier button, popover.background > contents.touch-selection button, popover.background > contents.magnifier button, .app-notification button, .app-notification.frame button, .osd button { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
popover.background.touch-selection button, popover.background.magnifier button, .app-notification button, .app-notification.frame button, .osd button { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.csd popover.background > contents.touch-selection button:hover, .csd popover.background > contents.magnifier button:hover, popover.background > contents.touch-selection button:hover, popover.background > contents.magnifier button:hover, .app-notification button:hover, .osd button:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
popover.background.touch-selection button:hover, popover.background.magnifier button:hover, .app-notification button:hover, .osd button:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.csd popover.background > contents.touch-selection button:active:backdrop, .csd popover.background > contents.magnifier button:active:backdrop, popover.background > contents.touch-selection button:active:backdrop, popover.background > contents.magnifier button:active:backdrop, .app-notification button:active:backdrop, .csd popover.background > contents.touch-selection button:active, .csd popover.background > contents.magnifier button:active, popover.background > contents.touch-selection button:active, popover.background > contents.magnifier button:active, .app-notification button:active, .csd popover.background > contents.touch-selection button:checked:backdrop, .csd popover.background > contents.magnifier button:checked:backdrop, popover.background > contents.touch-selection button:checked:backdrop, popover.background > contents.magnifier button:checked:backdrop, .app-notification button:checked:backdrop, .csd popover.background > contents.touch-selection button:checked, .csd popover.background > contents.magnifier button:checked, popover.background > contents.touch-selection button:checked, popover.background > contents.magnifier button:checked, .app-notification button:checked, .osd button:active:backdrop, .osd button:active, .osd button:checked:backdrop, .osd button:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
popover.background.touch-selection button:active:backdrop, popover.background.magnifier button:active:backdrop, .app-notification button:active:backdrop, popover.background.touch-selection button:active, popover.background.magnifier button:active, .app-notification button:active, popover.background.touch-selection button:checked:backdrop, popover.background.magnifier button:checked:backdrop, .app-notification button:checked:backdrop, popover.background.touch-selection button:checked, popover.background.magnifier button:checked, .app-notification button:checked, .osd button:active:backdrop, .osd button:active, .osd button:checked:backdrop, .osd button:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.csd popover.background > contents.touch-selection button:disabled:backdrop, .csd popover.background > contents.magnifier button:disabled:backdrop, popover.background > contents.touch-selection button:disabled:backdrop, popover.background > contents.magnifier button:disabled:backdrop, .app-notification button:disabled:backdrop, .csd popover.background > contents.touch-selection button:disabled, .csd popover.background > contents.magnifier button:disabled, popover.background > contents.touch-selection button:disabled, popover.background > contents.magnifier button:disabled, .app-notification button:disabled, .osd button:disabled:backdrop, .osd button:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
popover.background.touch-selection button:disabled:backdrop, popover.background.magnifier button:disabled:backdrop, .app-notification button:disabled:backdrop, popover.background.touch-selection button:disabled, popover.background.magnifier button:disabled, .app-notification button:disabled, .osd button:disabled:backdrop, .osd button:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
.csd popover.background > contents.touch-selection button:backdrop, .csd popover.background > contents.magnifier button:backdrop, popover.background > contents.touch-selection button:backdrop, popover.background > contents.magnifier button:backdrop, .app-notification button:backdrop, .osd button:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
popover.background.touch-selection button:backdrop, popover.background.magnifier button:backdrop, .app-notification button:backdrop, .osd button:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
.csd popover.background > contents.touch-selection button.flat, .csd popover.background > contents.magnifier button.flat, popover.background > contents.touch-selection button.flat, popover.background > contents.magnifier button.flat, .app-notification button.flat, .osd button.flat { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; box-shadow: none; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
popover.background.touch-selection button.flat, popover.background.magnifier button.flat, .app-notification button.flat, .osd button.flat { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; box-shadow: none; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
.csd popover.background > contents.touch-selection button.flat:hover, .csd popover.background > contents.magnifier button.flat:hover, popover.background > contents.touch-selection button.flat:hover, popover.background > contents.magnifier button.flat:hover, .app-notification button.flat:hover, .osd button.flat:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
popover.background.touch-selection button.flat:hover, popover.background.magnifier button.flat:hover, .app-notification button.flat:hover, .osd button.flat:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.csd popover.background > contents.touch-selection button.flat:disabled, .csd popover.background > contents.magnifier button.flat:disabled, popover.background > contents.touch-selection button.flat:disabled, popover.background > contents.magnifier button.flat:disabled, .app-notification button.flat:disabled, .osd button.flat:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-image: none; border-color: transparent; box-shadow: none; }
popover.background.touch-selection button.flat:disabled, popover.background.magnifier button.flat:disabled, .app-notification button.flat:disabled, .osd button.flat:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-image: none; border-color: transparent; box-shadow: none; }
.csd popover.background > contents.touch-selection button.flat:backdrop, .csd popover.background > contents.magnifier button.flat:backdrop, popover.background > contents.touch-selection button.flat:backdrop, popover.background > contents.magnifier button.flat:backdrop, .app-notification button.flat:backdrop, .osd button.flat:backdrop { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; }
popover.background.touch-selection button.flat:backdrop, popover.background.magnifier button.flat:backdrop, .app-notification button.flat:backdrop, .osd button.flat:backdrop { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; }
.csd popover.background > contents.touch-selection button.flat:active, .csd popover.background > contents.magnifier button.flat:active, popover.background > contents.touch-selection button.flat:active, popover.background > contents.magnifier button.flat:active, .app-notification button.flat:active, .csd popover.background > contents.touch-selection button.flat:checked, .csd popover.background > contents.magnifier button.flat:checked, popover.background > contents.touch-selection button.flat:checked, popover.background > contents.magnifier button.flat:checked, .app-notification button.flat:checked, .osd button.flat:active, .osd button.flat:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
popover.background.touch-selection button.flat:active, popover.background.magnifier button.flat:active, .app-notification button.flat:active, popover.background.touch-selection button.flat:checked, popover.background.magnifier button.flat:checked, .app-notification button.flat:checked, .osd button.flat:active, .osd button.flat:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
button.suggested-action { color: white; outline-color: rgba(255, 255, 255, 0.3); border-color: #0f3b71; border-bottom-color: #092444; background-image: linear-gradient(to top, #155099 2px, #15539e); text-shadow: 0 -1px rgba(0, 0, 0, 0.719216); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.719216); box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px 2px rgba(0, 0, 0, 0.07); }
@@ -499,7 +501,7 @@ button:link > label:active, button:visited > label:active, *:link:active, button
*:selected button:link > label:active, *:selected button:visited > label:active, *:selected *:link:active, *:selected button:active:link, *:selected button:active:visited { color: #d0ddec; }
button:link > label:disabled, button:visited > label:disabled, button:link > label:disabled:backdrop, button:visited > label:disabled:backdrop, *:link:disabled, button:disabled:link, button:disabled:visited, *:link:disabled:backdrop, button:disabled:backdrop:link, button:disabled:backdrop:visited { color: rgba(140, 140, 141, 0.8); }
button:link > label:disabled, button:visited > label:disabled, button:link > label:disabled:backdrop, button:visited > label:disabled:backdrop, *:link:disabled, button:disabled:link, button:disabled:visited, *:link:disabled:backdrop, button:disabled:backdrop:link, button:disabled:backdrop:visited { color: rgba(141, 141, 141, 0.8); }
button:link > label:backdrop:backdrop:hover, button:visited > label:backdrop:backdrop:hover, button:link > label:backdrop:backdrop:hover:selected, button:visited > label:backdrop:backdrop:hover:selected, button:link > label:backdrop, button:visited > label:backdrop, *:link:backdrop:backdrop:hover, button:backdrop:backdrop:hover:link, button:backdrop:backdrop:hover:visited, *:link:backdrop:backdrop:hover:selected, button:backdrop:backdrop:hover:selected:link, button:backdrop:backdrop:hover:selected:visited, .selection-mode .titlebar:not(headerbar) .subtitle:backdrop:backdrop:hover:link, .selection-mode.titlebar:not(headerbar) .subtitle:backdrop:backdrop:hover:link, .selection-mode headerbar .subtitle:backdrop:backdrop:hover:link, headerbar.selection-mode .subtitle:backdrop:backdrop:hover:link, *:link:backdrop, button:backdrop:link, button:backdrop:visited { color: #15539e; }
@@ -566,7 +568,7 @@ spinbutton.vertical button.up { border-bottom-style: none; border-bottom-left-ra
spinbutton.vertical button.down { border-top-style: none; border-top-left-radius: 0; border-top-right-radius: 0; }
.osd spinbutton.vertical button:first-child { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.osd spinbutton.vertical button:first-child { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.osd spinbutton.vertical button:first-child:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
@@ -574,7 +576,7 @@ spinbutton.vertical button.down { border-top-style: none; border-top-left-radius
.osd spinbutton.vertical button:first-child:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
.osd spinbutton.vertical button:first-child:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
.osd spinbutton.vertical button:first-child:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
treeview spinbutton:not(.vertical) { min-height: 0; border-style: none; border-radius: 0; }
@@ -592,7 +594,7 @@ toolbar { padding: 4px 3px 3px 4px; }
.osd toolbar { background-color: transparent; }
toolbar.osd { padding: 13px; border: none; border-radius: 5px; background-color: rgba(37, 37, 38, 0.7); }
toolbar.osd { padding: 13px; border: none; border-radius: 5px; background-color: rgba(38, 38, 38, 0.7); }
toolbar.osd.left, toolbar.osd.right, toolbar.osd.top, toolbar.osd.bottom { border-radius: 0; }
@@ -615,7 +617,7 @@ searchbar > revealer > box { padding: 6px; border-width: 0 0 1px; }
.inline-toolbar:backdrop, .location-bar:backdrop, searchbar > revealer > box:backdrop { border-color: #202020; background-color: #2e2e2e; box-shadow: none; transition: 200ms ease-out; }
/*************** Header bars * */
.titlebar:not(headerbar), headerbar { padding: 0 6px; min-height: 46px; border-width: 0 0 1px; border-style: solid; border-color: #070707; border-radius: 0; border-spacing: 6px; background: #1b1b1b linear-gradient(to top, #252526, #2b2b2b); box-shadow: inset 0 1px rgba(238, 238, 236, 0.07); /* Darken switchbuttons for headerbars. issue #1588 */ /* hide the close button separator */ }
.titlebar:not(headerbar), headerbar { padding: 0 6px; min-height: 46px; border-width: 0 0 1px; border-style: solid; border-color: #070707; border-radius: 0; border-spacing: 6px; background: #1b1b1b linear-gradient(to top, #262626, #2b2b2b); box-shadow: inset 0 1px rgba(238, 238, 236, 0.07); /* Darken switchbuttons for headerbars. issue #1588 */ /* hide the close button separator */ }
.titlebar:backdrop:not(headerbar), headerbar:backdrop { border-color: #202020; background-color: #353535; background-image: none; box-shadow: inset 0 1px rgba(238, 238, 236, 0.07); transition: 200ms ease-out; }
@@ -881,25 +883,23 @@ menuitem check:dir(ltr), menuitem radio:dir(ltr) { margin-right: 7px; }
menuitem check:dir(rtl), menuitem radio:dir(rtl) { margin-left: 7px; }
/*************** Popovers * */
popover.background { background-color: transparent; padding: 0px; }
popover.background { background-color: transparent; font: initial; }
popover.menu > arrow, popover > arrow { background-color: #353535; border: 1px solid #1b1b1b; }
popover.background > arrow, popover.background > contents { background-color: #353535; border: 1px solid #1b1b1b; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); }
popover > contents { padding: 8px; background-color: #353535; border: 1px solid #1b1b1b; margin: 0px; }
popover.background > arrow:backdrop, popover.background > contents:backdrop { background-color: #353535; border-color: #202020; box-shadow: none; }
popover.background > contents { background-color: #353535; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); }
.csd popover.background > contents, popover.background > contents { border: 1px solid #1b1b1b; border-radius: 9px; }
popover.background > contents:backdrop { background-color: #353535; box-shadow: none; }
popover.background > contents { padding: 8px; border-radius: 9px; }
popover.background > contents > list, popover.background > contents > .view, popover.background > contents > iconview, popover.background > contents > toolbar { border-style: none; background-color: transparent; }
.csd popover.background > contents.touch-selection, .csd popover.background > contents.magnifier, popover.background > contents.touch-selection, popover.background > contents.magnifier { border: 1px solid rgba(255, 255, 255, 0.1); }
popover.background > contents separator { margin: 3px; }
popover.background > contents list separator { margin: 0px; }
popover.background > contents list separator { margin: 0; }
.osd popover.background, popover.background.touch-selection, popover.background.magnifier { background-color: transparent; }
.osd popover.background > arrow, .osd popover.background > contents, popover.background.touch-selection > arrow, popover.background.touch-selection > contents, popover.background.magnifier > arrow, popover.background.magnifier > contents { border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: none; }
/************* Notebooks * */
notebook > header { padding: 1px; border-color: #1b1b1b; border-width: 1px; background-color: #282828; }
@@ -1149,7 +1149,7 @@ switch:backdrop:disabled slider label, switch:backdrop:disabled slider { color:
.view.content-view.check:active:not(list), iconview.content-view.check:active:not(list), .content-view .tile check:active:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(21, 83, 158, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
.view.content-view.check:backdrop:not(list), iconview.content-view.check:backdrop:not(list), .content-view .tile check:backdrop:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(89, 89, 90, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
.view.content-view.check:backdrop:not(list), iconview.content-view.check:backdrop:not(list), .content-view .tile check:backdrop:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(90, 90, 90, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
.view.content-view.check:checked:not(list), iconview.content-view.check:checked:not(list), .content-view .tile check:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: #eeeeec; background-color: rgba(21, 83, 158, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
@@ -1157,11 +1157,11 @@ switch:backdrop:disabled slider label, switch:backdrop:disabled slider { color:
.view.content-view.check:checked:active:not(list), iconview.content-view.check:checked:active:not(list), .content-view .tile check:checked:active:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: #eeeeec; background-color: rgba(21, 83, 158, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
.view.content-view.check:backdrop:checked:not(list), iconview.content-view.check:backdrop:checked:not(list), .content-view .tile check:backdrop:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: rgba(238, 238, 236, 0.8); background-color: rgba(89, 89, 90, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
.view.content-view.check:backdrop:checked:not(list), iconview.content-view.check:backdrop:checked:not(list), .content-view .tile check:backdrop:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: rgba(238, 238, 236, 0.8); background-color: rgba(90, 90, 90, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
checkbutton.text-button, radiobutton.text-button { padding: 2px 0; outline-offset: 0; border-spacing: 4px; }
check, radio { margin: 0 4px; min-height: 14px; min-width: 14px; border: 1px solid; -gtk-icon-source: none; color: #eeeeec; outline-color: rgba(238, 238, 236, 0.3); border-color: #070707; text-shadow: 0 -1px rgba(0, 0, 0, 0.834353); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.834353); background-image: linear-gradient(to bottom, #2d2d2d 20%, #252526 90%); box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.07); }
check, radio { margin: 0 4px; min-height: 14px; min-width: 14px; border: 1px solid; -gtk-icon-source: none; color: #eeeeec; outline-color: rgba(238, 238, 236, 0.3); border-color: #070707; text-shadow: 0 -1px rgba(0, 0, 0, 0.834353); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.834353); background-image: linear-gradient(to bottom, #2d2d2d 20%, #262626 90%); box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.07); }
check:only-child, radio:only-child { margin: 0; }
@@ -1189,13 +1189,13 @@ check:backdrop:disabled, radio:backdrop:disabled { border-color: #202020; backgr
check:backdrop:disabled label, check:backdrop:disabled, radio:backdrop:disabled label, radio:backdrop:disabled { color: #5b5b5b; }
.osd check, .osd radio { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.osd check, .osd radio { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.osd check:hover, .osd radio:hover { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.osd check:hover, .osd radio:hover { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.osd check:active, .osd radio:active { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.osd check:backdrop, .osd radio:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
.osd check:backdrop, .osd radio:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
.osd check:disabled, .osd radio:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
@@ -1288,7 +1288,7 @@ scale fill:disabled:backdrop, scale fill:disabled { border-color: transparent; b
.osd scale fill:disabled:backdrop, .osd scale fill:disabled { border-color: transparent; background-color: transparent; }
scale slider { color: #eeeeec; outline-color: rgba(238, 238, 236, 0.3); border-color: #070707; text-shadow: 0 -1px rgba(0, 0, 0, 0.834353); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.834353); background-image: linear-gradient(to bottom, #2d2d2d 20%, #252526 90%); box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.07); border: 1px solid black; border-radius: 100%; transition: all 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94); transition-property: background, border, box-shadow; }
scale slider { color: #eeeeec; outline-color: rgba(238, 238, 236, 0.3); border-color: #070707; text-shadow: 0 -1px rgba(0, 0, 0, 0.834353); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.834353); background-image: linear-gradient(to bottom, #2d2d2d 20%, #262626 90%); box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.07); border: 1px solid black; border-radius: 100%; transition: all 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94); transition-property: background, border, box-shadow; }
scale slider:hover { color: #eeeeec; outline-color: rgba(238, 238, 236, 0.3); border-color: #070707; box-shadow: inset 0 1px rgba(255, 255, 255, 0.02), 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.07); background-image: linear-gradient(to bottom, #353535 20%, #2b2b2b 90%); }
@@ -1308,17 +1308,17 @@ scale slider:backdrop:disabled label, scale slider:backdrop:disabled { color: #5
row:selected scale slider:disabled, row:selected scale slider { border-color: #030c17; }
.osd scale slider { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); border-color: rgba(0, 0, 0, 0.7); background-color: #252526; }
.osd scale slider { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); border-color: rgba(0, 0, 0, 0.7); background-color: #262626; }
.osd scale slider:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); background-color: #252526; }
.osd scale slider:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(68, 68, 68, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); background-color: #262626; }
.osd scale slider:active { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); background-color: #252526; }
.osd scale slider:active { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); background-color: #262626; }
.osd scale slider:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-color: #252526; }
.osd scale slider:disabled { color: #8a8a89; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(58, 58, 57, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-color: #262626; }
.osd scale slider:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(37, 37, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-color: #252526; }
.osd scale slider:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(38, 38, 38, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-color: #262626; }
.osd scale slider:backdrop:disabled { background-color: #252526; }
.osd scale slider:backdrop:disabled { background-color: #262626; }
scale value { color: alpha(currentColor,0.55); }
@@ -1627,7 +1627,9 @@ list row.expander { padding: 0px; }
list row.expander .row-header { padding: 2px; }
list.separators row:not(:first-child) { border-top: 1px solid #1b1b1b; }
list.separators.horizontal row:not(:first-child) { border-left: 1px solid #1b1b1b; }
list.separators:not(.horizontal) row:not(:first-child) { border-top: 1px solid #1b1b1b; }
row { transition: all 150ms cubic-bezier(0.25, 0.46, 0.45, 0.94); }
@@ -1648,7 +1650,7 @@ row.activatable:selected.has-open-popup, row.activatable:selected:hover { backgr
row.activatable:selected:backdrop { background-color: #15539e; }
/********************* App Notifications * */
.app-notification, .app-notification.frame { padding: 10px; border-radius: 0 0 5px 5px; background-color: rgba(37, 37, 38, 0.7); background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.2), transparent 2px); background-clip: padding-box; }
.app-notification, .app-notification.frame { padding: 10px; border-radius: 0 0 5px 5px; background-color: rgba(38, 38, 38, 0.7); background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.2), transparent 2px); background-clip: padding-box; }
.app-notification:backdrop, .app-notification.frame:backdrop { background-image: none; transition: 200ms ease-out; }
+28 -26
View File
@@ -48,6 +48,8 @@ flowbox flowboxchild { padding: 3px; }
flowbox flowboxchild:selected { outline-offset: -2px; }
coverflow cover { color: black; background-color: #ffffff; }
.content-view .tile { margin: 2px; background-color: transparent; border-radius: 0; padding: 0; }
.content-view .tile:backdrop { background-color: transparent; }
@@ -82,9 +84,9 @@ assistant .sidebar label { padding: 6px 12px; }
assistant .sidebar label.highlight { background-color: #cecece; }
.csd popover.background > contents.touch-selection, .csd popover.background > contents.magnifier, popover.background > contents.touch-selection, popover.background > contents.magnifier, .csd popover.background > contents.osd, popover.background > contents.osd, .app-notification, .app-notification.frame, .osd .scale-popup, .osd { color: #eeeeec; border: none; background-color: rgba(53, 53, 53, 0.7); background-clip: padding-box; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
.osd popover.background > arrow, .osd popover.background > contents, popover.background.touch-selection > arrow, popover.background.touch-selection > contents, popover.background.magnifier > arrow, popover.background.magnifier > contents, .app-notification, .app-notification.frame, .osd .scale-popup, .osd { color: #eeeeec; border: none; background-color: rgba(53, 53, 53, 0.7); background-clip: padding-box; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
.csd popover.background > contents.touch-selection:backdrop, .csd popover.background > contents.magnifier:backdrop, popover.background > contents.touch-selection:backdrop, popover.background > contents.magnifier:backdrop, .csd popover.background > contents.osd:backdrop, popover.background > contents.osd:backdrop, .app-notification:backdrop, .osd .scale-popup:backdrop, .osd:backdrop { text-shadow: none; -gtk-icon-shadow: none; }
.osd popover.background > arrow:backdrop, .osd popover.background > contents:backdrop, popover.background.touch-selection > arrow:backdrop, popover.background.touch-selection > contents:backdrop, popover.background.magnifier > arrow:backdrop, popover.background.magnifier > contents:backdrop, .app-notification:backdrop, .osd .scale-popup:backdrop, .osd:backdrop { text-shadow: none; -gtk-icon-shadow: none; }
/********************* Spinner Animation * */
@keyframes spin { to { -gtk-icon-transform: rotate(1turn); } }
@@ -283,25 +285,25 @@ button.osd:disabled:backdrop, button.osd:disabled { color: #919190; border-color
button.osd:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(53, 53, 53, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; border: none; }
.csd popover.background > contents.touch-selection button, .csd popover.background > contents.magnifier button, popover.background > contents.touch-selection button, popover.background > contents.magnifier button, .app-notification button, .app-notification.frame button, .osd button { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(53, 53, 53, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
popover.background.touch-selection button, popover.background.magnifier button, .app-notification button, .app-notification.frame button, .osd button { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(53, 53, 53, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.csd popover.background > contents.touch-selection button:hover, .csd popover.background > contents.magnifier button:hover, popover.background > contents.touch-selection button:hover, popover.background > contents.magnifier button:hover, .app-notification button:hover, .osd button:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(83, 83, 83, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
popover.background.touch-selection button:hover, popover.background.magnifier button:hover, .app-notification button:hover, .osd button:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(83, 83, 83, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.csd popover.background > contents.touch-selection button:active:backdrop, .csd popover.background > contents.magnifier button:active:backdrop, popover.background > contents.touch-selection button:active:backdrop, popover.background > contents.magnifier button:active:backdrop, .app-notification button:active:backdrop, .csd popover.background > contents.touch-selection button:active, .csd popover.background > contents.magnifier button:active, popover.background > contents.touch-selection button:active, popover.background > contents.magnifier button:active, .app-notification button:active, .csd popover.background > contents.touch-selection button:checked:backdrop, .csd popover.background > contents.magnifier button:checked:backdrop, popover.background > contents.touch-selection button:checked:backdrop, popover.background > contents.magnifier button:checked:backdrop, .app-notification button:checked:backdrop, .csd popover.background > contents.touch-selection button:checked, .csd popover.background > contents.magnifier button:checked, popover.background > contents.touch-selection button:checked, popover.background > contents.magnifier button:checked, .app-notification button:checked, .osd button:active:backdrop, .osd button:active, .osd button:checked:backdrop, .osd button:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
popover.background.touch-selection button:active:backdrop, popover.background.magnifier button:active:backdrop, .app-notification button:active:backdrop, popover.background.touch-selection button:active, popover.background.magnifier button:active, .app-notification button:active, popover.background.touch-selection button:checked:backdrop, popover.background.magnifier button:checked:backdrop, .app-notification button:checked:backdrop, popover.background.touch-selection button:checked, popover.background.magnifier button:checked, .app-notification button:checked, .osd button:active:backdrop, .osd button:active, .osd button:checked:backdrop, .osd button:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.csd popover.background > contents.touch-selection button:disabled:backdrop, .csd popover.background > contents.magnifier button:disabled:backdrop, popover.background > contents.touch-selection button:disabled:backdrop, popover.background > contents.magnifier button:disabled:backdrop, .app-notification button:disabled:backdrop, .csd popover.background > contents.touch-selection button:disabled, .csd popover.background > contents.magnifier button:disabled, popover.background > contents.touch-selection button:disabled, popover.background > contents.magnifier button:disabled, .app-notification button:disabled, .osd button:disabled:backdrop, .osd button:disabled { color: #919190; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(71, 71, 71, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
popover.background.touch-selection button:disabled:backdrop, popover.background.magnifier button:disabled:backdrop, .app-notification button:disabled:backdrop, popover.background.touch-selection button:disabled, popover.background.magnifier button:disabled, .app-notification button:disabled, .osd button:disabled:backdrop, .osd button:disabled { color: #919190; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(71, 71, 71, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
.csd popover.background > contents.touch-selection button:backdrop, .csd popover.background > contents.magnifier button:backdrop, popover.background > contents.touch-selection button:backdrop, popover.background > contents.magnifier button:backdrop, .app-notification button:backdrop, .osd button:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(53, 53, 53, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
popover.background.touch-selection button:backdrop, popover.background.magnifier button:backdrop, .app-notification button:backdrop, .osd button:backdrop { color: #eeeeec; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(53, 53, 53, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; }
.csd popover.background > contents.touch-selection button.flat, .csd popover.background > contents.magnifier button.flat, popover.background > contents.touch-selection button.flat, popover.background > contents.magnifier button.flat, .app-notification button.flat, .osd button.flat { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; box-shadow: none; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
popover.background.touch-selection button.flat, popover.background.magnifier button.flat, .app-notification button.flat, .osd button.flat { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; box-shadow: none; text-shadow: 0 1px black; -gtk-icon-shadow: 0 1px black; }
.csd popover.background > contents.touch-selection button.flat:hover, .csd popover.background > contents.magnifier button.flat:hover, popover.background > contents.touch-selection button.flat:hover, popover.background > contents.magnifier button.flat:hover, .app-notification button.flat:hover, .osd button.flat:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(83, 83, 83, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
popover.background.touch-selection button.flat:hover, popover.background.magnifier button.flat:hover, .app-notification button.flat:hover, .osd button.flat:hover { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(83, 83, 83, 0.7)); background-clip: padding-box; box-shadow: inset 0 1px rgba(255, 255, 255, 0.1); text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
.csd popover.background > contents.touch-selection button.flat:disabled, .csd popover.background > contents.magnifier button.flat:disabled, popover.background > contents.touch-selection button.flat:disabled, popover.background > contents.magnifier button.flat:disabled, .app-notification button.flat:disabled, .osd button.flat:disabled { color: #919190; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(71, 71, 71, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-image: none; border-color: transparent; box-shadow: none; }
popover.background.touch-selection button.flat:disabled, popover.background.magnifier button.flat:disabled, .app-notification button.flat:disabled, .osd button.flat:disabled { color: #919190; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(71, 71, 71, 0.5)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; background-image: none; border-color: transparent; box-shadow: none; }
.csd popover.background > contents.touch-selection button.flat:backdrop, .csd popover.background > contents.magnifier button.flat:backdrop, popover.background > contents.touch-selection button.flat:backdrop, popover.background > contents.magnifier button.flat:backdrop, .app-notification button.flat:backdrop, .osd button.flat:backdrop { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; }
popover.background.touch-selection button.flat:backdrop, popover.background.magnifier button.flat:backdrop, .app-notification button.flat:backdrop, .osd button.flat:backdrop { border-color: transparent; background-color: transparent; background-image: none; box-shadow: inset 0 1px rgba(255, 255, 255, 0); text-shadow: none; -gtk-icon-shadow: none; }
.csd popover.background > contents.touch-selection button.flat:active, .csd popover.background > contents.magnifier button.flat:active, popover.background > contents.touch-selection button.flat:active, popover.background > contents.magnifier button.flat:active, .app-notification button.flat:active, .csd popover.background > contents.touch-selection button.flat:checked, .csd popover.background > contents.magnifier button.flat:checked, popover.background > contents.touch-selection button.flat:checked, popover.background > contents.magnifier button.flat:checked, .app-notification button.flat:checked, .osd button.flat:active, .osd button.flat:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
popover.background.touch-selection button.flat:active, popover.background.magnifier button.flat:active, .app-notification button.flat:active, popover.background.touch-selection button.flat:checked, popover.background.magnifier button.flat:checked, .app-notification button.flat:checked, .osd button.flat:active, .osd button.flat:checked { color: white; border-color: rgba(0, 0, 0, 0.7); background-color: transparent; background-image: image(rgba(0, 0, 0, 0.7)); background-clip: padding-box; box-shadow: none; text-shadow: none; -gtk-icon-shadow: none; outline-color: rgba(238, 238, 236, 0.3); }
button.suggested-action { color: white; outline-color: rgba(255, 255, 255, 0.3); border-color: #1b6acb; border-bottom-color: #15539e; background-image: linear-gradient(to top, #2379e2 2px, #3584e4); text-shadow: 0 -1px rgba(0, 0, 0, 0.559216); -gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.559216); box-shadow: inset 0 1px rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.07); }
@@ -889,25 +891,23 @@ menuitem check:dir(ltr), menuitem radio:dir(ltr) { margin-right: 7px; }
menuitem check:dir(rtl), menuitem radio:dir(rtl) { margin-left: 7px; }
/*************** Popovers * */
popover.background { background-color: transparent; padding: 0px; }
popover.background { background-color: transparent; font: initial; }
popover.menu > arrow, popover > arrow { background-color: #f6f5f4; border: 1px solid #cdc7c2; }
popover.background > arrow, popover.background > contents { background-color: #f6f5f4; border: 1px solid #cdc7c2; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); }
popover > contents { padding: 8px; background-color: #f6f5f4; border: 1px solid #cdc7c2; margin: 0px; }
popover.background > arrow:backdrop, popover.background > contents:backdrop { background-color: #f6f5f4; border-color: #d5d0cc; box-shadow: none; }
popover.background > contents { background-color: #f6f5f4; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); }
.csd popover.background > contents, popover.background > contents { border: 1px solid #cdc7c2; border-radius: 9px; }
popover.background > contents:backdrop { background-color: #f6f5f4; box-shadow: none; }
popover.background > contents { padding: 8px; border-radius: 9px; }
popover.background > contents > list, popover.background > contents > .view, popover.background > contents > iconview, popover.background > contents > toolbar { border-style: none; background-color: transparent; }
.csd popover.background > contents.touch-selection, .csd popover.background > contents.magnifier, popover.background > contents.touch-selection, popover.background > contents.magnifier { border: 1px solid rgba(255, 255, 255, 0.1); }
popover.background > contents separator { margin: 3px; }
popover.background > contents list separator { margin: 0px; }
popover.background > contents list separator { margin: 0; }
.osd popover.background, popover.background.touch-selection, popover.background.magnifier { background-color: transparent; }
.osd popover.background > arrow, .osd popover.background > contents, popover.background.touch-selection > arrow, popover.background.touch-selection > contents, popover.background.magnifier > arrow, popover.background.magnifier > contents { border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: none; }
/************* Notebooks * */
notebook > header { padding: 1px; border-color: #cdc7c2; border-width: 1px; background-color: #e1dedb; }
@@ -1163,7 +1163,7 @@ row:selected switch slider:checked, row:selected switch slider { border-color: #
.view.content-view.check:active:not(list), iconview.content-view.check:active:not(list), .content-view .tile check:active:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(53, 132, 228, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
.view.content-view.check:backdrop:not(list), iconview.content-view.check:backdrop:not(list), .content-view .tile check:backdrop:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(140, 140, 141, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
.view.content-view.check:backdrop:not(list), iconview.content-view.check:backdrop:not(list), .content-view .tile check:backdrop:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: transparent; background-color: rgba(141, 141, 141, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: none; -gtk-icon-shadow: none; }
.view.content-view.check:checked:not(list), iconview.content-view.check:checked:not(list), .content-view .tile check:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: #eeeeec; background-color: rgba(53, 132, 228, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
@@ -1171,7 +1171,7 @@ row:selected switch slider:checked, row:selected switch slider { border-color: #
.view.content-view.check:checked:active:not(list), iconview.content-view.check:checked:active:not(list), .content-view .tile check:checked:active:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: #eeeeec; background-color: rgba(53, 132, 228, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
.view.content-view.check:backdrop:checked:not(list), iconview.content-view.check:backdrop:checked:not(list), .content-view .tile check:backdrop:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: rgba(238, 238, 236, 0.8); background-color: rgba(140, 140, 141, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
.view.content-view.check:backdrop:checked:not(list), iconview.content-view.check:backdrop:checked:not(list), .content-view .tile check:backdrop:checked:not(list) { margin: 4px; min-width: 32px; min-height: 32px; color: rgba(238, 238, 236, 0.8); background-color: rgba(141, 141, 141, 0.95); border-radius: 5px; background-image: none; transition: 200ms; box-shadow: none; border-width: 0; -gtk-icon-source: -gtk-icontheme('object-select-symbolic'); -gtk-icon-shadow: none; }
checkbutton.text-button, radiobutton.text-button { padding: 2px 0; outline-offset: 0; border-spacing: 4px; }
@@ -1643,7 +1643,9 @@ list row.expander { padding: 0px; }
list row.expander .row-header { padding: 2px; }
list.separators row:not(:first-child) { border-top: 1px solid #cdc7c2; }
list.separators.horizontal row:not(:first-child) { border-left: 1px solid #cdc7c2; }
list.separators:not(.horizontal) row:not(:first-child) { border-top: 1px solid #cdc7c2; }
row { transition: all 150ms cubic-bezier(0.25, 0.46, 0.45, 0.94); }
+2
View File
@@ -59,6 +59,8 @@ gtk_tests = [
['testlist2'],
['testlist3'],
['testlist4'],
['testlistview'],
['testlistview-animating'],
['testlevelbar'],
['testlockbutton'],
['testmenubutton'],
+192
View File
@@ -0,0 +1,192 @@
#include <gtk/gtk.h>
#ifdef SMALL
#define AVERAGE 15
#define VARIANCE 10
#else
#define AVERAGE 300
#define VARIANCE 200
#endif
static void
update_label (GtkListItem *list_item,
GParamSpec *pspec,
GtkLabel *label)
{
gpointer item;
char *s;
item = gtk_list_item_get_item (list_item);
if (item)
s = g_strdup_printf ("%u: %s",
gtk_list_item_get_position (list_item),
(const char *) g_object_get_data (item, "message"));
else
s = NULL;
gtk_label_set_text (label, s);
g_free (s);
}
static void
setup_list_item (GtkListItem *list_item,
gpointer unused)
{
GtkWidget *label = gtk_label_new ("");
g_signal_connect (list_item, "notify", G_CALLBACK (update_label), label);
gtk_container_add (GTK_CONTAINER (list_item), label);
}
static GtkWidget *
create_widget_for_listbox (gpointer item,
gpointer unused)
{
const char *message = g_object_get_data (item, "message");
GtkWidget *widget;
widget = gtk_label_new (message);
return widget;
}
static gboolean reverse_sort;
static int
compare (gconstpointer first,
gconstpointer second,
gpointer unused)
{
int diff = (GPOINTER_TO_UINT (g_object_get_data ((gpointer) first, "counter")) % 1000)
- (GPOINTER_TO_UINT (g_object_get_data ((gpointer) second, "counter")) % 1000);
if (reverse_sort)
return -diff;
else
return diff;
}
static void
add (GListStore *store)
{
static guint counter;
GObject *o;
char *message;
guint pos;
counter++;
o = g_object_new (G_TYPE_OBJECT, NULL);
g_object_set_data (o, "counter", GUINT_TO_POINTER (counter));
message = g_strdup_printf ("Item %u", counter);
g_object_set_data_full (o, "message", message, g_free);
pos = g_random_int_range (0, g_list_model_get_n_items (G_LIST_MODEL (store)) + 1);
g_list_store_insert (store, pos, o);
g_object_unref (o);
}
static void
delete (GListStore *store)
{
guint pos;
pos = g_random_int_range (0, g_list_model_get_n_items (G_LIST_MODEL (store)));
g_list_store_remove (store, pos);
}
static gboolean
do_stuff (gpointer store)
{
if (g_random_int_range (AVERAGE - VARIANCE, AVERAGE + VARIANCE) < g_list_model_get_n_items (store))
delete (store);
else
add (store);
return G_SOURCE_CONTINUE;
}
static gboolean
revert_sort (gpointer sort)
{
reverse_sort = !reverse_sort;
gtk_sort_list_model_resort (sort);
return G_SOURCE_CONTINUE;
}
int
main (int argc,
char *argv[])
{
GtkWidget *win, *hbox, *vbox, *sw, *listview, *listbox, *label;
GListStore *store;
GtkSortListModel *sort;
guint i;
gtk_init ();
store = g_list_store_new (G_TYPE_OBJECT);
for (i = 0; i < AVERAGE; i++)
add (store);
sort = gtk_sort_list_model_new (G_LIST_MODEL (store),
compare,
NULL, NULL);
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (win), 400, 600);
g_signal_connect (win, "destroy", G_CALLBACK (gtk_main_quit), win);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_container_add (GTK_CONTAINER (win), hbox);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
gtk_container_add (GTK_CONTAINER (hbox), vbox);
label = gtk_label_new ("GtkListView");
gtk_container_add (GTK_CONTAINER (vbox), label);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_set_hexpand (sw, TRUE);
gtk_widget_set_vexpand (sw, TRUE);
gtk_container_add (GTK_CONTAINER (vbox), sw);
listview = gtk_list_view_new_with_factory (
gtk_functions_list_item_factory_new (setup_list_item,
NULL,
NULL, NULL));
gtk_container_add (GTK_CONTAINER (sw), listview);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
gtk_container_add (GTK_CONTAINER (hbox), vbox);
label = gtk_label_new ("GtkListBox");
gtk_container_add (GTK_CONTAINER (vbox), label);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_set_hexpand (sw, TRUE);
gtk_widget_set_vexpand (sw, TRUE);
gtk_container_add (GTK_CONTAINER (vbox), sw);
listbox = gtk_list_box_new ();
gtk_container_add (GTK_CONTAINER (sw), listbox);
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (sort));
gtk_list_box_bind_model (GTK_LIST_BOX (listbox),
G_LIST_MODEL (sort),
create_widget_for_listbox,
NULL, NULL);
g_timeout_add (100, do_stuff, store);
g_timeout_add_seconds (3, revert_sort, sort);
gtk_widget_show (win);
gtk_main ();
g_object_unref (store);
return 0;
}
+681
View File
@@ -0,0 +1,681 @@
#include <gtk/gtk.h>
#define FILE_INFO_TYPE_SELECTION (file_info_selection_get_type ())
G_DECLARE_FINAL_TYPE (FileInfoSelection, file_info_selection, FILE_INFO, SELECTION, GObject)
struct _FileInfoSelection
{
GObject parent_instance;
GListModel *model;
};
struct _FileInfoSelectionClass
{
GObjectClass parent_class;
};
static GType
file_info_selection_get_item_type (GListModel *list)
{
FileInfoSelection *self = FILE_INFO_SELECTION (list);
return g_list_model_get_item_type (self->model);
}
static guint
file_info_selection_get_n_items (GListModel *list)
{
FileInfoSelection *self = FILE_INFO_SELECTION (list);
return g_list_model_get_n_items (self->model);
}
static gpointer
file_info_selection_get_item (GListModel *list,
guint position)
{
FileInfoSelection *self = FILE_INFO_SELECTION (list);
return g_list_model_get_item (self->model, position);
}
static void
file_info_selection_list_model_init (GListModelInterface *iface)
{
iface->get_item_type = file_info_selection_get_item_type;
iface->get_n_items = file_info_selection_get_n_items;
iface->get_item = file_info_selection_get_item;
}
static gboolean
file_info_selection_is_selected (GtkSelectionModel *model,
guint position)
{
FileInfoSelection *self = FILE_INFO_SELECTION (model);
gpointer item;
item = g_list_model_get_item (self->model, position);
if (item == NULL)
return FALSE;
if (GTK_IS_TREE_LIST_ROW (item))
{
GtkTreeListRow *row = item;
item = gtk_tree_list_row_get_item (row);
g_object_unref (row);
}
return g_file_info_get_attribute_boolean (item, "filechooser::selected");
}
static void
file_info_selection_set_selected (FileInfoSelection *self,
guint position,
gboolean selected)
{
gpointer item;
item = g_list_model_get_item (self->model, position);
if (item == NULL)
return;
if (GTK_IS_TREE_LIST_ROW (item))
{
GtkTreeListRow *row = item;
item = gtk_tree_list_row_get_item (row);
g_object_unref (row);
}
g_file_info_set_attribute_boolean (item, "filechooser::selected", selected);
}
static gboolean
file_info_selection_select_item (GtkSelectionModel *model,
guint position,
gboolean exclusive)
{
FileInfoSelection *self = FILE_INFO_SELECTION (model);
if (exclusive)
{
guint i;
for (i = 0; i < g_list_model_get_n_items (self->model); i++)
file_info_selection_set_selected (self, i, i == position);
gtk_selection_model_selection_changed (model, 0, g_list_model_get_n_items (self->model));
}
else
{
file_info_selection_set_selected (self, position, TRUE);
gtk_selection_model_selection_changed (model, position, 1);
}
return TRUE;
}
static gboolean
file_info_selection_unselect_item (GtkSelectionModel *model,
guint position)
{
FileInfoSelection *self = FILE_INFO_SELECTION (model);
file_info_selection_set_selected (self, position, FALSE);
gtk_selection_model_selection_changed (model, position, 1);
return TRUE;
}
static gboolean
file_info_selection_select_range (GtkSelectionModel *model,
guint position,
guint n_items,
gboolean exclusive)
{
FileInfoSelection *self = FILE_INFO_SELECTION (model);
guint i;
if (exclusive)
for (i = 0; i < position; i++)
file_info_selection_set_selected (self, i, FALSE);
for (i = position; i < position + n_items; i++)
file_info_selection_set_selected (self, i, TRUE);
if (exclusive)
for (i = position + n_items; i < g_list_model_get_n_items (self->model); i++)
file_info_selection_set_selected (self, i, FALSE);
if (exclusive)
gtk_selection_model_selection_changed (model, 0, g_list_model_get_n_items (self->model));
else
gtk_selection_model_selection_changed (model, position, n_items);
return TRUE;
}
static gboolean
file_info_selection_unselect_range (GtkSelectionModel *model,
guint position,
guint n_items)
{
FileInfoSelection *self = FILE_INFO_SELECTION (model);
guint i;
for (i = position; i < position + n_items; i++)
file_info_selection_set_selected (self, i, FALSE);
gtk_selection_model_selection_changed (model, position, n_items);
return TRUE;
}
static void
file_info_selection_selection_model_init (GtkSelectionModelInterface *iface)
{
iface->is_selected = file_info_selection_is_selected;
iface->select_item = file_info_selection_select_item;
iface->unselect_item = file_info_selection_unselect_item;
iface->select_range = file_info_selection_select_range;
iface->unselect_range = file_info_selection_unselect_range;
}
G_DEFINE_TYPE_EXTENDED (FileInfoSelection, file_info_selection, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
file_info_selection_list_model_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL,
file_info_selection_selection_model_init))
static void
file_info_selection_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
FileInfoSelection *self)
{
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
}
static void
file_info_selection_clear_model (FileInfoSelection *self)
{
if (self->model == NULL)
return;
g_signal_handlers_disconnect_by_func (self->model,
file_info_selection_items_changed_cb,
self);
g_clear_object (&self->model);
}
static void
file_info_selection_dispose (GObject *object)
{
FileInfoSelection *self = FILE_INFO_SELECTION (object);
file_info_selection_clear_model (self);
G_OBJECT_CLASS (file_info_selection_parent_class)->dispose (object);
}
static void
file_info_selection_class_init (FileInfoSelectionClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = file_info_selection_dispose;
}
static void
file_info_selection_init (FileInfoSelection *self)
{
}
static FileInfoSelection *
file_info_selection_new (GListModel *model)
{
FileInfoSelection *result;
result = g_object_new (FILE_INFO_TYPE_SELECTION, NULL);
result->model = g_object_ref (model);
g_signal_connect (result->model, "items-changed",
G_CALLBACK (file_info_selection_items_changed_cb), result);
return result;
}
/*** ---------------------- ***/
GSList *pending = NULL;
guint active = 0;
static void
loading_cb (GtkDirectoryList *dir,
GParamSpec *pspec,
gpointer unused)
{
if (gtk_directory_list_is_loading (dir))
{
active++;
/* HACK: ensure loading finishes and the dir doesn't get destroyed */
g_object_ref (dir);
}
else
{
active--;
g_object_unref (dir);
while (active < 20 && pending)
{
GtkDirectoryList *dir2 = pending->data;
pending = g_slist_remove (pending, dir2);
gtk_directory_list_set_file (dir2, g_object_get_data (G_OBJECT (dir2), "file"));
g_object_unref (dir2);
}
}
}
static GtkDirectoryList *
create_directory_list (GFile *file)
{
GtkDirectoryList *dir;
dir = gtk_directory_list_new (G_FILE_ATTRIBUTE_STANDARD_TYPE
"," G_FILE_ATTRIBUTE_STANDARD_NAME
"," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
NULL);
gtk_directory_list_set_io_priority (dir, G_PRIORITY_DEFAULT_IDLE);
g_signal_connect (dir, "notify::loading", G_CALLBACK (loading_cb), NULL);
g_assert (!gtk_directory_list_is_loading (dir));
if (active > 20)
{
g_object_set_data_full (G_OBJECT (dir), "file", g_object_ref (file), g_object_unref);
pending = g_slist_prepend (pending, g_object_ref (dir));
}
else
{
gtk_directory_list_set_file (dir, file);
}
return dir;
}
static int
compare_files (gconstpointer first,
gconstpointer second,
gpointer unused)
{
GFile *first_file, *second_file;
char *first_path, *second_path;
int result;
#if 0
GFileType first_type, second_type;
/* This is a bit slow, because each g_file_query_file_type() does a stat() */
first_type = g_file_query_file_type (G_FILE (first), G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL);
second_type = g_file_query_file_type (G_FILE (second), G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL);
if (first_type == G_FILE_TYPE_DIRECTORY && second_type != G_FILE_TYPE_DIRECTORY)
return -1;
if (first_type != G_FILE_TYPE_DIRECTORY && second_type == G_FILE_TYPE_DIRECTORY)
return 1;
#endif
first_file = G_FILE (g_file_info_get_attribute_object (G_FILE_INFO (first), "standard::file"));
second_file = G_FILE (g_file_info_get_attribute_object (G_FILE_INFO (second), "standard::file"));
first_path = g_file_get_path (first_file);
second_path = g_file_get_path (second_file);
result = strcasecmp (first_path, second_path);
g_free (first_path);
g_free (second_path);
return result;
}
static GListModel *
create_list_model_for_directory (gpointer file)
{
GtkSortListModel *sort;
GtkDirectoryList *dir;
if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) != G_FILE_TYPE_DIRECTORY)
return NULL;
dir = create_directory_list(file);
sort = gtk_sort_list_model_new (G_LIST_MODEL (dir),
compare_files,
NULL, NULL);
g_object_unref (dir);
return G_LIST_MODEL (sort);
}
typedef struct _RowData RowData;
struct _RowData
{
GtkWidget *expander;
GtkWidget *icon;
GtkWidget *name;
GCancellable *cancellable;
GtkTreeListRow *current_item;
};
static void row_data_notify_item (GtkListItem *item,
GParamSpec *pspec,
RowData *data);
static void
row_data_unbind (RowData *data)
{
if (data->current_item == NULL)
return;
if (data->cancellable)
{
g_cancellable_cancel (data->cancellable);
g_clear_object (&data->cancellable);
}
g_clear_object (&data->current_item);
}
static void
row_data_update_info (RowData *data,
GFileInfo *info)
{
GIcon *icon;
const char *thumbnail_path;
thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
if (thumbnail_path)
{
/* XXX: not async */
GFile *thumbnail_file = g_file_new_for_path (thumbnail_path);
icon = g_file_icon_new (thumbnail_file);
g_object_unref (thumbnail_file);
}
else
{
icon = g_file_info_get_icon (info);
}
gtk_widget_set_visible (data->icon, icon != NULL);
gtk_image_set_from_gicon (GTK_IMAGE (data->icon), icon);
}
static void
copy_attribute (GFileInfo *to,
GFileInfo *from,
const gchar *attribute)
{
GFileAttributeType type;
gpointer value;
if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL))
g_file_info_set_attribute (to, attribute, type, value);
}
static void
row_data_got_thumbnail_info_cb (GObject *source,
GAsyncResult *res,
gpointer _data)
{
RowData *data = _data; /* invalid if operation was cancelled */
GFile *file = G_FILE (source);
GFileInfo *queried, *info;
queried = g_file_query_info_finish (file, res, NULL);
if (queried == NULL)
return;
/* now we know row is valid */
info = gtk_tree_list_row_get_item (data->current_item);
copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
g_object_unref (queried);
row_data_update_info (data, info);
g_clear_object (&data->cancellable);
}
static void
row_data_bind (RowData *data,
GtkTreeListRow *item)
{
GFileInfo *info;
row_data_unbind (data);
if (item == NULL)
return;
data->current_item = g_object_ref (item);
gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (data->expander), item);
info = gtk_tree_list_row_get_item (item);
if (!g_file_info_has_attribute (info, "filechooser::queried"))
{
data->cancellable = g_cancellable_new ();
g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE);
g_file_query_info_async (G_FILE (g_file_info_get_attribute_object (info, "standard::file")),
G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
G_FILE_ATTRIBUTE_STANDARD_ICON,
G_FILE_QUERY_INFO_NONE,
G_PRIORITY_DEFAULT,
data->cancellable,
row_data_got_thumbnail_info_cb,
data);
}
row_data_update_info (data, info);
gtk_label_set_label (GTK_LABEL (data->name), g_file_info_get_display_name (info));
g_object_unref (info);
}
static void
row_data_notify_item (GtkListItem *item,
GParamSpec *pspec,
RowData *data)
{
row_data_bind (data, gtk_list_item_get_item (item));
}
static void
row_data_free (gpointer _data)
{
RowData *data = _data;
row_data_unbind (data);
g_slice_free (RowData, data);
}
static void
setup_widget (GtkListItem *list_item,
gpointer unused)
{
GtkWidget *box, *child;
RowData *data;
data = g_slice_new0 (RowData);
g_signal_connect (list_item, "notify::item", G_CALLBACK (row_data_notify_item), data);
g_object_set_data_full (G_OBJECT (list_item), "row-data", data, row_data_free);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_container_add (GTK_CONTAINER (list_item), box);
child = gtk_label_new (NULL);
gtk_label_set_width_chars (GTK_LABEL (child), 5);
gtk_label_set_xalign (GTK_LABEL (child), 1.0);
g_object_bind_property (list_item, "position", child, "label", G_BINDING_SYNC_CREATE);
gtk_container_add (GTK_CONTAINER (box), child);
data->expander = gtk_tree_expander_new ();
gtk_container_add (GTK_CONTAINER (box), data->expander);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (data->expander), box);
data->icon = gtk_image_new ();
gtk_container_add (GTK_CONTAINER (box), data->icon);
data->name = gtk_label_new (NULL);
gtk_label_set_max_width_chars (GTK_LABEL (data->name), 25);
gtk_label_set_ellipsize (GTK_LABEL (data->name), PANGO_ELLIPSIZE_END);
gtk_container_add (GTK_CONTAINER (box), data->name);
}
static GListModel *
create_list_model_for_file_info (gpointer file_info,
gpointer unused)
{
GFile *file = G_FILE (g_file_info_get_attribute_object (file_info, "standard::file"));
if (file == NULL)
return NULL;
return create_list_model_for_directory (file);
}
static gboolean
update_statusbar (GtkStatusbar *statusbar)
{
GListModel *model = g_object_get_data (G_OBJECT (statusbar), "model");
GString *string = g_string_new (NULL);
guint n;
gboolean result = G_SOURCE_REMOVE;
gtk_statusbar_remove_all (statusbar, 0);
n = g_list_model_get_n_items (model);
g_string_append_printf (string, "%u", n);
if (GTK_IS_FILTER_LIST_MODEL (model))
{
guint n_unfiltered = g_list_model_get_n_items (gtk_filter_list_model_get_model (GTK_FILTER_LIST_MODEL (model)));
if (n != n_unfiltered)
g_string_append_printf (string, "/%u", n_unfiltered);
}
g_string_append (string, " items");
if (pending || active)
{
g_string_append_printf (string, " (%u directories remaining)", active + g_slist_length (pending));
result = G_SOURCE_CONTINUE;
}
result = G_SOURCE_CONTINUE;
gtk_statusbar_push (statusbar, 0, string->str);
g_free (string->str);
return result;
}
static gboolean
match_file (gpointer item, gpointer data)
{
GtkWidget *search_entry = data;
GFileInfo *info = gtk_tree_list_row_get_item (item);
GFile *file = G_FILE (g_file_info_get_attribute_object (info, "standard::file"));
char *path;
gboolean result;
path = g_file_get_path (file);
result = strstr (path, gtk_editable_get_text (GTK_EDITABLE (search_entry))) != NULL;
g_object_unref (info);
g_free (path);
return result;
}
int
main (int argc, char *argv[])
{
GtkWidget *win, *vbox, *sw, *listview, *search_entry, *statusbar;
GListModel *dirmodel;
GtkTreeListModel *tree;
GtkFilterListModel *filter;
FileInfoSelection *selectionmodel;
GFile *root;
gtk_init ();
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (win), 800, 600);
g_signal_connect (win, "destroy", G_CALLBACK (gtk_main_quit), win);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (win), vbox);
search_entry = gtk_search_entry_new ();
gtk_container_add (GTK_CONTAINER (vbox), search_entry);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_set_vexpand (sw, TRUE);
gtk_search_entry_set_key_capture_widget (GTK_SEARCH_ENTRY (search_entry), sw);
gtk_container_add (GTK_CONTAINER (vbox), sw);
listview = gtk_grid_view_new_with_factory (
gtk_functions_list_item_factory_new (setup_widget,
NULL,
NULL, NULL));
gtk_container_add (GTK_CONTAINER (sw), listview);
if (argc > 1)
root = g_file_new_for_commandline_arg (argv[1]);
else
root = g_file_new_for_path (g_get_current_dir ());
dirmodel = create_list_model_for_directory (root);
tree = gtk_tree_list_model_new (FALSE,
dirmodel,
TRUE,
create_list_model_for_file_info,
NULL, NULL);
g_object_unref (dirmodel);
g_object_unref (root);
filter = gtk_filter_list_model_new (G_LIST_MODEL (tree),
match_file,
search_entry,
NULL);
g_signal_connect_swapped (search_entry, "search-changed", G_CALLBACK (gtk_filter_list_model_refilter), filter);
selectionmodel = file_info_selection_new (G_LIST_MODEL (filter));
g_object_unref (filter);
gtk_grid_view_set_model (GTK_GRID_VIEW (listview), G_LIST_MODEL (selectionmodel));
statusbar = gtk_statusbar_new ();
gtk_widget_add_tick_callback (statusbar, (GtkTickCallback) update_statusbar, NULL, NULL);
g_object_set_data (G_OBJECT (statusbar), "model", filter);
g_signal_connect_swapped (filter, "items-changed", G_CALLBACK (update_statusbar), statusbar);
update_statusbar (GTK_STATUSBAR (statusbar));
gtk_container_add (GTK_CONTAINER (vbox), statusbar);
g_object_unref (tree);
g_object_unref (selectionmodel);
gtk_widget_show (win);
gtk_main ();
return 0;
}