Compare commits

...

266 Commits

Author SHA1 Message Date
Matthias Clasen
d080a2c482 gtk-demo: Plug memory leaks in the settings demo 2019-12-25 01:10:48 -05:00
Matthias Clasen
682dbe1c9c inspector: Use search instead of filtering for properties
This is mainly to test the search support.
2019-12-24 20:09:48 -05:00
Matthias Clasen
3f5d6bd8b5 listbase: search support
Add a filter to GtkListBase, and move the selection
to the first matching item whenever the filter changes.
This is meant to be used with single selection and
a string filter that is hooked up to a search entry.
2019-12-24 20:09:01 -05:00
Matthias Clasen
98aedb7592 inspector: Add columns to the object tree
Add columnview columns in the object tree.
We do the same for treeview columns.
2019-12-24 15:36:08 -05:00
Matthias Clasen
b1fdd77ede builderlistitemfactory: Precompile the xml
This is the one place where we can really take advantage
of precompiling, since we instantiate this template
over and over.
2019-12-24 15:36:08 -05:00
Matthias Clasen
ed7f507206 builder: Avoid a crash in error cases
Don't leave a dangling pointer behind. I've hit
cases where we crash trying to free it again.
2019-12-24 15:36:08 -05:00
Matthias Clasen
a101e8079f builder: Avoid another critical
Only free an expression if we managed
to create one.
2019-12-24 15:36:08 -05:00
Matthias Clasen
b8faadec64 builder: Avoid a critical
Check that the property exists before trying
to create a property expression, and report
an error if it doesn't.
2019-12-24 15:36:08 -05:00
Matthias Clasen
51b4768fd7 expression: Add a new property expression constructor
Allow creating property expresions from a GParamSpec.
This lets us avoid a critical, and instead report an
error in GtkBuilder if the property does not exist.
2019-12-24 15:36:08 -05:00
Matthias Clasen
8febf5e98a Spread single-click-activate api
This makes sense to have in all the views,
not just GtkListView.
2019-12-24 15:36:08 -05:00
Matthias Clasen
1d9e09aaca printdialog: Port to column view
This is not 100% complete. The search is not there yet.
2019-12-24 15:36:08 -05:00
Matthias Clasen
eecb8092dc printer: Fix the default value of icon-name
Just set this to "printer", so we don't have
to fix it up in the print dialog.
2019-12-24 15:36:08 -05:00
Matthias Clasen
ddd504ddfe printbackend: Add a list model getter
Now that we have a list model for printers,
we can start using it.
2019-12-24 15:36:08 -05:00
Matthias Clasen
4044a8cc6c printbackend: Use a list store 2019-12-24 15:36:08 -05:00
Matthias Clasen
05c869d19b inspector: Expand the actions list 2019-12-24 15:36:08 -05:00
Matthias Clasen
57d635e1a1 inspector: Expand the resource list
This is an experiment with adding a filler column.
2019-12-24 15:36:08 -05:00
Matthias Clasen
2c1a0d3d2e inspector: Expand the property list
It looks better this way.
2019-12-24 15:36:08 -05:00
Matthias Clasen
d5d3b4b991 inspector: Expand the object tree
This is how it used to look, and it looks better that way.
2019-12-24 15:36:08 -05:00
Matthias Clasen
19166f15e3 columnview: Take expand into account
When allocating columns, distribute extra space
to columns that have expand set to TRUE.
2019-12-24 15:36:08 -05:00
Matthias Clasen
fe31c2221c columnviewcolumn: Add an expand property
This will be used to determine how to distribute
available extra space in a column view.
2019-12-24 15:36:08 -05:00
Matthias Clasen
50e8ccf030 gtk-demo: Add more scrolling benchmarks
Add a listview and gridview to the scrolling
benchmarks.
2019-12-24 15:36:08 -05:00
Matthias Clasen
7539fe1acc gtk-demo: Make gridview demo use rubberbanding 2019-12-24 15:36:08 -05:00
Matthias Clasen
ee03ae8b9b Add rubberband api
Add an enable-rubberband property to GtkListView,
GtkGridView and GtkColumnView.
2019-12-24 15:36:08 -05:00
Matthias Clasen
7bf21cf9d2 listbase: Add rubberband selection
Implement the typical rubberband selection, including
autoscroll. This is only useful with multiselection,
and not very compatible with single-click-activate.
Therefore, it is not enabled by default, and needs
to be turned on with the enable-rubber-band property.
2019-12-24 15:36:08 -05:00
Matthias Clasen
466aec41f5 multiselection: Add a copy constructor
Add a function to create a multiselection that
is a copy of an existing selection model. We
can do this efficiently if the original is a
multiselection as well, by just copying the
entire set in on go.
2019-12-24 15:36:08 -05:00
Matthias Clasen
5e9adeb9bf Add GtkMultiSelection
This is implemented using a private GtkSet helper.
2019-12-24 15:36:08 -05:00
Matthias Clasen
c7b2307a0b columnviewtitle: Display a context menu
When the ::header-menu property is set on the
column, use the menu model to create and show
a context menu.
2019-12-24 15:36:08 -05:00
Matthias Clasen
6e838098cb columnviewcolumn: Add a menu property
Add a ::header-menu property that will be used
to create a context menu for the header of the
column.
2019-12-24 15:36:08 -05:00
Matthias Clasen
ee76623e54 Add a selection model test
The test shows that we are failing to emit
::selection-changed in some circumstances.
2019-12-24 15:36:08 -05:00
Matthias Clasen
36aaece297 testcolumnview: Reordering in the column editor
Use Ctrl-Up/Down to move the column around.
2019-12-24 15:36:08 -05:00
Matthias Clasen
4c8de636ce testcolumnview: Flesh out column editor
Turn the column list into an editor with
controls for visibility, resizability, reorderability
and width.
2019-12-24 15:36:08 -05:00
Matthias Clasen
8c086dbdc7 columview: Add autoscroll
Autoscroll when the pointer gets close to the
edge during column resizing or reordering. This
is similar to what the treeview does, but it is
implemented using a tick callback, and has
variable speed.
2019-12-24 15:36:08 -05:00
Matthias Clasen
f86aeaf8cd columnview: Allow to cancel reorder with Escape
The treeview does this too.
2019-12-24 15:36:08 -05:00
Matthias Clasen
c814864a3f columnview: Interactive column reordering
Allow rearranging columns by dragging, in the same
way the treeview does.

We add the "dnd" style class to the header while
it is dragged, and we move the header of the dragged
column to the end of its parents children, so that
it gets drawn on top.
2019-12-24 15:36:08 -05:00
Matthias Clasen
9e9d4d914e columnviewcolumn: Add a reorderable property
This will be used for interactive column reordering
in the future.
2019-12-24 15:36:08 -05:00
Matthias Clasen
8f2351d5d3 columnviewlayout: Use header allocation for titles
Normally, this will be identical to the column
allocation, but we will temporarily change it
during column reordering.
2019-12-24 15:36:08 -05:00
Matthias Clasen
4817c7757b columnviewcolumn: Add reordering helpers
Add helper functions that let us temporarily give
a different allocation to headers. These will be
used to implement interactive column reordering
in GtkColumnView.
2019-12-24 15:36:08 -05:00
Matthias Clasen
a44c4b4877 columnviewtitle: Invert on release
This is necessary to make drag-to-reorder work
without triggering inversion.
2019-12-24 15:36:08 -05:00
Matthias Clasen
f8a14427ea columnview: Interactive column resizing
This copies just enough of the treeview code to
get columns moving.
2019-12-24 15:36:08 -05:00
Matthias Clasen
9d0904a445 columnviewcolumn: Add a helper
We need to check whether clicks are in the headers
of columns, so let the column view get at the the
header widget.
2019-12-24 15:36:08 -05:00
Matthias Clasen
4a659c169d columnviewcolumn: Add a resizable property
This will be used for interactive column resizing
in the future.
2019-12-24 15:36:08 -05:00
Matthias Clasen
5ca53555c7 Turn GtkRecentInfo into an object
This is in preparation to eventually using
a list model of recent infos.
2019-12-24 15:36:08 -05:00
Matthias Clasen
0bbc074e08 gtk-demo: Add row reordering to the settings demo 2019-12-24 15:36:08 -05:00
Matthias Clasen
7af244944a gtk-demo: Add filtering to the settings demo
A demo of filtering with lists was missing so far.
2019-12-24 15:36:08 -05:00
Matthias Clasen
ae084d2929 gtk-demo: Crank up max columns of gridview
This lets us fit more cells on screen.
2019-12-24 15:36:08 -05:00
Matthias Clasen
fe3dd5cd8b gtk-demo: Make gridview content switchable
Add a dropdown to switch out the item factory.
2019-12-24 15:36:08 -05:00
Matthias Clasen
3dbac6b133 gtk-demo: Add sorting to the gridview demo 2019-12-24 15:36:08 -05:00
Matthias Clasen
2cb5ce8c72 gtk-demo: Cosmetic changes for the flowbox demo
Mention the size of the dataset.
2019-12-24 15:36:08 -05:00
Matthias Clasen
8ae6e58e0c gtk-demo: Cosmetic changes to the file browser demo
Mention that it involves switching between
different views.
2019-12-24 15:36:08 -05:00
Matthias Clasen
6eebc9183a gtk-demo: Cosmetic changes for the weather demo
Give the weather demo a size and a title, and mention
the size of the dataset in the description.
2019-12-24 15:36:08 -05:00
Matthias Clasen
b84780cc98 gtk-demo: Cosmetic changes to the clocks demo
Give the clocks demo a title and size,
and tweak the description.
2019-12-24 15:36:08 -05:00
Matthias Clasen
46a6ab68f2 gtk-demo: Demo columnview sorting
Enhance the settings demo to have a sortable column.
2019-12-24 15:36:08 -05:00
Matthias Clasen
e5c57d10d7 filechooser: Use a dropdown for choices 2019-12-24 15:36:08 -05:00
Matthias Clasen
9aed6a2b56 filechooser: Use a dropdown for the filter combo 2019-12-24 15:36:08 -05:00
Matthias Clasen
843fa70bef inspector: Use dropdowns in the visual page
Convert everything in the visual page to dropdowns.
2019-12-24 15:36:08 -05:00
Matthias Clasen
2ea918a5e7 inspector: Use a dropdown for size groups
Use a GtkDropDown for the modes of size groups.
2019-12-24 15:36:08 -05:00
Matthias Clasen
f7608a2119 inspector: Use a dropdown for controllers
Use a GtkDropDown for the phases of event controllers.
2019-12-24 15:36:07 -05:00
Matthias Clasen
f55528fd10 inspector: Use a dropdown for attribute mapping
Use a GtkDropDown for the attribute mapping editor.
2019-12-24 15:36:07 -05:00
Matthias Clasen
1d7322ed63 inspector: Use dropdowns in property editor
Replace combo boxes by dropdowns in the property editor.
2019-12-24 15:36:07 -05:00
Matthias Clasen
f3dc528e7d Add GtkDropDown
This is a simple drop down control using list models.
2019-12-24 15:36:07 -05:00
Matthias Clasen
6265a60663 columnview: Add a helper
The column code needs to get access to the
listitem widgets that are children of the listview,
so add a getter.
2019-12-24 15:36:07 -05:00
Matthias Clasen
08bbf44602 columnview: Add column reordering
Add an API to allow reordering columns.
2019-12-24 15:36:07 -05:00
Matthias Clasen
640f165a81 columnviewcolumn: Add a fixed-width property
When fixed-width is set to a value > -1, we make the
measure function of cell and title widgets return this
width, and we set overflow to hidden.
2019-12-24 15:36:07 -05:00
Matthias Clasen
34cbaa9057 columnview: Implement horizontal scrolling
The listview inside always thinks it gets its full size,
and updates its horizontal adjustment accordingly.

So keep our own adjustment, and update it in size_allocate.
2019-12-24 15:36:07 -05:00
Matthias Clasen
2f666332ea columnview: Revise scroll-minimum handling
Tweak the behavior slightly. We don't show
a scrollbar as long as we have at least
min-size available, but we still give the
entire size to the child, up to nat-size.

This matches how viewports handle scroll-minimum.
2019-12-24 15:36:07 -05:00
Matthias Clasen
972090f34d column: Add a visible property
This lets us hide columns, which is useful.
2019-12-24 15:36:07 -05:00
Matthias Clasen
6ab8b1cfe5 inspector: Touch up list styling
This is just the minimal amount of work to make
headers recognizable.
2019-12-24 15:36:07 -05:00
Matthias Clasen
84fd2290a6 inspector: Use a column view for actions
A straight conversion from list box to column view.
2019-12-24 15:36:07 -05:00
Matthias Clasen
55f9e86d67 inspector: Make the resource list sortable
This is using a GtkTreeListRowSorter to keep expanded
state of the tree while changing the sorting.
2019-12-24 15:36:07 -05:00
Matthias Clasen
9892831d52 inspector: Use a column view for the resource list
A conversion from tree view to column view.
2019-12-24 15:36:07 -05:00
Matthias Clasen
baff649ca6 inspector: Use a column view for properties
Just a straight conversion from list box to column view.
2019-12-24 15:36:07 -05:00
Matthias Clasen
921ff0460b columnview: Add a sort-by api 2019-12-24 15:36:07 -05:00
Matthias Clasen
cab09d27db columnview: Document sorting
Add a paragraph outlining how sorting is set up
for GtkColumnView.
2019-12-24 15:36:07 -05:00
Matthias Clasen
65b3a9358a docs: Fix misc typos and omissions 2019-12-24 15:36:07 -05:00
Matthias Clasen
649bcc69e3 docs: Add more private headers to ignore 2019-12-24 15:36:07 -05:00
Matthias Clasen
2357771bd9 treelistmodel: Small documentation tweaks 2019-12-24 15:36:07 -05:00
Matthias Clasen
d53a0ba584 sorter: Small documentation fixes 2019-12-24 15:36:07 -05:00
Matthias Clasen
e2f78e95b2 numericsorter: Small documentation improvements 2019-12-24 15:36:07 -05:00
Matthias Clasen
dea3c8aed0 multisorter: Small documntation improvements 2019-12-24 15:36:07 -05:00
Matthias Clasen
f2a82e7d35 multifilter: Small documentation improvements 2019-12-24 15:36:07 -05:00
Matthias Clasen
9ff85899d0 builderlistitemfactory: Add an example to the docs 2019-12-24 15:36:07 -05:00
Matthias Clasen
602d141a7b maplistmodel: Add an example to the docs 2019-12-24 15:36:07 -05:00
Matthias Clasen
0bed5a8b9d stringfilter: Documnentation additions 2019-12-24 15:36:07 -05:00
Matthias Clasen
f2a50fa590 filter: Documentation fixes 2019-12-24 15:36:07 -05:00
Matthias Clasen
f833959acd filterlistmodel: Documentation fixes 2019-12-24 15:36:07 -05:00
Matthias Clasen
02168260e0 expression: Small documentation fixes 2019-12-24 15:36:07 -05:00
Matthias Clasen
68fbb59f82 docs: Add gtk_expression_watch_unwatch 2019-12-24 15:36:07 -05:00
Matthias Clasen
e7f3a4f61a expression: Correct an example in the docs 2019-12-24 15:36:07 -05:00
Matthias Clasen
e32ae895e3 Add some tests for new GtkBuilder syntax
Some valid and invalid examples for <closure>,
<lookup> and <constant>.
2019-12-24 15:36:07 -05:00
Matthias Clasen
44c5d47ed8 gtk-builder-tool: Minimally validate <binding>
Check that the toplevel property name is legit.
2019-12-24 15:36:07 -05:00
Matthias Clasen
a9f963b5d9 multifilter: Fix a few annotations 2019-12-24 15:36:07 -05:00
Matthias Clasen
0a711ec0b2 builderlistitemfactory: Fix a few annotations 2019-12-24 15:36:07 -05:00
Matthias Clasen
1630482d13 treelistrowsorter: Fix a doc typo 2019-12-24 15:36:07 -05:00
Matthias Clasen
bdaa41184f columnviewcolumn: Fix a type mismatch
The column-view property was declared with the wrong
type. This was causing criticals in the inspector.
2019-12-24 15:36:07 -05:00
Matthias Clasen
0a36ba0611 listitemwidget: Fix single-click-activate
This was broken by the renaming of ::contains-pointer-focus
to ::contains-pointer. Fix it up.
2019-12-24 15:36:07 -05:00
Matthias Clasen
cc39cc2e8d Fix gtk_column_view_create_cells
This code was assuming that the listitem widgets
are children of the column view, which they aren't.
2019-12-24 15:36:07 -05:00
Matthias Clasen
51e2a51dfd filterlistmodel: Remove pre-filter remnants
We are not using the filter func typdef anymore.
2019-12-24 15:36:07 -05:00
Matthias Clasen
90b32cac0b Add filter test to testsuite 2019-12-22 23:12:21 -05:00
Matthias Clasen
b56cfc5293 Add a test for multi filter unreffing
This tests the fix in the previous commit.
2019-12-22 23:12:21 -05:00
Matthias Clasen
7bb1f036d6 multifilter: Remove an extraneous unref
The sequence is using unref as free func,
no need to ido it ourselves.
2019-12-22 23:12:21 -05:00
Benjamin Otte
0cd203b07d testsuite: Add tests for GtkTreeListSorter 2019-12-18 05:48:03 +01:00
Matthias Clasen
86b29d4d7a Add GtkTreeListRowSorter
This is a special-purpose sorter that can
apply the sorting of another sorter to the
levels of a GtkTreeListModel.
2019-12-18 05:48:02 +01:00
Benjamin Otte
3ffb005135 testcolumnview: Add sorters 2019-12-18 05:48:02 +01:00
Matthias Clasen
a4b15d205e column view title: Show sort indicators 2019-12-18 05:48:02 +01:00
Matthias Clasen
0b4f75cf25 columnview: Add sorting
This is a somewhat large commit that:

- Adds GtkColumnViewSorter
This is a special-purpose, private sorter implementation which sorts
according to multiple sorters, allowing each individual sorter to be
inverted. This will be used with clickable column view headers.

- Adds a read-only GtkColumnView::sorter property
The GtkColumnView creates a GtkColumnViewSorter at startup that it uses
for this property.

- Adds a writable GtkColumnViewColumn::sorter property
This allows defining per-column sorters. Whenever an application sets a
sorter for a column, the header becomes clickable and whenever
a header is clicked, that column's sorter is prepended to the list of
sorters, unless it is already the first sorter, in which case we invert
its order. No column can be in the list more than once.
2019-12-18 05:48:02 +01:00
Matthias Clasen
163477acfb gtk-demo: Add a large grid demo
This is similar to the flowbox demo, but much bigger.
2019-12-18 05:45:39 +01:00
Matthias Clasen
f80335505e listview: Add single-click-activate
Add a single-click-activate property to GtkListView.
2019-12-18 05:45:39 +01:00
Matthias Clasen
ffdcde53f5 listitemwidget: Add single-click-activate
Add a mode to GtkListItemWidget that activates on
single click and selects on hover. Make
GtkListItemManager set this on its items
when its own 'property' of the same name is set.
2019-12-18 05:45:39 +01:00
Matthias Clasen
5d836b5e15 builder-tool: Pass through CDATA where it makes sense
This avoids a ton of escaping for
GtkBuilderListItemFactory::bytes.
2019-12-18 05:45:39 +01:00
Matthias Clasen
539213133f docs: Reorganize list widgets in their own chapter 2019-12-18 05:45:39 +01:00
Benjamin Otte
6977a03f65 fontchooserwidget: Port to listmodels
The port is kind of evil, in that it stores either a PangoFontFamily or a
PangoFontFace in the list, depending on if the fontchooser is configured
to select fonts or faces.
It also does not cache the font description anymore, so more calls to
pango_font_describe() may happen.

If both of these issues turn out problematic, the fontchooser would need
to resurrect GtkDelayedFontDescription again and put objects of that
type through the model.

These changes depend on Pango 1.46's introduction of listmodels and
various new getters, so the dependency has been upgraded.
2019-12-18 05:45:39 +01:00
Matthias Clasen
0875374b1d Add some tests for expression binding
In particular, test that expressios can deal with object == this.
2019-12-18 05:45:39 +01:00
Benjamin Otte
fe1ce2aaf8 expression: Allow passing a this object to bind()
This gives a bit more control over the arguments passed to expressions.
2019-12-18 05:45:39 +01:00
Benjamin Otte
a81429c10f widget: Do parent_class handling properly
The previous cosde did not actually query the parent class, it just did
a very complicated C cast.
2019-12-18 05:45:39 +01:00
Benjamin Otte
2f52d98ef4 gtk-demo: Add a Clocks demo
This demo is meant to showcase expressions.

It also needs the fixes in glib 2.64 to work properly.
2019-12-18 05:45:39 +01:00
Benjamin Otte
5a076b0c2f xxx: Add a hack to make paintables transform to/from objects
See also: https://gitlab.gnome.org/GNOME/glib/merge_requests/1251
2019-12-18 05:45:39 +01:00
Benjamin Otte
69f59109ee inspector: Remove private struct for prop editor 2019-12-18 05:45:39 +01:00
Benjamin Otte
6bf508ce73 inspector: Make Controller page a GtkWidget 2019-12-18 05:45:39 +01:00
Benjamin Otte
ae26ce017b inspector: Remove private struct from controllers 2019-12-18 05:45:39 +01:00
Benjamin Otte
0a126f65fb columnview: Add header
This uses a custom GtkColumnViewTitle widget. So far that widget is
pretty boring, but that will change once we added
resizing, reordering, dnd, sorting, hiding/showing of columns or
whatever UIs we want.
2019-12-18 05:45:39 +01:00
Benjamin Otte
6d0695260d tests: Add testcolumnview 2019-12-18 05:45:37 +01:00
Benjamin Otte
a43e0b1e62 columnview: Add a custom LayoutManager
The ColumnView now allocates column widths first and then the individual
rows use the new layout manager which looks at the column allocations to
allocate their children.
2019-12-15 23:33:04 +01:00
Benjamin Otte
01d4a28c9f constraint-editor: Don't poke around in widget internals 2019-12-15 23:33:04 +01:00
Benjamin Otte
052343ca7d columnview: Fix styling with Adwaita
- Use "treeview" as the node name
- Add .view style class
2019-12-15 23:33:04 +01:00
Benjamin Otte
7f4260f97e inspector: Port object tree to GtkColumnView 2019-12-15 23:33:04 +01:00
Benjamin Otte
2b42f0fcb3 columnview: Add GtkColumnViewCell
It's a GtkListItemWidget subclass that tracks the column it belongs to
and allows the column to track it.

We also use this subclass to implement sizing support so columns share
the same size and get resized in sync.
2019-12-15 23:33:04 +01:00
Benjamin Otte
f46c405351 widget: Add a hook for resizes
It's private, no APIs, we don't talk about it. But we will start using
it very soon, so we can do size request caching in columns and avoid
sizegroups...
2019-12-15 23:33:04 +01:00
Benjamin Otte
8f4a0391b3 columnview: Implement GtkScrollable
Just forward it to the listview for now.
2019-12-15 23:33:04 +01:00
Benjamin Otte
5b282411c4 columnview: Add listitems for the columns
They are not aligned in columns yet, but they do exist.
2019-12-15 23:33:04 +01:00
Benjamin Otte
e4392bdf5a listitemwidget: Lazily create listitems
We only create them in root/unroot (they should be created in
appear/disappear, but that vfunc doesn't exist yet), that way we can
avoid expensive work while the widget isn't used for anything.
2019-12-15 23:33:04 +01:00
Benjamin Otte
e845e93f58 listitem: Move position/item/selected tracking to widget
This way, we can ensure it's always there when we need it (before the
item gets created) and gone when we don't (if some GC language holds on
to the item after we've destroyed the widget).
2019-12-15 23:33:04 +01:00
Benjamin Otte
98b4269abd listitemwidget: Add a private struct
I had to rename the item property to list_item anyway, so I could just
do the next step with it.
2019-12-15 23:33:04 +01:00
Benjamin Otte
9b132b80ae listitemfactory: Simplify
Instead of 6 vfuncs, we now have 3 and rely on the factory keeping track
of what it needs to do.

We're doing lots of dancing from one object to another here, but this
will hopefully get simpler with further commits.
2019-12-15 23:33:04 +01:00
Benjamin Otte
7c402bcf2c listitemfactory: Reorganize vfuncs
Instead of bind/rebind/update/unbind, we now just have update, and the
factories get to interpret that in the way they want.
2019-12-15 23:33:04 +01:00
Benjamin Otte
6d73e3a6fa listitem: Make this a GObject
This splits GtkListItem into 2 parts:

1. GtkListItem
   This is purely a GObject with public API for developers who want to
   populate lists. There is no chance to cause conflict with GtkWidget
   properties that the list implementation assumed control over and
   defines a clear boundary.
2. GtkListItemWidget
   The widget part of the listitem. This is not only fully in control of
   the list machinery, the machinery can also use different widget
   implementations for different list widgets like I inted to for
   GtkColumnView.
2019-12-15 23:33:04 +01:00
Benjamin Otte
730575677f builder: Make gtk_builder_extend_with_template() work with objects
This will be relevant later when we introduce GtkListItem which is not a
GtkWidget.
2019-12-15 23:33:04 +01:00
Benjamin Otte
d4d07bcb2a gtk-demo: Add a Coverflow application launcher
This is roughly the simplest demo I could come up with.

But I documented it, so there's your tutorial.

Related: #2214
2019-12-15 23:33:04 +01:00
Benjamin Otte
01cdc67db0 Add GtkSignalListItemFactory
So the poor Rust users can actually use this.

I would totally not use this ever!
2019-12-15 23:33:04 +01:00
Benjamin Otte
77c9d0d0fb columnview: Allow adding/removing columns
... and make that work in UI files via <child>, too.
2019-12-15 23:33:04 +01:00
Benjamin Otte
3cc9fa9f67 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-12-15 23:33:04 +01:00
Benjamin Otte
181b9655ef 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-12-15 23:33:04 +01:00
Benjamin Otte
1eab9ba350 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-12-15 23:33:04 +01:00
Benjamin Otte
d1d0adeae9 listbase: Take over anchor handling
With that, pretty much all code but allocating the widgets is gone from
the gridview and listview.
2019-12-15 23:33:04 +01:00
Benjamin Otte
64a2f04006 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-12-15 23:33:04 +01:00
Benjamin Otte
c835bb30d1 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-12-15 23:33:04 +01:00
Benjamin Otte
96d333f649 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-12-15 23:33:04 +01:00
Benjamin Otte
66142ae74f listbase: Move orientable implementation here 2019-12-15 23:33:04 +01:00
Benjamin Otte
75f539b3a4 listbase: Move selection handling here 2019-12-15 23:33:04 +01:00
Benjamin Otte
ad12914c55 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-12-15 23:33:04 +01:00
Benjamin Otte
f614011569 listbase: Move GtkScrollable implementation
Shared code between GtkGridView and GtkListView.
2019-12-15 23:33:04 +01:00
Benjamin Otte
013db26d22 Add GtkListBase
This is a base item for GTK's list widgets so they can share some (read:
hopefully a lot of) code.
2019-12-15 23:33:04 +01:00
Benjamin Otte
9fab1b3d25 gridview: Simplify allocation code
It doesn't fix the bug I'm after, but it looks a lot better.
2019-12-15 23:33:04 +01:00
Benjamin Otte
c3bb95d7d9 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-12-15 23:33:04 +01:00
Benjamin Otte
27a891adb8 demo: Add a file browser demo 2019-12-15 23:33:04 +01:00
Benjamin Otte
94b162829c gridview: Add move keybindings 2019-12-15 23:33:04 +01:00
Benjamin Otte
e54f3f68a0 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-12-15 23:33:04 +01:00
Benjamin Otte
ddc665bb01 gridview: Add a focus tracker
... and use that to properly update selections when moving around with
the arrow keys.
2019-12-15 23:33:04 +01:00
Benjamin Otte
d0495e36e6 gridview: Implement list.scroll-to action 2019-12-15 23:33:04 +01:00
Benjamin Otte
4444bb3d98 gridview: Add activation 2019-12-15 23:33:04 +01:00
Benjamin Otte
b9616124cb 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-12-15 23:33:04 +01:00
Benjamin Otte
6218a27daf gridview: Implement the list.select-item action 2019-12-15 23:33:04 +01:00
Benjamin Otte
51ecc2d566 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-12-15 23:33:04 +01:00
Benjamin Otte
050bd6d5e3 gridview: Implement anchors and scrolling 2019-12-15 23:33:04 +01:00
Benjamin Otte
0229f11784 widget: Add gtk_widget_get_size()
A little bit of convenience.
2019-12-15 23:33:04 +01:00
Benjamin Otte
cee950b455 listitemmanager: Handle NULL factory
Just don't call it and create empty listitems.
2019-12-15 23:33:04 +01:00
Timm Bäder
3a5f47c3b8 demo: Use a listview as sidebar 2019-12-15 23:33:04 +01:00
Benjamin Otte
484f804ba6 gtk-demo: Introduce awards
We need a way to get a useful listbox, so here we go!
2019-12-15 23:33:04 +01:00
Benjamin Otte
5b96335956 builder: Autofill scope property of listitemfactory
I couldn't come up with a better way to automatically inherit the scope
in the builder list item factory that didn't involve a magic
incantation in the XML file. And I do not want developers to know magic
incantations to do a thing that should pretty much always be done.
2019-12-15 23:33:04 +01:00
Benjamin Otte
61362af27b builderlistitemfactory: Add scope argument
This way, the scope used when creating builder instances can be
influenced. This way, callbacks can be passed into the factory.
2019-12-15 23:33:04 +01:00
Benjamin Otte
fc82f5fe29 listitemfactory: Make the builder factory properly buildable
Turn the construct arguments into construct properties so that they can
be set from ui files.
2019-12-15 23:33:04 +01:00
Benjamin Otte
5ae2c1fdca listview: Add move keybindings
My god, these are a lot.

And my god, these are complicated to get right.
2019-12-15 23:33:04 +01:00
Benjamin Otte
eba9ec613d listview: Add gtk_list_view_get_position_at_y() 2019-12-15 23:33:04 +01:00
Benjamin Otte
61fc0a6a59 listitem: Add "listitem.select" action and keybindings for it
In fact, grab space with all modifiers to toggle selection of the
current item.
2019-12-15 23:33:03 +01:00
Benjamin Otte
250760468b listview: Add a focus tracker
This ensures that the row with the input focus always stays available,
even when scrolled out of view.
2019-12-15 23:33:03 +01:00
Benjamin Otte
9d5063cf68 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-12-15 23:33:03 +01:00
Benjamin Otte
c4af364bfb listview: Track focus movements and update selection
When focus moves via tab/arrow, we need to select the new item.
2019-12-15 23:33:03 +01:00
Benjamin Otte
3e1cad5755 listview: Implement activation
- a GtkListview::activate signal
- a GtkListItem::activatable property
- activate list items on double clicks and <Enter> presses
2019-12-15 23:33:03 +01:00
Benjamin Otte
42c077443e 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-12-15 23:33:03 +01:00
Benjamin Otte
7a49d2e848 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-12-15 23:33:03 +01:00
Benjamin Otte
462953cac0 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-12-15 23:33:03 +01:00
Benjamin Otte
5b5ab5c592 inspector: Use a GtkTreeExpander in the object tree 2019-12-15 23:33:03 +01:00
Benjamin Otte
d89f9b6684 inspector: Use a treeexpander in the recorder 2019-12-15 23:33:03 +01:00
Benjamin Otte
71f104782a 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-12-15 23:33:03 +01:00
Benjamin Otte
297a2795c3 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-12-15 23:33:03 +01:00
Benjamin Otte
081645186d 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-12-15 23:33:03 +01:00
Benjamin Otte
20201662f1 listview: Pass the CSS name of listitems to the manager
... instead of hardcoding "row".
2019-12-15 21:11:03 +01:00
Benjamin Otte
3c97226e66 gridview: Implement GtkOrientable
Again, this is just the skeleton, because the Gridview does nothing yet.
2019-12-15 21:11:03 +01:00
Benjamin Otte
6b4b1e1921 gridview: Add factory handling
Just copy the listview APIs.

Code still doesn't do anything with it.
2019-12-15 21:11:03 +01:00
Benjamin Otte
246421f92c 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-12-15 21:11:03 +01:00
Benjamin Otte
603055b70d textview: Make cursor work when blinking is disabled 2019-12-15 21:10:29 +01:00
Benjamin Otte
6a26bb0e2f gtk-demo: Add a rough start at a Weather demo
This demos a horizontal listview.
2019-12-15 21:10:29 +01:00
Benjamin Otte
81bc2117b2 listview: Implement GtkOrientable 2019-12-15 21:10:28 +01:00
Benjamin Otte
f307f99275 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-12-15 21:10:28 +01:00
Benjamin Otte
8157408df8 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-12-15 21:10:28 +01:00
Benjamin Otte
9630b9f244 listview: Add list.scroll_to_item action
The action scrolls the given item into view.

Listitems activate this action when they gain focus.
2019-12-15 21:10:28 +01:00
Benjamin Otte
4b277dd47a testlistview: Load icons async
Speeds up loading by 4x, because out of view icons aren't loaded
anymore.
2019-12-15 21:10:28 +01:00
Benjamin Otte
5fcdf00a1a testlistview: Port to directory list 2019-12-15 21:10:28 +01:00
Benjamin Otte
48e1a3cc06 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-12-15 21:10:28 +01:00
Benjamin Otte
8c5b077555 listitemfactory: Split implementation out
.. into gtkfunctionslistitemfactory.c

Now we can add a different implmenetation.
2019-12-15 21:10:28 +01:00
Benjamin Otte
023ff509b2 listitemfactory: vfuncify
No functional changes other than a new indirection.
2019-12-15 21:10:28 +01:00
Benjamin Otte
a0a7da2e3b 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-12-15 21:10:28 +01:00
Benjamin Otte
b7ae37ab3d listview: Add gtk_list_view_set_show_separators()
Do the same thing that GtkListBox does in commit
0249bd4f8a
2019-12-15 21:10:28 +01:00
Benjamin Otte
6d8c6bb2f7 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-12-15 21:10:28 +01:00
Benjamin Otte
b00c04a986 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-12-15 21:10:28 +01:00
Benjamin Otte
62da088018 gridview: Implement GtkScrollable
We can now scroll all the nothing we display.

We also clip it properly.
2019-12-15 21:10:28 +01:00
Benjamin Otte
6d93197f08 listitemmanager: Move list of listitems here
All the listview infrastructure moved with it, so the next step is
moving that back...
2019-12-15 21:10:28 +01:00
Benjamin Otte
72ce2ff512 wayland: Remove function declaration for nonexisting function 2019-12-15 21:10:28 +01:00
Benjamin Otte
cfd503893c gridview: Add API for setting number of columns
The API isn't used yet.
2019-12-15 21:10:28 +01:00
Benjamin Otte
a2ec02875a gtk: Add a GtkGridView skeleton 2019-12-15 21:10:28 +01:00
Benjamin Otte
07036e28c0 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-12-15 21:10:28 +01:00
Benjamin Otte
0b3135bf14 listview: Add initial support for displaying selections 2019-12-15 21:10:28 +01:00
Benjamin Otte
2ea5607f79 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-12-15 21:10:28 +01:00
Benjamin Otte
35e516f996 listview: Add selection properties to ListItem
This just brings the infrastructure into place, we're not using the
properties yet.
2019-12-15 21:10:28 +01:00
Benjamin Otte
b856fe66b9 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-12-15 21:10:28 +01:00
Benjamin Otte
7d01e25558 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-12-15 21:10:28 +01:00
Benjamin Otte
b99db2a60b 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-12-15 21:10:28 +01:00
Benjamin Otte
085e4ad273 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-12-15 21:10:28 +01:00
Benjamin Otte
72dea0d5e9 testlistview: Show the row number
Always show the current row. This is mostly useful for debugging, not
for beauty.
2019-12-15 21:10:28 +01:00
Benjamin Otte
6dd18fafaa 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-12-15 21:10:28 +01:00
Benjamin Otte
9b9aec360e 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-12-15 21:10:28 +01:00
Benjamin Otte
0db67c9709 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-12-15 21:10:28 +01:00
Benjamin Otte
c935bf5648 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-12-15 21:10:28 +01:00
Benjamin Otte
94a9752c43 tests: Make animating listview do random resorts 2019-12-15 21:10:28 +01:00
Benjamin Otte
de005bd1d6 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-12-15 21:10:28 +01:00
Benjamin Otte
134db60479 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-12-15 21:10:28 +01:00
Benjamin Otte
662b142c86 listview: Add GtkListItem
GtkListItem is a generic row widget that is supposed to replace
GtkListBoxRow and GtkFlowBoxChild.
2019-12-15 21:10:28 +01:00
Benjamin Otte
22a9001103 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-12-15 21:10:28 +01:00
Benjamin Otte
42b62c7ba2 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-12-15 21:10:28 +01:00
Benjamin Otte
8bf19f7cbf 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-12-15 21:10:28 +01:00
Benjamin Otte
15dce5c26c listview: Implement GtkScrollable
Scrolling in a very basic form is also supported
2019-12-15 21:10:28 +01:00
Benjamin Otte
742dfd919d 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-12-15 21:10:28 +01:00
Benjamin Otte
b5e54c1e7d 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-12-15 21:10:28 +01:00
Benjamin Otte
9756330b75 gtk: Add a GtkListView skeleton 2019-12-15 21:10:28 +01:00
Benjamin Otte
8126b24a78 builder: Add <binding> tag
The tag contains an expression that it then gtk_expression_bind()s to
the object it is contained in.
2019-12-15 21:10:28 +01:00
Benjamin Otte
9520a04d26 builder: Allow text content in <lookup>
<lookup>foo</lookup>
is now short for
  <lookup>
    <constant>foo</constant>
  </lookup>
ie it looks up the object with the given name so it can then do a
property lookup with it.

This is the most common operation, so it's a nice shortcut.
2019-12-15 21:10:28 +01:00
Benjamin Otte
aa1d21a7ae builder: Allow <constant> without a type
A constant without a type is assumed to be an object. This is the most
common case and allows
  <constant>foo</constant>
without requiring updates to the type whenever the foo object changes.
2019-12-15 21:10:28 +01:00
Benjamin Otte
1c354586d0 builder: Make <lookup> type optional
If no type is set, use the type of the expression.
2019-12-15 21:10:28 +01:00
Benjamin Otte
90277fba71 gtk-demo: Make fishbowl info text use bindings
It's a good demo for how bindings can format multiple properties into an
informative string with 1 line of code (and 5 lines of XML).
2019-12-15 21:10:28 +01:00
Matthias Clasen
830939d8c7 sorter: Add tests
Some basic tests for GtkSorter.
2019-12-15 21:10:28 +01:00
Benjamin Otte
900616ec7a sortlistmodel: Make sort stable
The sort of the sortlistmodel is now stable with respect to the original
list model.

That means that if the sorter compares items as equal, the model
will make sure those items keep the order they were in in the original
model.

Or in other words: The model guarantees a total order based on the
item's position in the original model.
2019-12-15 21:10:28 +01:00
Benjamin Otte
2c047aa76f sortlistmodel: Redo the way we store the items
We need to keep this data around for changes in future commits where we
make the sorting stable.

An important part of the new data handling is that the unsorted list
needs to always be dealt with before the sorted list - upon creation we
rely on the unsorted iter and upon destruction, the sorted sequence
frees the entry leaving the unsorted sequence pointer invalid.

This change does not do any behavioral changes.
2019-12-15 21:10:28 +01:00
Matthias Clasen
c1071d8c5d Redo sort list model with GtkSorter
Reshuffle the api to take full advantage
of GtkSorter. Update all callers.
2019-12-15 21:10:28 +01:00
Matthias Clasen
7c9ebaef3d Add GtkNumericSorter
This sorter compares numbers obtained from items
by evaluating an expression.
2019-12-15 21:10:28 +01:00
Matthias Clasen
90c75c8b9a Add GtkMultiSorter
This is a sorter that tries multiple sorters in turn.
2019-12-15 21:10:28 +01:00
Matthias Clasen
3c96f65094 Add GtkStringSorter
This is a GtkSorter implementation collating strings
2019-12-15 21:10:28 +01:00
Matthias Clasen
06fbc82ddf Add GtkCustomSorter
This is a GtkSorter implementation which uses a GCompareDataFunc.
2019-12-15 21:10:28 +01:00
Matthias Clasen
575b42c481 Add GtkSorter
This is a helper object for sorting, similar to GtkFilter.
2019-12-15 21:10:28 +01:00
Benjamin Otte
10631c979d Add GtkOrdering
This is an enum that we're gonna use soon and it's worth introducing as a
separate commit.

The intention is to have meaningful names for return values in
comparison functions.
2019-12-15 21:10:28 +01:00
Matthias Clasen
c4ce8ef60f More expression tests
Test type mismatches, and the this pointer
during evaluation.
2019-12-15 21:10:28 +01:00
Benjamin Otte
04df027567 expression: Invalidate bindings before destroying them
Use a weak ref to invalidate bindings. Make sure that this happens
before creating any watches, so that notifies from the
watched expression about changes will not trigger set_property() calls
during dispose()/finalize().

Invalidating also ensures that the watches aren't removed, which can
trigger warnings if the watches are watching the object itself, and the
weak refs cannot be removed anymore.
2019-12-15 21:10:28 +01:00
Benjamin Otte
fc0751a909 expression: Add gtk_expression_bind()
Add a simple way to bind expressions to object properties. This is
essentially the thing to replace g_object_bind_property().
2019-12-15 21:10:28 +01:00
Benjamin Otte
03815102d5 testsuite: Add expression tests 2019-12-15 21:10:28 +01:00
Benjamin Otte
e8613aab9c expression: Add the ability to watch an expression 2019-12-15 21:10:28 +01:00
Benjamin Otte
b603f472ab builder: Add support for parsing expressions 2019-12-15 21:10:28 +01:00
Benjamin Otte
b4b6be9f6e filter: Add tests
Some basic tests for GtkFilter
2019-12-15 21:10:28 +01:00
Benjamin Otte
bb12d3d8ca Add GtkMultiFilter, GtkAnyFilter, GtkEveryFilter
GtkMultiFilter is the abstract base class for managing multiple child
filter.
GtkAnyFilter and GtkEveryFilter are the actual implementations.
2019-12-15 21:10:28 +01:00
Benjamin Otte
52e385672a filter: Add GtkStringFilter
Users provide a search filter and an expression that evaluates the items
to a string and then the filter goes and matches those strings to the
search term.
2019-12-15 21:10:28 +01:00
Benjamin Otte
ca2ebaf90f expression: Make property expression allow subexpressions 2019-12-15 21:10:28 +01:00
Benjamin Otte
46c042ed06 expression: Add GtkObjectExpression
Weak refs break cycles...
2019-12-15 21:10:27 +01:00
Benjamin Otte
e462a2bdcc Add GtkExpression
GtkExpressions allow looking up values from objects.

There are a few simple expressions, but the main one is the closure
expression that just calls a user-provided closure.
2019-12-15 21:10:27 +01:00
Benjamin Otte
bb59b48871 filterlistmodel: Rewrite to use GtkFilter 2019-12-15 21:10:27 +01:00
Benjamin Otte
da413fa72d tests: Remove testtreemodel test
testlistview does everything this test does.
2019-12-15 21:10:27 +01:00
Benjamin Otte
75556a3485 Add GtkCustomFilter 2019-12-15 21:10:27 +01:00
Benjamin Otte
53401e5d89 Add GtkFilter 2019-12-15 21:10:27 +01:00
Benjamin Otte
56838cab86 Add GtkDirectoryList
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-12-15 21:10:27 +01:00
Benjamin Otte
0a551b9b2a gdk: Add GDK_DECLARE_EXPORTED_TYPE()
This is like G_DECLARE_FINAL_TYPE(), except it doesn't make the class
struct public, so internally all the subclassing in the world can
happen.

Some subtle differences:
- It marks the get_type() function as G_GNUC_CONST
- It doesn't require the parent type
- It uses G_DEFINE_AUTOPTR_CLEANUP_FUNC() instead of the private glib
  chainup method.

Port a bunch of GDK and the whole event controller + gesture machinery
to provew that it works.

As a side effect, we gained a bunch of missing autocleanups.
2019-12-15 21:10:27 +01:00
255 changed files with 116532 additions and 4280 deletions

View File

@@ -169,6 +169,7 @@ constraint_view_init (ConstraintView *self)
GListModel *guides;
GListModel *children;
GListModel *constraints;
GtkFilter *filter;
manager = gtk_constraint_layout_new ();
gtk_widget_set_layout_manager (GTK_WIDGET (self), manager);
@@ -176,8 +177,12 @@ constraint_view_init (ConstraintView *self)
all_children = gtk_widget_observe_children (GTK_WIDGET (self));
all_constraints = gtk_constraint_layout_observe_constraints (GTK_CONSTRAINT_LAYOUT (manager));
guides = gtk_constraint_layout_observe_guides (GTK_CONSTRAINT_LAYOUT (manager));
constraints = (GListModel *)gtk_filter_list_model_new (all_constraints, omit_internal, NULL, NULL);
children = (GListModel *)gtk_filter_list_model_new (all_children, omit_internal, NULL, NULL);
filter = gtk_custom_filter_new (omit_internal, NULL, NULL);
constraints = (GListModel *)gtk_filter_list_model_new (all_constraints, filter);
g_object_unref (filter);
filter = gtk_custom_filter_new (omit_internal, NULL, NULL);
children = (GListModel *)gtk_filter_list_model_new (all_children, filter);
g_object_unref (filter);
list = g_list_store_new (G_TYPE_LIST_MODEL);
g_list_store_append (list, children);

View File

@@ -184,58 +184,6 @@ max_input (GtkSpinButton *spin_button,
return FALSE;
}
static gboolean
min_output (GtkSpinButton *spin_button)
{
GtkAdjustment *adjustment;
double value;
GtkWidget *box, *text;
adjustment = gtk_spin_button_get_adjustment (spin_button);
value = gtk_adjustment_get_value (adjustment);
box = gtk_widget_get_first_child (GTK_WIDGET (spin_button));
text = gtk_widget_get_first_child (box);
if (value == 0.0)
{
gtk_editable_set_text (GTK_EDITABLE (spin_button), "");
gtk_text_set_placeholder_text (GTK_TEXT (text), "unset");
return TRUE;
}
else
{
gtk_text_set_placeholder_text (GTK_TEXT (text), "");
return FALSE;
}
}
static gboolean
max_output (GtkSpinButton *spin_button)
{
GtkAdjustment *adjustment;
double value;
GtkWidget *box, *text;
adjustment = gtk_spin_button_get_adjustment (spin_button);
value = gtk_adjustment_get_value (adjustment);
box = gtk_widget_get_first_child (GTK_WIDGET (spin_button));
text = gtk_widget_get_first_child (box);
if (value == (double)G_MAXINT)
{
gtk_editable_set_text (GTK_EDITABLE (spin_button), "");
gtk_text_set_placeholder_text (GTK_TEXT (text), "unset");
return TRUE;
}
else
{
gtk_text_set_placeholder_text (GTK_TEXT (text), "");
return FALSE;
}
}
static void
guide_editor_constructed (GObject *object)
{
@@ -244,16 +192,12 @@ guide_editor_constructed (GObject *object)
guide_strength_combo (editor->strength);
g_signal_connect (editor->min_width, "input", G_CALLBACK (min_input), NULL);
g_signal_connect (editor->min_width, "output", G_CALLBACK (min_output), NULL);
g_signal_connect (editor->min_height, "input", G_CALLBACK (min_input), NULL);
g_signal_connect (editor->min_height, "output", G_CALLBACK (min_output), NULL);
g_signal_connect (editor->max_width, "input", G_CALLBACK (max_input), NULL);
g_signal_connect (editor->max_width, "output", G_CALLBACK (max_output), NULL);
g_signal_connect (editor->max_height, "input", G_CALLBACK (max_input), NULL);
g_signal_connect (editor->max_height, "output", G_CALLBACK (max_output), NULL);
if (editor->guide)
{

247
demos/gtk-demo/award.c Normal file
View File

@@ -0,0 +1,247 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "award.h"
struct _GtkAward
{
GObject parent;
char *explanation;
char *name;
char *title;
GDateTime *granted; /* or NULL if not granted */
};
enum {
PROP_0,
PROP_EXPLANATION,
PROP_NAME,
PROP_TITLE,
PROP_GRANTED,
N_PROPS,
};
static GParamSpec *properties[N_PROPS] = { NULL, };
G_DEFINE_TYPE (GtkAward, gtk_award, G_TYPE_OBJECT)
static void
gtk_award_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkAward *self = GTK_AWARD (object);
switch (prop_id)
{
case PROP_EXPLANATION:
self->explanation = g_value_dup_string (value);
break;
case PROP_NAME:
self->name = g_value_dup_string (value);
break;
case PROP_TITLE:
self->title = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_award_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkAward *self = GTK_AWARD (object);
switch (prop_id)
{
case PROP_EXPLANATION:
g_value_set_string (value, self->explanation);
break;
case PROP_NAME:
g_value_set_string (value, self->name);
break;
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
case PROP_GRANTED:
g_value_set_boxed (value, self->granted);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_award_dispose (GObject *object)
{
GtkAward *self = GTK_AWARD (object);
g_clear_pointer (&self->name, g_free);
g_clear_pointer (&self->title, g_free);
g_clear_pointer (&self->granted, g_date_time_unref);
G_OBJECT_CLASS (gtk_award_parent_class)->dispose (object);
}
static void
gtk_award_class_init (GtkAwardClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_award_set_property;
gobject_class->get_property = gtk_award_get_property;
gobject_class->dispose = gtk_award_dispose;
properties[PROP_EXPLANATION] =
g_param_spec_string ("explanation",
"Explanation",
"How to get the title",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_NAME] =
g_param_spec_string ("name",
"Name",
"internal name of the award",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_TITLE] =
g_param_spec_string ("title",
"Title",
"user-visible title",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_GRANTED] =
g_param_spec_boxed ("granted",
"Granted",
"Timestamp the award was granted or NULL if not granted yet",
G_TYPE_DATE_TIME,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_award_init (GtkAward *self)
{
}
GListModel *
gtk_award_get_list (void)
{
static GListModel *list = NULL;
if (list == NULL)
{
GtkBuilder *builder;
g_type_ensure (GTK_TYPE_AWARD);
builder = gtk_builder_new_from_resource ("/awards.ui");
list = G_LIST_MODEL (gtk_builder_get_object (builder, "list"));
g_object_ref (list);
g_object_unref (builder);
}
return g_object_ref (list);
}
const char *
gtk_award_get_name (GtkAward *award)
{
return award->name;
}
const char *
gtk_award_get_title (GtkAward *award)
{
return award->title;
}
GDateTime *
gtk_award_get_granted (GtkAward *award)
{
return award->granted;
}
GtkAward *
award_find (const char *name)
{
GListModel *list;
GtkAward *self;
guint i;
list = gtk_award_get_list ();
g_object_unref (list);
for (i = 0; i < g_list_model_get_n_items (list); i++)
{
self = g_list_model_get_item (list, i);
g_object_unref (self);
if (g_ascii_strcasecmp (name, self->name) == 0)
return self;
}
return NULL;
}
void
award (const char *name)
{
GtkAward *self;
GNotification *notification;
self = award_find (name);
if (self == NULL)
{
g_warning ("Did not find award \"%s\"", name);
return;
}
if (self->granted)
return;
self->granted = g_date_time_new_now_utc ();
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_GRANTED]);
notification = g_notification_new ("You won an award!");
g_notification_set_body (notification, self->title);
g_application_send_notification (g_application_get_default (), NULL, notification);
g_object_unref (notification);
}

18
demos/gtk-demo/award.h Normal file
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__ */

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="gtk40">
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="label" bind-source="GtkListItem" bind-property="position"></property>
<property name="margin">6</property>
</object>
</property>
</template>
</interface>

89
demos/gtk-demo/awards.ui Normal file
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>

View File

@@ -0,0 +1,49 @@
/* Awards
*
* This demo demonstrates how to use lists to show the awards you have collected
* while exploring this demo.
*
*/
#include <gtk/gtk.h>
/* Include the header for accessing the awards */
#include "award.h"
static GtkWidget *window = NULL;
GtkWidget *
do_awardview (GtkWidget *do_widget)
{
if (!window)
{
GtkWidget *sw, *listview;
GListModel *list;
window = gtk_window_new (GTK_WINDOW_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);
listview = gtk_list_view_new_with_factory (
gtk_builder_list_item_factory_new_from_resource (NULL, "/awardview/awardlistitem.ui"));
list = gtk_award_get_list ();
gtk_list_view_set_model (GTK_LIST_VIEW (listview), list);
g_object_unref (list);
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
gtk_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

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,22 @@
<file>gnome-fs-directory.png</file>
<file>gnome-fs-regular.png</file>
</gresource>
<gresource prefix="/listview_filebrowser">
<file>listview_filebrowser.ui</file>
</gresource>
<gresource prefix="/listview_minesweeper">
<file>listview_minesweeper.ui</file>
<file>listview_minesweeper_cell.ui</file>
</gresource>
<gresource prefix="/listview_settings">
<file>listview_settings.ui</file>
</gresource>
<gresource prefix="/listview_weather">
<file compressed="true">listview_weather.txt</file>
</gresource>
<gresource prefix="/listview_colors">
<file compressed="true">color.names.txt</file>
</gresource>
<gresource prefix="/shortcuts">
<file>shortcuts.ui</file>
<file>shortcuts-builder.ui</file>
@@ -147,6 +170,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 +213,13 @@
<file>infobar.c</file>
<file>links.c</file>
<file>listbox.c</file>
<file>listview_applauncher.c</file>
<file>listview_colors.c</file>
<file>listview_clocks.c</file>
<file>listview_filebrowser.c</file>
<file>listview_minesweeper.c</file>
<file>listview_settings.c</file>
<file>listview_weather.c</file>
<file>list_store.c</file>
<file>markup.c</file>
<file>menus.c</file>

View File

@@ -265,6 +265,14 @@ fishbowl_changes_toggled_cb (GtkToggleButton *button,
gtk_button_set_icon_name (GTK_BUTTON (button), "changes-allow");
}
char *
format_header_cb (GObject *object,
guint count,
double fps)
{
return g_strdup_printf ("%u Icons, %.2f fps", count, fps);
}
GtkWidget *
do_fishbowl (GtkWidget *do_widget)
{

View File

@@ -28,22 +28,12 @@
</child>
<child type="end">
<object class="GtkLabel">
<property name="label">fps</property>
</object>
</child>
<child type="end">
<object class="GtkLabel">
<property name="label" bind-source="bowl" bind-property="framerate-string"/>
</object>
</child>
<child type="end">
<object class="GtkLabel">
<property name="label">Icons, </property>
</object>
</child>
<child type="end">
<object class="GtkLabel">
<property name="label" bind-source="bowl" bind-property="count"/>
<binding name="label">
<closure type="gchararray" function="format_header_cb">
<lookup name="count">bowl</lookup>
<lookup name="framerate">bowl</lookup>
</closure>
</binding>
</object>
</child>
<child type="end">

View File

@@ -4,6 +4,8 @@
* as needed and support sorting and filtering.
*
* The children of a GtkFlowBox are regular widgets
*
* The dataset used here has 665 colors.
*/
#include <gtk/gtk.h>

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]:

View File

@@ -53,7 +53,6 @@ enum {
PROP_BENCHMARK,
PROP_COUNT,
PROP_FRAMERATE,
PROP_FRAMERATE_STRING,
PROP_UPDATE_DELAY,
NUM_PROPERTIES
};
@@ -289,14 +288,6 @@ gtk_fishbowl_get_property (GObject *object,
g_value_set_double (value, gtk_fishbowl_get_framerate (fishbowl));
break;
case PROP_FRAMERATE_STRING:
{
char *s = g_strdup_printf ("%.2f", gtk_fishbowl_get_framerate (fishbowl));
g_value_set_string (value, s);
g_free (s);
}
break;
case PROP_UPDATE_DELAY:
g_value_set_int64 (value, gtk_fishbowl_get_update_delay (fishbowl));
break;
@@ -350,13 +341,6 @@ gtk_fishbowl_class_init (GtkFishbowlClass *klass)
0,
G_PARAM_READABLE);
props[PROP_FRAMERATE_STRING] =
g_param_spec_string ("framerate-string",
"Framerate as string",
"Framerate as string, with 2 decimals",
NULL,
G_PARAM_READABLE);
props[PROP_UPDATE_DELAY] =
g_param_spec_int64 ("update-delay",
"Update delay",
@@ -508,7 +492,6 @@ gtk_fishbowl_do_update (GtkFishbowl *fishbowl)
priv->framerate = ((int)(priv->framerate * 100))/100.0;
g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_FRAMERATE]);
g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_FRAMERATE_STRING]);
if (!priv->benchmark)
return;

View File

@@ -12,7 +12,7 @@ static GtkWidget *window = NULL;
static GtkWidget *scrolledwindow;
static int selected;
#define N_WIDGET_TYPES 4
#define N_WIDGET_TYPES 6
static int hincrement = 5;
@@ -61,6 +61,7 @@ populate_icons (void)
gtk_grid_attach (GTK_GRID (grid), create_icon (), left, top, 1, 1);
hincrement = 0;
vincrement = 5;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_NEVER,
@@ -97,6 +98,7 @@ populate_text (gboolean hilight)
gtk_text_view_set_buffer (GTK_TEXT_VIEW (textview), buffer);
hincrement = 0;
vincrement = 5;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_NEVER,
@@ -121,6 +123,7 @@ populate_image (void)
gtk_picture_set_can_shrink (GTK_PICTURE (image), FALSE);
hincrement = 5;
vincrement = 5;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_AUTOMATIC,
@@ -128,6 +131,42 @@ populate_image (void)
gtk_container_add (GTK_CONTAINER (scrolledwindow), image);
}
extern GtkWidget *create_weather_view (void);
static void
populate_list (void)
{
GtkWidget *list;
list = create_weather_view ();
hincrement = 5;
vincrement = 0;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_container_add (GTK_CONTAINER (scrolledwindow), list);
}
extern GtkWidget *create_color_grid (void);
static void
populate_grid (void)
{
GtkWidget *list;
list = create_color_grid ();
hincrement = 0;
vincrement = 5;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_container_add (GTK_CONTAINER (scrolledwindow), list);
}
static void
set_widget_type (int type)
{
@@ -162,6 +201,16 @@ set_widget_type (int type)
populate_image ();
break;
case 4:
gtk_window_set_title (GTK_WINDOW (window), "Scrolling a list");
populate_list ();
break;
case 5:
gtk_window_set_title (GTK_WINDOW (window), "Scrolling a grid");
populate_grid ();
break;
default:
g_assert_not_reached ();
}

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);
}

View File

@@ -0,0 +1,200 @@
/* Lists/Application launcher
*
* This demo uses the GtkCoverFlow widget as a fancy application launcher.
*
* It is also a very small introduction to listviews.
*/
#include <gtk/gtk.h>
/* This is the function that creates the #GListModel that we need.
* GTK list widgets need a #GListModel to display, as models support change
* notifications.
* Unfortunately various older APIs do not provide list models, so we create
* our own.
*/
static GListModel *
create_application_list (void)
{
GListStore *store;
GList *apps, *l;
/* We use a #GListStore here, which is a simple array-like list implementation
* for manual management.
* List models need to know what type of data they provide, so we need to
* provide the type here. As we want to do a list of applications, #GAppInfo
* is the object we provide.
*/
store = g_list_store_new (G_TYPE_APP_INFO);
apps = g_app_info_get_all ();
for (l = apps; l; l = l->next)
{
g_list_store_append (store, l->data);
}
g_list_free_full (apps, g_object_unref);
return G_LIST_MODEL (store);
}
/* This is the function we use for setting up new listitems to display.
* We add just a #GtkImage here to display the application's icon as this is just
* a simple demo.
*/
static void
setup_listitem_cb (GtkListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *image;
image = gtk_image_new ();
gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
gtk_list_item_set_child (list_item, image);
}
/* Here we need to prepare the listitem for displaying its item. We get the
* listitem already set up from the previous function, so we can reuse the
* #GtkImage widget we set up above.
* We get the item - which we know is a #GAppInfo because it comes out of
* the model we set up above, grab its icon and display it.
*/
static void
bind_listitem_cb (GtkListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *image;
GAppInfo *app_info;
image = gtk_list_item_get_child (list_item);
app_info = gtk_list_item_get_item (list_item);
gtk_image_set_from_gicon (GTK_IMAGE (image), g_app_info_get_icon (app_info));
}
/* In more complex code, we would also need functions to unbind and teardown
* the listitem, but this is simple code, so the default implementations are
* enough. If we had connected signals, this step would have been necessary.
*
* The #GtkSignalListItemFactory documentation contains more information about
* this step.
*/
/* This function is called whenever an item in the list is activated. This is
* the simple way to allow reacting to the Enter key or double-clicking on a
* listitem.
* Of course, it is possible to use far more complex interactions by turning
* off activation and adding buttons or other widgets in the setup function
* above, but this is a simple demo, so we'll use the simple way.
*/
static void
activate_cb (GtkCoverFlow *coverflow,
guint position,
gpointer unused)
{
GAppInfo *app_info;
GdkAppLaunchContext *context;
GError *error = NULL;
app_info = g_list_model_get_item (gtk_cover_flow_get_model (coverflow), position);
/* Prepare the context for launching the application and launch it. This
* code is explained in detail in the documentation for #GdkAppLaunchContext
* and #GAppInfo.
*/
context = gdk_display_get_app_launch_context (gtk_widget_get_display (GTK_WIDGET (coverflow)));
if (!g_app_info_launch (app_info,
NULL,
G_APP_LAUNCH_CONTEXT (context),
&error))
{
GtkWidget *dialog;
/* And because error handling is important, even a simple demo has it:
* We display an error dialog that something went wrong.
*/
dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (coverflow))),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"Could not launch %s", g_app_info_get_display_name (app_info));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
g_clear_error (&error);
gtk_widget_show (dialog);
}
g_object_unref (context);
g_object_unref (app_info);
}
static GtkWidget *window = NULL;
GtkWidget *
do_listview_applauncher (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *coverflow, *sw;;
GListModel *model;
GtkListItemFactory *factory;
/* Create a window and set a few defaults */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), 640, 320);
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Application Launcher");
g_signal_connect (window, "destroy",
G_CALLBACK(gtk_widget_destroyed), &window);
/* The #GtkListitemFactory is what is used to create #GtkListItems
* to display the data from the model. So it is absolutely necessary
* to create one.
* We will use a #GtkSignalListItemFactory because it is the simplest
* one to use. Different ones are available for different use cases.
* The most powerful one is #GtkBuilderListItemFactory which uses
* #GtkBuilder .ui files, so it requires little code.
*/
factory = gtk_signal_list_item_factory_new ();
g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
g_signal_connect (factory, "bind", G_CALLBACK (bind_listitem_cb), NULL);
/* Create the list widget here: We use a coverflow widget because it's
* the coolest one. We could just as well use other list widgets such
* as a #GtkListView or a #GtkGridView and the code would look very
* similar.
*/
coverflow = gtk_cover_flow_new_with_factory (factory);
/* We connect the activate signal here. It's the function we defined
* above for launching the selected application.
*/
g_signal_connect (coverflow, "activate", G_CALLBACK (activate_cb), NULL);
/* And of course we need to set the data model. Here we call the function
* we wrote above that gives us the list of applications. Then we set
* it on the coverflow list widget.
* The coverflow will now take items from the model and use the factory
* to create as many listitems as it needs to show itself to the user.
*/
model = create_application_list ();
gtk_cover_flow_set_model (GTK_COVER_FLOW (coverflow), model);
g_object_unref (model);
/* List widgets should always be contained in a #GtkScrolledWindow,
* because otherwise they might get too large or they might not
* be scrollable.
*/
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (window), sw);
gtk_container_add (GTK_CONTAINER (sw), coverflow);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}

View File

@@ -0,0 +1,491 @@
/* Lists/Clocks
*
* This demo displays the time in different timezones.
*
* It is using a GtkGridView.
*
* The goal is to show how to set up expressions that track changes
* in objects and make them update widgets. For that, we create a
* GtkClock object that updates its time every second and then use
* various ways to display that time.
*
* Typically, this will be done using GtkBuilder .ui files with the
* help of the <binding> tag, but this demo shows the code that runs
* behind that.
*/
#include <gtk/gtk.h>
#define GTK_TYPE_CLOCK (gtk_clock_get_type ())
G_DECLARE_FINAL_TYPE (GtkClock, gtk_clock, GTK, CLOCK, GObject)
/* This is our object. It's just a timezone */
typedef struct _GtkClock GtkClock;
struct _GtkClock
{
GObject parent_instance;
/* We allow this to be NULL for the local timezone */
GTimeZone *timezone;
/* Name of the location we're displaying time for */
char *location;
};
enum {
PROP_0,
PROP_LOCATION,
PROP_TIME,
PROP_TIMEZONE,
N_PROPS
};
/* This function returns the current time in the clock's timezone.
* Note that this returns a new object every time, so we need to
* remember to unref it after use. */
static GDateTime *
gtk_clock_get_time (GtkClock *clock)
{
if (clock->timezone)
return g_date_time_new_now (clock->timezone);
else
return g_date_time_new_now_local ();
}
/* Here, we implement the functionality required by the GdkPaintable interface.
* This way we have a trivial way to display an analog clock.
* It also allows demonstrating how to directly use objects in the listview
* later by making this object do something interesting. */
static void
gtk_clock_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
GtkClock *self = GTK_CLOCK (paintable);
GDateTime *time;
GskRoundedRect outline;
#define BLACK ((GdkRGBA) { 0, 0, 0, 1 })
/* save/restore() is necessary so we can undo the transforms we start
* out with. */
gtk_snapshot_save (snapshot);
/* First, we move the (0, 0) point to the center of the area so
* we can draw everything relative to it. */
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (width / 2, height / 2));
/* Next we scale it, so that we can pretend that the clock is
* 100px in size. That way, we don't need to do any complicated
* math later.
* We use MIN() here so that we use the smaller dimension for sizing.
* That way we don't overdraw but keep the aspect ratio. */
gtk_snapshot_scale (snapshot, MIN (width, height) / 100.0, MIN (width, height) / 100.0);
/* Now we have a circle with diameter 100px (and radius 50px) that
* has its (0, 0) point at the center.
* Let's draw a simple clock into it. */
time = gtk_clock_get_time (self);
/* First, draw a circle. This is a neat little trick to draw a circle
* without requiring Cairo. */
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-50, -50, 100, 100), 50);
gtk_snapshot_append_border (snapshot,
&outline,
(float[4]) { 4, 4, 4, 4 },
(GdkRGBA [4]) { BLACK, BLACK, BLACK, BLACK });
/* Next, draw the hour hand.
* We do this using tranforms again: Instead of computing where the angle points
* to, we just rotate everything and then draw the hand as if if was :00.
* We don't even need to care about am/pm here because rotations just work. */
gtk_snapshot_save (snapshot);
gtk_snapshot_rotate (snapshot, 30 * g_date_time_get_hour (time) + 0.5 * g_date_time_get_minute (time));
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-2, -23, 4, 25), 2);
gtk_snapshot_push_rounded_clip (snapshot, &outline);
gtk_snapshot_append_color (snapshot, &BLACK, &outline.bounds);
gtk_snapshot_pop (snapshot);
gtk_snapshot_restore (snapshot);
/* And the same as above for the minute hand. Just make this one longer
* so people can tell the hands apart. */
gtk_snapshot_save (snapshot);
gtk_snapshot_rotate (snapshot, 6 * g_date_time_get_minute (time));
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-2, -43, 4, 45), 2);
gtk_snapshot_push_rounded_clip (snapshot, &outline);
gtk_snapshot_append_color (snapshot, &BLACK, &outline.bounds);
gtk_snapshot_pop (snapshot);
gtk_snapshot_restore (snapshot);
/* and finally, the second indicator. */
gtk_snapshot_save (snapshot);
gtk_snapshot_rotate (snapshot, 6 * g_date_time_get_second (time));
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-2, -43, 4, 10), 2);
gtk_snapshot_push_rounded_clip (snapshot, &outline);
gtk_snapshot_append_color (snapshot, &BLACK, &outline.bounds);
gtk_snapshot_pop (snapshot);
gtk_snapshot_restore (snapshot);
/* And finally, don't forget to restore the initial save() that we did for
* the initial transformations. */
gtk_snapshot_restore (snapshot);
g_date_time_unref (time);
}
/* Our desired size is 100px. That sounds okay for an analog clock */
static int
gtk_clock_get_intrinsic_width (GdkPaintable *paintable)
{
return 100;
}
static int
gtk_clock_get_intrinsic_height (GdkPaintable *paintable)
{
return 100;
}
/* Initialize the paintable interface. This way we turn our clock objects
* into objects that can be drawn.
* There are more functions to this interface to define desired size,
* but this is enough.
*/
static void
gtk_clock_paintable_init (GdkPaintableInterface *iface)
{
iface->snapshot = gtk_clock_snapshot;
iface->get_intrinsic_width = gtk_clock_get_intrinsic_width;
iface->get_intrinsic_height = gtk_clock_get_intrinsic_height;
}
/* Finally, we define the type. The important part is adding the paintable
* interface, so GTK knows that this object can indeed be drawm.
*/
G_DEFINE_TYPE_WITH_CODE (GtkClock, gtk_clock, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_clock_paintable_init))
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_clock_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkClock *self = GTK_CLOCK (object);
switch (property_id)
{
case PROP_LOCATION:
g_value_set_string (value, self->location);
break;
case PROP_TIME:
g_value_take_boxed (value, gtk_clock_get_time (self));
break;
case PROP_TIMEZONE:
g_value_set_boxed (value, self->timezone);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_clock_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkClock *self = GTK_CLOCK (object);
switch (property_id)
{
case PROP_LOCATION:
self->location = g_value_dup_string (value);
break;
case PROP_TIMEZONE:
self->timezone = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/* This is the list of all the ticking clocks */
static GSList *ticking_clocks = NULL;
/* This is the id of the timeout source that is updating all ticking clocks */
static guint ticking_clock_id = 0;
/* Every second, this function is called to tell everybody that the
* clocks are ticking.
*/
static gboolean
gtk_clock_tick (gpointer unused)
{
GSList *l;
for (l = ticking_clocks; l; l = l->next)
{
GtkClock *clock = l->data;
/* We will now return a different value for the time porperty,
* so notify about that.
*/
g_object_notify_by_pspec (G_OBJECT (clock), properties[PROP_TIME]);
/* We will also draw the hands of the clock differently.
* So notify about that, too.
*/
gdk_paintable_invalidate_contents (GDK_PAINTABLE (clock));
}
return G_SOURCE_CONTINUE;
}
static void
gtk_clock_stop_ticking (GtkClock *self)
{
ticking_clocks = g_slist_remove (ticking_clocks, self);
/* If no clock is remaining, stop running the tick updates */
if (ticking_clocks == NULL && ticking_clock_id != 0)
g_clear_handle_id (&ticking_clock_id, g_source_remove);
}
static void
gtk_clock_start_ticking (GtkClock *self)
{
/* if no clock is ticking yet, start */
if (ticking_clock_id == 0)
ticking_clock_id = g_timeout_add_seconds (1, gtk_clock_tick, NULL);
ticking_clocks = g_slist_prepend (ticking_clocks, self);
}
static void
gtk_clock_finalize (GObject *object)
{
GtkClock *self = GTK_CLOCK (object);
gtk_clock_stop_ticking (self);
g_free (self->location);
g_clear_pointer (&self->timezone, g_time_zone_unref);
G_OBJECT_CLASS (gtk_clock_parent_class)->finalize (object);
}
static void
gtk_clock_class_init (GtkClockClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = gtk_clock_get_property;
gobject_class->set_property = gtk_clock_set_property;
gobject_class->finalize = gtk_clock_finalize;
properties[PROP_LOCATION] =
g_param_spec_string ("location", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
properties[PROP_TIME] =
g_param_spec_boxed ("time", NULL, NULL, G_TYPE_DATE_TIME, G_PARAM_READABLE);
properties[PROP_TIMEZONE] =
g_param_spec_boxed ("timezone", NULL, NULL, G_TYPE_TIME_ZONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_clock_init (GtkClock *self)
{
gtk_clock_start_ticking (self);
}
static GtkClock *
gtk_clock_new (const char *location,
GTimeZone *timezone)
{
GtkClock *result;
result = g_object_new (GTK_TYPE_CLOCK,
"location", location,
"timezone", timezone,
NULL);
g_clear_pointer (&timezone, g_time_zone_unref);
return result;
}
static GListModel *
create_clocks_model (void)
{
GListStore *result;
GtkClock *clock;
result = g_list_store_new (GTK_TYPE_CLOCK);
/* local time */
clock = gtk_clock_new ("local", NULL);
g_list_store_append (result, clock);
g_object_unref (clock);
/* UTC time */
clock = gtk_clock_new ("UTC", g_time_zone_new_utc ());
g_list_store_append (result, clock);
g_object_unref (clock);
/* A bunch of timezones with GTK hackers */
clock = gtk_clock_new ("San Francisco", g_time_zone_new ("America/Los_Angeles"));
g_list_store_append (result, clock);
g_object_unref (clock);
clock = gtk_clock_new ("Boston", g_time_zone_new ("America/New_York"));
g_list_store_append (result, clock);
g_object_unref (clock);
clock = gtk_clock_new ("London", g_time_zone_new ("Europe/London"));
g_list_store_append (result, clock);
g_object_unref (clock);
clock = gtk_clock_new ("Berlin", g_time_zone_new ("Europe/Berlin"));
g_list_store_append (result, clock);
g_object_unref (clock);
clock = gtk_clock_new ("Moscow", g_time_zone_new ("Europe/Moscow"));
g_list_store_append (result, clock);
g_object_unref (clock);
clock = gtk_clock_new ("New Delhi", g_time_zone_new ("Asia/Kolkata"));
g_list_store_append (result, clock);
g_object_unref (clock);
clock = gtk_clock_new ("Shanghai", g_time_zone_new ("Asia/Shanghai"));
g_list_store_append (result, clock);
g_object_unref (clock);
return G_LIST_MODEL (result);
}
static char *
convert_time_to_string (GObject *image,
GDateTime *time,
gpointer unused)
{
return g_date_time_format (time, "%x\n%X");
}
/* And this function is the crux for this whole demo.
* It shows how to use expressions to set up bindings.
*/
static void
setup_listitem_cb (GtkListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *box, *picture, *location_label, *time_label;
GtkExpression *clock_expression, *expression;
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_list_item_set_child (list_item, box);
/* First, we create an expression that gets us the clock from the listitem:
* 1. Create an expression that gets the list item.
* 2. Use that expression's "item" property to get the clock
*/
expression = gtk_constant_expression_new (GTK_TYPE_LIST_ITEM, list_item);
clock_expression = gtk_property_expression_new (GTK_TYPE_LIST_ITEM, expression, "item");
/* Bind the clock's location to a label.
* This is easy: We just get the "location" property of the clock.
*/
expression = gtk_property_expression_new (GTK_TYPE_CLOCK,
gtk_expression_ref (clock_expression),
"location");
/* Now create the label and bind the expression to it. */
location_label = gtk_label_new (NULL);
gtk_expression_bind (expression, location_label, "label", location_label);
gtk_container_add (GTK_CONTAINER (box), location_label);
/* Here we bind the item itself to a GdkPicture.
* This is simply done by using the clock expression itself.
*/
expression = gtk_expression_ref (clock_expression);
/* Now create the widget and bind the expression to it. */
picture = gtk_picture_new ();
gtk_expression_bind (expression, picture, "paintable", picture);
gtk_container_add (GTK_CONTAINER (box), picture);
/* And finally, everything comes together.
* We create a label for displaying the time as text.
* For that, we need to transform the "GDateTime" of the
* time property into a string so that the label can display it.
*/
expression = gtk_property_expression_new (GTK_TYPE_CLOCK,
gtk_expression_ref (clock_expression),
"time");
expression = gtk_cclosure_expression_new (G_TYPE_STRING,
NULL,
1, (GtkExpression *[1]) { expression },
G_CALLBACK (convert_time_to_string),
NULL, NULL);
/* Now create the label and bind the expression to it. */
time_label = gtk_label_new (NULL);
gtk_expression_bind (expression, time_label, "label", time_label);
gtk_container_add (GTK_CONTAINER (box), time_label);
gtk_expression_unref (clock_expression);
}
static GtkWidget *window = NULL;
GtkWidget *
do_listview_clocks (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *gridview, *sw;
GtkListItemFactory *factory;
GListModel *model;
GtkNoSelection *selection;
/* This is the normal window setup code every demo does */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Clocks");
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
/* List widgets go into a scrolled window. Always. */
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (window), sw);
/* Create the factory that creates the listitems. Because we
* used bindings above during setup, we only need to connect
* to the setup signal.
* The bindings take care of the bind step.
*/
factory = gtk_signal_list_item_factory_new ();
g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
gridview = gtk_grid_view_new_with_factory (factory);
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
model = create_clocks_model ();
selection = gtk_no_selection_new (model);
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), G_LIST_MODEL (selection));
gtk_container_add (GTK_CONTAINER (sw), gridview);
g_object_unref (selection);
g_object_unref (model);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}

View File

@@ -0,0 +1,593 @@
/* Lists/Colors
*
* This demo displays a named colors.
*
* It is using a GtkGridView, and shows
* how to sort the data in various ways.
*
* The dataset used here has 9283 items.
*/
#include <gtk/gtk.h>
#define GTK_TYPE_COLOR (gtk_color_get_type ())
G_DECLARE_FINAL_TYPE (GtkColor, gtk_color, GTK, COLOR, GObject)
/* This is our object. It's just a color */
typedef struct _GtkColor GtkColor;
struct _GtkColor
{
GObject parent_instance;
char *name;
GdkRGBA *color;
int h, s, v;
};
enum {
PROP_0,
PROP_NAME,
PROP_COLOR,
PROP_RED,
PROP_GREEN,
PROP_BLUE,
PROP_HUE,
PROP_SATURATION,
PROP_VALUE,
N_PROPS
};
static void
gtk_color_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
GtkColor *self = GTK_COLOR (paintable);
gtk_snapshot_append_color (snapshot, self->color, &GRAPHENE_RECT_INIT (0, 0, width, height));
}
static int
gtk_color_get_intrinsic_width (GdkPaintable *paintable)
{
return 32;
}
static int
gtk_color_get_intrinsic_height (GdkPaintable *paintable)
{
return 32;
}
static void
gtk_color_paintable_init (GdkPaintableInterface *iface)
{
iface->snapshot = gtk_color_snapshot;
iface->get_intrinsic_width = gtk_color_get_intrinsic_width;
iface->get_intrinsic_height = gtk_color_get_intrinsic_height;
}
/*
* Finally, we define the type. The important part is adding the paintable
* interface, so GTK knows that this object can indeed be drawm.
*/
G_DEFINE_TYPE_WITH_CODE (GtkColor, gtk_color, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_color_paintable_init))
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_color_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkColor *self = GTK_COLOR (object);
switch (property_id)
{
case PROP_NAME:
g_value_set_string (value, self->name);
break;
case PROP_COLOR:
g_value_set_boxed (value, self->color);
break;
case PROP_RED:
g_value_set_float (value, self->color->red);
break;
case PROP_GREEN:
g_value_set_float (value, self->color->green);
break;
case PROP_BLUE:
g_value_set_float (value, self->color->blue);
break;
case PROP_HUE:
g_value_set_int (value, self->h);
break;
case PROP_SATURATION:
g_value_set_int (value, self->s);
break;
case PROP_VALUE:
g_value_set_int (value, self->v);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_color_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkColor *self = GTK_COLOR (object);
switch (property_id)
{
case PROP_NAME:
self->name = g_value_dup_string (value);
break;
case PROP_COLOR:
self->color = g_value_dup_boxed (value);
break;
case PROP_HUE:
self->h = g_value_get_int (value);
break;
case PROP_SATURATION:
self->s = g_value_get_int (value);
break;
case PROP_VALUE:
self->v = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_color_finalize (GObject *object)
{
GtkColor *self = GTK_COLOR (object);
g_free (self->name);
g_clear_pointer (&self->color, gdk_rgba_free);
G_OBJECT_CLASS (gtk_color_parent_class)->finalize (object);
}
static void
gtk_color_class_init (GtkColorClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = gtk_color_get_property;
gobject_class->set_property = gtk_color_set_property;
gobject_class->finalize = gtk_color_finalize;
properties[PROP_NAME] =
g_param_spec_string ("name", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
properties[PROP_COLOR] =
g_param_spec_boxed ("color", NULL, NULL, GDK_TYPE_RGBA, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
properties[PROP_RED] =
g_param_spec_float ("red", NULL, NULL, 0, 1, 0, G_PARAM_READABLE);
properties[PROP_GREEN] =
g_param_spec_float ("green", NULL, NULL, 0, 1, 0, G_PARAM_READABLE);
properties[PROP_BLUE] =
g_param_spec_float ("blue", NULL, NULL, 0, 1, 0, G_PARAM_READABLE);
properties[PROP_HUE] =
g_param_spec_int ("hue", NULL, NULL, 0, 360, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
properties[PROP_SATURATION] =
g_param_spec_int ("saturation", NULL, NULL, 0, 100, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
properties[PROP_VALUE] =
g_param_spec_int ("value", NULL, NULL, 0, 100, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_color_init (GtkColor *self)
{
}
static GtkColor *
gtk_color_new (const char *name,
float r, float g, float b,
int h, int s, int v)
{
GtkColor *result;
GdkRGBA color = { r, g, b, 1.0 };
result = g_object_new (GTK_TYPE_COLOR,
"name", name,
"color", &color,
"hue", h,
"saturation", s,
"value", v,
NULL);
return result;
}
static GListModel *
create_colors_model (void)
{
GListStore *result;
GtkColor *color;
GBytes *data;
char **lines;
guint i;
result = g_list_store_new (GTK_TYPE_COLOR);
data = g_resources_lookup_data ("/listview_colors/color.names.txt", 0, NULL);
lines = g_strsplit (g_bytes_get_data (data, NULL), "\n", 0);
for (i = 0; lines[i]; i++)
{
const char *name;
char **fields;
int red, green, blue;
int h, s, v;
if (lines[i][0] == '#' || lines[i][0] == '\0')
continue;
fields = g_strsplit (lines[i], " ", 0);
name = fields[1];
red = atoi (fields[3]);
green = atoi (fields[4]);
blue = atoi (fields[5]);
h = atoi (fields[9]);
s = atoi (fields[10]);
v = atoi (fields[11]);
color = gtk_color_new (name, red / 255., green / 255., blue / 255., h, s, v);
g_list_store_append (result, color);
g_object_unref (color);
g_strfreev (fields);
}
g_strfreev (lines);
g_bytes_unref (data);
return G_LIST_MODEL (result);
}
static char *
get_rgb_markup (gpointer this,
GtkColor *color)
{
if (!color)
return NULL;
return g_strdup_printf ("<b>R:</b> %d <b>G:</b> %d <b>B:</b> %d",
(int)(color->color->red * 255),
(int)(color->color->green * 255),
(int)(color->color->blue * 255));
}
static char *
get_hsv_markup (gpointer this,
GtkColor *color)
{
if (!color)
return NULL;
return g_strdup_printf ("<b>H:</b> %d <b>S:</b> %d <b>V:</b> %d",
color->h,
color->s,
color->v);
}
static void
setup_simple_listitem_cb (GtkListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *picture;
GtkExpression *color_expression, *expression;
expression = gtk_constant_expression_new (GTK_TYPE_LIST_ITEM, list_item);
color_expression = gtk_property_expression_new (GTK_TYPE_LIST_ITEM, expression, "item");
picture = gtk_picture_new ();
gtk_widget_set_size_request (picture, 32, 32);
gtk_expression_bind (color_expression, picture, "paintable", NULL);
gtk_list_item_set_child (list_item, picture);
}
static void
setup_listitem_cb (GtkListItemFactory *factory,
GtkListItem *list_item)
{
GtkWidget *box, *picture, *name_label, *rgb_label, *hsv_label;;
GtkExpression *color_expression, *expression;
GtkExpression *params[1];
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_list_item_set_child (list_item, box);
expression = gtk_constant_expression_new (GTK_TYPE_LIST_ITEM, list_item);
color_expression = gtk_property_expression_new (GTK_TYPE_LIST_ITEM, expression, "item");
expression = gtk_property_expression_new (GTK_TYPE_COLOR,
gtk_expression_ref (color_expression),
"name");
name_label = gtk_label_new (NULL);
gtk_expression_bind (expression, name_label, "label", NULL);
gtk_container_add (GTK_CONTAINER (box), name_label);
expression = gtk_expression_ref (color_expression);
picture = gtk_picture_new ();
gtk_expression_bind (expression, picture, "paintable", NULL);
gtk_container_add (GTK_CONTAINER (box), picture);
params[0] = gtk_expression_ref (color_expression);
expression = gtk_cclosure_expression_new (G_TYPE_STRING,
NULL,
1, params,
(GCallback)get_rgb_markup,
NULL, NULL);
rgb_label = gtk_label_new (NULL);
gtk_label_set_use_markup (GTK_LABEL (rgb_label), TRUE);
gtk_expression_bind (expression, rgb_label, "label", NULL);
gtk_container_add (GTK_CONTAINER (box), rgb_label);
params[0] = gtk_expression_ref (color_expression);
expression = gtk_cclosure_expression_new (G_TYPE_STRING,
NULL,
1, params,
(GCallback)get_hsv_markup,
NULL, NULL);
hsv_label = gtk_label_new (NULL);
gtk_label_set_use_markup (GTK_LABEL (hsv_label), TRUE);
gtk_expression_bind (expression, hsv_label, "label", NULL);
gtk_container_add (GTK_CONTAINER (box), hsv_label);
gtk_expression_unref (color_expression);
}
static void
set_title (gpointer item,
const char *title)
{
g_object_set_data (G_OBJECT (item), "title", (gpointer)title);
}
static char *
get_title (gpointer item)
{
return g_strdup ((char *)g_object_get_data (G_OBJECT (item), "title"));
}
static gboolean
set_item (GBinding *binding,
const GValue *from,
GValue *to,
gpointer data)
{
GObject *source = g_binding_get_source (binding);
GListModel *model;
guint selected;
gpointer item;
selected = g_value_get_uint (from);
model = gtk_drop_down_get_model (GTK_DROP_DOWN (source));
item = g_list_model_get_item (model, selected);
g_value_set_object (to, item);
g_clear_object (&item);
return TRUE;
}
GtkWidget *
create_color_grid (void)
{
GtkWidget *gridview;
GtkListItemFactory *factory;
GListModel *model, *selection;
gridview = gtk_grid_view_new ();
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
factory = gtk_signal_list_item_factory_new ();
g_signal_connect (factory, "setup", G_CALLBACK (setup_simple_listitem_cb), NULL);
gtk_grid_view_set_factory (GTK_GRID_VIEW (gridview), factory);
g_object_unref (factory);
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), 24);
gtk_grid_view_set_enable_rubberband (GTK_GRID_VIEW (gridview), TRUE);
model = G_LIST_MODEL (gtk_sort_list_model_new (create_colors_model (), NULL));
selection = G_LIST_MODEL (gtk_multi_selection_new (model));
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), selection);
g_object_unref (selection);
g_object_unref (model);
return gridview;
}
static GtkWidget *window = NULL;
GtkWidget *
do_listview_colors (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *header, *gridview, *sw, *box, *dropdown;
GtkListItemFactory *factory;
GListStore *factories;
GListModel *model;
GtkSorter *sorter;
GtkSorter *multi_sorter;
GListStore *sorters;
GtkExpression *expression;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Colors");
header = gtk_header_bar_new ();
gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), TRUE);
gtk_window_set_titlebar (GTK_WINDOW (window), header);
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
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);
gridview = create_color_grid ();
gtk_container_add (GTK_CONTAINER (sw), gridview);
model = gtk_grid_view_get_model (GTK_GRID_VIEW (gridview));
g_object_get (model, "model", &model, NULL);
sorters = g_list_store_new (GTK_TYPE_SORTER);
sorter = gtk_string_sorter_new (gtk_property_expression_new (GTK_TYPE_COLOR, NULL, "name"));
set_title (sorter, "Name");
g_list_store_append (sorters, sorter);
g_object_unref (sorter);
multi_sorter = gtk_multi_sorter_new ();
sorter = gtk_numeric_sorter_new (gtk_property_expression_new (GTK_TYPE_COLOR, NULL, "red"));
gtk_numeric_sorter_set_sort_order (GTK_NUMERIC_SORTER (sorter), GTK_SORT_DESCENDING);
set_title (sorter, "Red");
g_list_store_append (sorters, sorter);
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), sorter);
sorter = gtk_numeric_sorter_new (gtk_property_expression_new (GTK_TYPE_COLOR, NULL, "green"));
gtk_numeric_sorter_set_sort_order (GTK_NUMERIC_SORTER (sorter), GTK_SORT_DESCENDING);
set_title (sorter, "Green");
g_list_store_append (sorters, sorter);
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), sorter);
sorter = gtk_numeric_sorter_new (gtk_property_expression_new (GTK_TYPE_COLOR, NULL, "blue"));
gtk_numeric_sorter_set_sort_order (GTK_NUMERIC_SORTER (sorter), GTK_SORT_DESCENDING);
set_title (sorter, "Blue");
g_list_store_append (sorters, sorter);
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), sorter);
set_title (multi_sorter, "RGB");
g_list_store_append (sorters, multi_sorter);
g_object_unref (multi_sorter);
multi_sorter = gtk_multi_sorter_new ();
sorter = gtk_numeric_sorter_new (gtk_property_expression_new (GTK_TYPE_COLOR, NULL, "hue"));
gtk_numeric_sorter_set_sort_order (GTK_NUMERIC_SORTER (sorter), GTK_SORT_DESCENDING);
set_title (sorter, "Hue");
g_list_store_append (sorters, sorter);
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), sorter);
sorter = gtk_numeric_sorter_new (gtk_property_expression_new (GTK_TYPE_COLOR, NULL, "saturation"));
gtk_numeric_sorter_set_sort_order (GTK_NUMERIC_SORTER (sorter), GTK_SORT_DESCENDING);
set_title (sorter, "Saturation");
g_list_store_append (sorters, sorter);
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), sorter);
sorter = gtk_numeric_sorter_new (gtk_property_expression_new (GTK_TYPE_COLOR, NULL, "value"));
gtk_numeric_sorter_set_sort_order (GTK_NUMERIC_SORTER (sorter), GTK_SORT_DESCENDING);
set_title (sorter, "Value");
g_list_store_append (sorters, sorter);
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), sorter);
set_title (multi_sorter, "HSV");
g_list_store_append (sorters, multi_sorter);
g_object_unref (multi_sorter);
dropdown = gtk_drop_down_new ();
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
gtk_container_add (GTK_CONTAINER (box), gtk_label_new ("Sort by:"));
gtk_container_add (GTK_CONTAINER (box), dropdown);
gtk_header_bar_pack_end (GTK_HEADER_BAR (header), box);
expression = gtk_cclosure_expression_new (G_TYPE_STRING,
NULL,
0, NULL,
(GCallback)get_title,
NULL, NULL);
gtk_drop_down_set_expression (GTK_DROP_DOWN (dropdown), expression);
gtk_expression_unref (expression);
gtk_drop_down_set_model (GTK_DROP_DOWN (dropdown), G_LIST_MODEL (sorters));
g_object_unref (sorters);
g_object_bind_property_full (dropdown, "selected",
model, "sorter",
G_BINDING_SYNC_CREATE,
set_item, NULL,
NULL, NULL);
factories = g_list_store_new (GTK_TYPE_LIST_ITEM_FACTORY);
factory = gtk_signal_list_item_factory_new ();
g_signal_connect (factory, "setup", G_CALLBACK (setup_simple_listitem_cb), NULL);
set_title (factory, "Colors");
g_list_store_append (factories, factory);
factory = gtk_signal_list_item_factory_new ();
g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
set_title (factory, "Everything");
g_list_store_append (factories, factory);
dropdown = gtk_drop_down_new ();
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
gtk_container_add (GTK_CONTAINER (box), gtk_label_new ("Show:"));
gtk_container_add (GTK_CONTAINER (box), dropdown);
gtk_header_bar_pack_end (GTK_HEADER_BAR (header), box);
expression = gtk_cclosure_expression_new (G_TYPE_STRING,
NULL,
0, NULL,
(GCallback)get_title,
NULL, NULL);
gtk_drop_down_set_expression (GTK_DROP_DOWN (dropdown), expression);
gtk_expression_unref (expression);
gtk_drop_down_set_model (GTK_DROP_DOWN (dropdown), G_LIST_MODEL (factories));
g_object_unref (factories);
g_object_bind_property_full (dropdown, "selected",
gridview, "factory",
G_BINDING_SYNC_CREATE,
set_item, NULL,
NULL, NULL);
g_object_unref (model);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}

View File

@@ -0,0 +1,256 @@
/* Lists/File browser
*
* This demo shows off the different layouts that are quickly achievable
* with GtkListview and GtkGridView by implementing a file browser with
* different views.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
static GtkWidget *window = NULL;
/* Create a simple object that holds the data for the different views */
typedef struct _FileBrowserView FileBrowserView;
struct _FileBrowserView
{
GObject parent_instance;
GtkListItemFactory *factory;
char *icon_name;
GtkOrientation orientation;
};
enum {
PROP_0,
PROP_FACTORY,
PROP_ICON_NAME,
PROP_ORIENTATION,
N_PROPS
};
#define FILE_BROWSER_TYPE_VIEW (file_browser_view_get_type ())
G_DECLARE_FINAL_TYPE (FileBrowserView, file_browser_view, FILE_BROWSER, VIEW, GObject);
G_DEFINE_TYPE (FileBrowserView, file_browser_view, G_TYPE_OBJECT);
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
file_browser_view_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
FileBrowserView *self = FILE_BROWSER_VIEW (object);
switch (property_id)
{
case PROP_FACTORY:
g_value_set_object (value, self->factory);
break;
case PROP_ICON_NAME:
g_value_set_string (value, self->icon_name);
break;
case PROP_ORIENTATION:
g_value_set_enum (value, self->orientation);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
file_browser_view_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
FileBrowserView *self = FILE_BROWSER_VIEW (object);
switch (prop_id)
{
case PROP_FACTORY:
g_set_object (&self->factory, g_value_get_object (value));
break;
case PROP_ICON_NAME:
g_free (self->icon_name);
self->icon_name = g_value_dup_string (value);
break;
case PROP_ORIENTATION:
self->orientation = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
file_browser_view_finalize (GObject *object)
{
FileBrowserView *self = FILE_BROWSER_VIEW (object);
g_object_unref (self->factory);
g_free (self->icon_name);
G_OBJECT_CLASS (file_browser_view_parent_class)->dispose (object);
}
static void
file_browser_view_class_init (FileBrowserViewClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = file_browser_view_get_property;
gobject_class->set_property = file_browser_view_set_property;
gobject_class->finalize = file_browser_view_finalize;
properties[PROP_FACTORY] =
g_param_spec_object ("factory",
"factory",
"factory to use in the main view",
GTK_TYPE_LIST_ITEM_FACTORY,
G_PARAM_READWRITE);
properties[PROP_ICON_NAME] =
g_param_spec_string ("icon-name",
"icon name",
"icon to display for selecting this view",
NULL,
G_PARAM_READWRITE);
properties[PROP_ORIENTATION] =
g_param_spec_enum ("orientation",
"orientation",
"orientation of the view",
GTK_TYPE_ORIENTATION,
GTK_ORIENTATION_VERTICAL,
G_PARAM_READWRITE);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void file_browser_view_init (FileBrowserView *self)
{
}
char *
filebrowser_get_display_name (GObject *object,
GFileInfo *info)
{
if (!info)
return NULL;
return g_strdup (g_file_info_get_attribute_string (info, "standard::display-name"));
}
char *
filebrowser_get_content_type (GObject *object,
GFileInfo *info)
{
if (!info)
return NULL;
return g_strdup (g_file_info_get_attribute_string (info, "standard::content-type"));
}
char *
filebrowser_get_size (GObject *object,
GFileInfo *info)
{
if (!info)
return NULL;
return g_format_size (g_file_info_get_attribute_uint64 (info, "standard::size"));
}
GIcon *
filebrowser_get_icon (GObject *object,
GFileInfo *info)
{
GIcon *icon;
if (info)
icon = G_ICON (g_file_info_get_attribute_object (info, "standard::icon"));
else
icon = NULL;
if (icon)
g_object_ref (icon);
return icon;
}
void
filebrowser_up_clicked_cb (GtkButton *button,
GtkDirectoryList *list)
{
GFile *file;
file = g_file_get_parent (gtk_directory_list_get_file (list));
if (file == NULL)
return;
gtk_directory_list_set_file (list, file);
}
void
filebrowser_view_activated_cb (GtkGridView *view,
guint pos,
GtkDirectoryList *list)
{
GFileInfo *info;
info = g_list_model_get_item (gtk_grid_view_get_model (view), pos);
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
gtk_directory_list_set_file (list, G_FILE (g_file_info_get_attribute_object (info, "standard::file")));
g_object_unref (info);
}
GtkWidget *
do_listview_filebrowser (GtkWidget *do_widget)
{
if (!window)
{
GtkWidget *view;
GtkBuilder *builder;
GtkDirectoryList *dirlist;
GFile *file;
char *cwd;
builder = gtk_builder_new_from_resource ("/listview_filebrowser/listview_filebrowser.ui");
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
/* Create the model and fill it with the contents of the current directory */
cwd = g_get_current_dir ();
file = g_file_new_for_path (cwd);
g_free (cwd);
dirlist = GTK_DIRECTORY_LIST (gtk_builder_get_object (builder, "dirlist"));
gtk_directory_list_set_file (dirlist, file);
g_object_unref (file);
/* grab focus in the view */
view = GTK_WIDGET (gtk_builder_get_object (builder, "view"));
gtk_widget_grab_focus (view);
g_object_unref (builder);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}

View File

@@ -0,0 +1,240 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GListStore" id="viewlist">
<property name="item-type">FileBrowserView</property>
<child>
<object class="FileBrowserView">
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkBox">
<child>
<object class="GtkImage">
<binding name="gicon">
<closure type="GIcon" function="filebrowser_get_icon">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<binding name="label">
<closure type="gchararray" function="filebrowser_get_display_name">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
<property name="icon-name">view-list-symbolic</property>
<property name="orientation">horizontal</property>
</object>
</child>
<child>
<object class="FileBrowserView">
<property name="icon-name">view-grid-symbolic</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkImage">
<property name="icon-size">large</property>
<binding name="gicon">
<closure type="GIcon" function="filebrowser_get_icon">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="wrap">1</property>
<property name="wrap-mode">word-char</property>
<property name="lines">2</property>
<property name="ellipsize">end</property>
<property name="width-chars">10</property>
<property name="max-width-chars">30</property>
<binding name="label">
<closure type="gchararray" function="filebrowser_get_display_name">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
<property name="orientation">vertical</property>
</object>
</child>
<child>
<object class="FileBrowserView">
<property name="icon-name">view-paged-symbolic</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkBox">
<child>
<object class="GtkImage">
<property name="icon-size">large</property>
<binding name="gicon">
<closure type="GIcon" function="filebrowser_get_icon">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<binding name="label">
<closure type="gchararray" function="filebrowser_get_display_name">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<binding name="label">
<closure type="gchararray" function="filebrowser_get_size">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<binding name="label">
<closure type="gchararray" function="filebrowser_get_content_type">
<lookup name="item">GtkListItem</lookup>
</closure>
</binding>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
</child>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
<property name="orientation">horizontal</property>
</object>
</child>
</object>
<object class="GtkDirectoryList" id="dirlist">
<property name="attributes">standard::name,standard::display-name,standard::icon,standard::size,standard::content-type</property>
</object>
<object class="GtkWindow" id="window">
<property name="title" translatable="yes">File browser</property>
<property name="default-width">600</property>
<property name="default-height">400</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="">
<property name="show-title-buttons">1</property>
<child>
<object class="GtkButton">
<property name="icon-name">go-up</property>
<signal name="clicked" handler="filebrowser_up_clicked_cb" object="dirlist" swapped="no"/>
</object>
</child>
<child type="end">
<object class="GtkListView">
<property name="valign">center</property>
<property name="orientation">horizontal</property>
<style>
<class name="linked"/>
</style>
<property name="model">
<object class="GtkSingleSelection" id="selected-view">
<property name="model">viewlist</property>
</object>
</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkImage">
<binding name="icon-name">
<lookup type="FileBrowserView" name="icon-name">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="can-focus">1</property>
<child>
<object class="GtkGridView" id="view">
<property name="model">dirlist</property>
<property name="max-columns">15</property>
<binding name="factory">
<lookup name="factory" type="FileBrowserView">
<lookup name="selected-item">selected-view</lookup>
</lookup>
</binding>
<binding name="orientation">
<lookup name="orientation" type="FileBrowserView">
<lookup name="selected-item">selected-view</lookup>
</lookup>
</binding>
<signal name="activate" handler="filebrowser_view_activated_cb" object="dirlist" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</interface>

View File

@@ -0,0 +1,473 @@
/* 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);
}
void
minesweeper_cell_clicked_cb (GtkGridView *gridview,
guint pos,
SweeperGame *game)
{
sweeper_game_reveal_cell (game, pos);
}
void
minesweeper_new_game_cb (GtkButton *button,
SweeperGame *game)
{
sweeper_game_new_game (game, 8, 8, 10);
}
static GtkWidget *window = NULL;
GtkWidget *
do_listview_minesweeper (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkBuilder *builder;
g_type_ensure (SWEEPER_TYPE_GAME);
builder = gtk_builder_new_from_resource ("/listview_minesweeper/listview_minesweeper.ui");
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
g_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;
}

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="SweeperGame" id="game">
</object>
<object class="GtkWindow" id="window">
<property name="title" translatable="yes">Minesweeper</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="">
<property name="show-title-buttons">1</property>
<child>
<object class="GtkButton">
<property name="label">New Game</property>
<signal name="clicked" handler="minesweeper_new_game_cb" object="game" swapped="no"/>
</object>
</child>
<child type="title">
<object class="GtkImage">
<property name="icon-name">trophy-gold</property>
<binding name="visible">
<lookup name="win">game</lookup>
</binding>
</object>
</child>
</object>
</child>
<child>
<object class="GtkGridView" id="view">
<property name="model">
<object class="GtkNoSelection">
<property name="model">game</property>
</object>
</property>
<binding name="max-columns">
<lookup name="width">game</lookup>
</binding>
<binding name="min-columns">
<lookup name="width">game</lookup>
</binding>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="resource">/listview_minesweeper/listview_minesweeper_cell.ui</property>
</object>
</property>
<signal name="activate" handler="minesweeper_cell_clicked_cb" object="game" swapped="no"/>
</object>
</child>
</object>
</interface>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="halign">center</property>
<property name="valign">center</property>
<binding name="label">
<lookup name="label" type="SweeperCell">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>

View File

@@ -0,0 +1,398 @@
/* Lists/Settings
*
* This demo shows a settings viewer for GSettings.
*
* It demonstrates how to implement support for trees with GtkListView.
*
* It also shows how to set up sorting for columns in a GtkColumnView.
*/
#include <gtk/gtk.h>
/* Create an object that wraps GSettingsSchemaKey because that's a boxed type */
typedef struct _SettingsKey SettingsKey;
struct _SettingsKey
{
GObject parent_instance;
GSettings *settings;
GSettingsSchemaKey *key;
};
enum {
PROP_0,
PROP_NAME,
PROP_SUMMARY,
PROP_DESCRIPTION,
PROP_VALUE,
N_PROPS
};
#define SETTINGS_TYPE_KEY (settings_key_get_type ())
G_DECLARE_FINAL_TYPE (SettingsKey, settings_key, SETTINGS, KEY, GObject);
G_DEFINE_TYPE (SettingsKey, settings_key, G_TYPE_OBJECT);
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
settings_key_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
SettingsKey *self = SETTINGS_KEY (object);
switch (property_id)
{
case PROP_DESCRIPTION:
g_value_set_string (value, g_settings_schema_key_get_description (self->key));
break;
case PROP_NAME:
g_value_set_string (value, g_settings_schema_key_get_name (self->key));
break;
case PROP_SUMMARY:
g_value_set_string (value, g_settings_schema_key_get_summary (self->key));
break;
case PROP_VALUE:
{
GVariant *variant = g_settings_get_value (self->settings, g_settings_schema_key_get_name (self->key));
g_value_take_string (value, g_variant_print (variant, FALSE));
g_variant_unref (variant);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
settings_key_finalize (GObject *object)
{
SettingsKey *self = SETTINGS_KEY (object);
g_object_unref (self->settings);
g_settings_schema_key_unref (self->key);
G_OBJECT_CLASS (settings_key_parent_class)->finalize (object);
}
static void
settings_key_class_init (SettingsKeyClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = settings_key_finalize;
gobject_class->get_property = settings_key_get_property;
properties[PROP_DESCRIPTION] =
g_param_spec_string ("description", NULL, NULL, NULL, G_PARAM_READABLE);
properties[PROP_NAME] =
g_param_spec_string ("name", NULL, NULL, NULL, G_PARAM_READABLE);
properties[PROP_SUMMARY] =
g_param_spec_string ("summary", NULL, NULL, NULL, G_PARAM_READABLE);
properties[PROP_VALUE] =
g_param_spec_string ("value", NULL, NULL, NULL, G_PARAM_READABLE);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
settings_key_init (SettingsKey *self)
{
}
static SettingsKey *
settings_key_new (GSettings *settings,
GSettingsSchemaKey *key)
{
SettingsKey *result = g_object_new (SETTINGS_TYPE_KEY, NULL);
result->settings = g_object_ref (settings);
result->key = g_settings_schema_key_ref (key);
return result;
}
static int
strvcmp (gconstpointer p1,
gconstpointer p2)
{
const char * const *s1 = p1;
const char * const *s2 = p2;
return strcmp (*s1, *s2);
}
static GtkFilter *current_filter;
static gboolean
transform_settings_to_keys (GBinding *binding,
const GValue *from_value,
GValue *to_value,
gpointer data)
{
GtkTreeListRow *treelistrow;
GSettings *settings;
GSettingsSchema *schema;
GListStore *store;
GtkSortListModel *sort_model;
GtkFilterListModel *filter_model;
GtkFilter *filter;
GtkExpression *expression;
char **keys;
guint i;
treelistrow = g_value_get_object (from_value);
if (treelistrow == NULL)
return TRUE;
settings = gtk_tree_list_row_get_item (treelistrow);
g_object_get (settings, "settings-schema", &schema, NULL);
store = g_list_store_new (SETTINGS_TYPE_KEY);
keys = g_settings_schema_list_keys (schema);
for (i = 0; keys[i] != NULL; i++)
{
GSettingsSchemaKey *almost_there = g_settings_schema_get_key (schema, keys[i]);
SettingsKey *finally = settings_key_new (settings, almost_there);
g_list_store_append (store, finally);
g_object_unref (finally);
g_settings_schema_key_unref (almost_there);
}
g_strfreev (keys);
g_settings_schema_unref (schema);
g_object_unref (settings);
sort_model = gtk_sort_list_model_new (G_LIST_MODEL (store),
gtk_column_view_get_sorter (GTK_COLUMN_VIEW (data)));
g_object_unref (store);
expression = gtk_property_expression_new (SETTINGS_TYPE_KEY, NULL, "name");
filter = gtk_string_filter_new ();
gtk_string_filter_set_expression (GTK_STRING_FILTER (filter), expression);
filter_model = gtk_filter_list_model_new (G_LIST_MODEL (sort_model), filter);
gtk_expression_unref (expression);
g_object_unref (sort_model);
g_set_object (&current_filter, filter);
g_object_unref (filter);
g_value_take_object (to_value, filter_model);
return TRUE;
}
static GListModel *
create_settings_model (gpointer item,
gpointer unused)
{
GSettings *settings = item;
char **schemas;
GListStore *result;
guint i;
if (settings == NULL)
{
g_settings_schema_source_list_schemas (g_settings_schema_source_get_default (),
TRUE,
&schemas,
NULL);
}
else
{
schemas = g_settings_list_children (settings);
}
if (schemas == NULL || schemas[0] == NULL)
{
g_free (schemas);
return NULL;
}
qsort (schemas, g_strv_length (schemas), sizeof (char *), strvcmp);
result = g_list_store_new (G_TYPE_SETTINGS);
for (i = 0; schemas[i] != NULL; i++)
{
GSettings *child;
if (settings == NULL)
child = g_settings_new (schemas[i]);
else
child = g_settings_get_child (settings, schemas[i]);
g_list_store_append (result, child);
g_object_unref (child);
}
g_strfreev (schemas);
return G_LIST_MODEL (result);
}
static void
search_enabled (GtkSearchEntry *entry)
{
gtk_editable_set_text (GTK_EDITABLE (entry), "");
}
static void
search_changed (GtkSearchEntry *entry,
gpointer data)
{
const char *text = gtk_editable_get_text (GTK_EDITABLE (entry));
if (current_filter)
gtk_string_filter_set_search (GTK_STRING_FILTER (current_filter), text);
}
static void
stop_search (GtkSearchEntry *entry,
gpointer data)
{
gtk_editable_set_text (GTK_EDITABLE (entry), "");
if (current_filter)
gtk_string_filter_set_search (GTK_STRING_FILTER (current_filter), "");
}
static void
move_column (GtkListView *columns_list, gboolean down)
{
GListModel *columns;
guint position;
GtkColumnViewColumn *selected;
GtkColumnView *view;
columns = gtk_list_view_get_model (columns_list);
position = gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (columns));
selected = gtk_single_selection_get_selected_item (GTK_SINGLE_SELECTION (columns));
view = gtk_column_view_column_get_column_view (selected);
if (down && position + 1 < g_list_model_get_n_items (columns))
position++;
else if (!down && position > 0)
position--;
else
return;
gtk_column_view_insert_column (view, position, selected);
gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (columns), position);
}
static void
move_column_down (GtkListView *columns_list)
{
move_column (columns_list, TRUE);
}
static void
move_column_up (GtkListView *columns_list)
{
move_column (columns_list, FALSE);
}
static void
column_visible_toggled (GtkListItem *item, GtkToggleButton *button)
{
GtkColumnViewColumn *column = gtk_list_item_get_item (item);
gtk_column_view_column_set_visible (column, gtk_toggle_button_get_active (button));
}
static GtkWidget *window = NULL;
GtkWidget *
do_listview_settings (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *listview, *columnview;
GListModel *model;
GtkTreeListModel *treemodel;
GtkSingleSelection *selection;
GtkBuilderScope *scope;
GtkBuilder *builder;
GtkColumnViewColumn *name_column;
GtkSorter *sorter;
GtkWidget *menubutton, *popover;
GtkWidget *columns_list;
g_type_ensure (SETTINGS_TYPE_KEY);
scope = gtk_builder_cscope_new ();
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "search_enabled", (GCallback)search_enabled);
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "search_changed", (GCallback)search_changed);
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "stop_search", (GCallback)stop_search);
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "move_column_down", (GCallback)move_column_down);
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "move_column_up", (GCallback)move_column_up);
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "column_visible_toggled", (GCallback)column_visible_toggled);
builder = gtk_builder_new ();
gtk_builder_set_scope (builder, scope);
{
g_autoptr(GError) error = NULL;
gtk_builder_add_from_resource (builder, "/listview_settings/listview_settings.ui", &error);
if (error)
g_warning ("%s", error->message);
}
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);
listview = GTK_WIDGET (gtk_builder_get_object (builder, "listview"));
columnview = GTK_WIDGET (gtk_builder_get_object (builder, "columnview"));
model = create_settings_model (NULL, NULL);
treemodel = gtk_tree_list_model_new (FALSE,
model,
TRUE,
create_settings_model,
NULL,
NULL);
selection = gtk_single_selection_new (G_LIST_MODEL (treemodel));
g_object_bind_property_full (selection, "selected-item",
columnview, "model",
G_BINDING_SYNC_CREATE,
transform_settings_to_keys,
NULL,
columnview, NULL);
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (selection));
g_object_unref (selection);
g_object_unref (treemodel);
g_object_unref (model);
name_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "name_column"));
sorter = gtk_string_sorter_new (gtk_property_expression_new (SETTINGS_TYPE_KEY, NULL, "name"));
gtk_column_view_column_set_sorter (name_column, sorter);
g_object_unref (sorter);
menubutton = GTK_WIDGET (gtk_builder_get_object (builder, "menubutton"));
popover = GTK_WIDGET (gtk_builder_get_object (builder, "column_popover"));
gtk_menu_button_set_popover (GTK_MENU_BUTTON (menubutton), popover);
columns_list = GTK_WIDGET (gtk_builder_get_object (builder, "columns_list"));
model = G_LIST_MODEL (gtk_single_selection_new (gtk_column_view_get_columns (GTK_COLUMN_VIEW (columnview))));
gtk_list_view_set_model (GTK_LIST_VIEW (columns_list), model);
g_object_unref (model);
g_object_unref (builder);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}

View File

@@ -0,0 +1,262 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkWindow" id="window">
<property name="title" translatable="yes">Settings</property>
<property name="default-width">640</property>
<property name="default-height">480</property>
<child type="titlebar">
<object class="GtkHeaderBar">
<property name="show-title-buttons">1</property>
<child type="end">
<object class="GtkMenuButton" id="menubutton">
<property name="icon-name">open-menu-symbolic</property>
</object>
</child>
<child type="end">
<object class="GtkToggleButton" id="search_button">
<property name="icon-name">system-search-symbolic</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkPaned">
<property name="position">300</property>
<child>
<object class="GtkScrolledWindow">
<child>
<object class="GtkListView" id="listview">
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkTreeExpander" id="expander">
<binding name="list-row">
<lookup name="item">GtkListItem</lookup>
</binding>
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<lookup name="schema" type="GSettings">
<lookup name="item">expander</lookup>
</lookup>
</binding>
</object>
</property>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkSearchBar">
<property name="search-mode-enabled" bind-source="search_button" bind-property="active" bind-flags="bidirectional"/>
<signal name="notify::search-mode-enabled" handler="search_enabled" object="entry"/>
<child>
<object class="GtkSearchEntry" id="entry">
<signal name="search-changed" handler="search_changed"/>
<signal name="stop-search" handler="stop_search"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="expand">1</property>
<child>
<object class="GtkColumnView" id="columnview">
<child>
<object class="GtkColumnViewColumn" id="name_column">
<property name="title">Name</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<lookup name="name" type="SettingsKey">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="value_column">
<property name="title">Value</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<lookup name="value" type="SettingsKey">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="summary_column">
<property name="title">Summary</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<lookup name="summary" type="SettingsKey">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="description_column">
<property name="title">Description</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<lookup name="description" type="SettingsKey">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<object class="GtkPopover" id="column_popover">
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkListView" id="columns_list">
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkBox">
<property name="spacing">10</property>
<child>
<object class="GtkCheckButton">
<signal name="toggled" handler="column_visible_toggled" object="GtkListItem"/>
<binding name="active">
<lookup name="visible" type="GtkColumnViewColumn">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="xalign">0</property>
<property name="label">label</property>
<binding name="label">
<lookup name="title" type="GtkColumnViewColumn">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</child>
</object>
</property>
</template>
</interface>
]]></property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="halign">center</property>
<style>
<class name="linked"/>
</style>
<child>
<object class="GtkButton">
<property name="icon-name">go-down-symbolic</property>
<signal name="clicked" handler="move_column_down" object="columns_list"/>
</object>
</child>
<child>
<object class="GtkButton">
<property name="icon-name">go-up-symbolic</property>
<signal name="clicked" handler="move_column_up" object="columns_list"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>

View File

@@ -0,0 +1,329 @@
/* Lists/Weather
*
* This demo shows a few of the rarer features of GtkListView and
* how they can be used to display weather information.
*
* The hourly weather info uses a horizontal listview. This is easy
* to achieve because GtkListView implements the GtkOrientable interface.
* To make the items in the list stand out more, the listview uses
* separators.
*
* A GtkNoSelectionModel is used to make sure no item in the list can be
* selected. All other interactions with the items is still possible.
*
* The dataset used here has 70000 items.
*/
#include <gtk/gtk.h>
#define GTK_TYPE_WEATHER_INFO (gtk_weather_info_get_type ())
G_DECLARE_FINAL_TYPE (GtkWeatherInfo, gtk_weather_info, GTK, WEATHER_INFO, GObject)
typedef enum {
GTK_WEATHER_CLEAR,
GTK_WEATHER_FEW_CLOUDS,
GTK_WEATHER_FOG,
GTK_WEATHER_OVERCAST,
GTK_WEATHER_SCATTERED_SHOWERS,
GTK_WEATHER_SHOWERS,
GTK_WEATHER_SNOW,
GTK_WEATHER_STORM
} GtkWeatherType;
struct _GtkWeatherInfo
{
GObject parent_instance;
gint64 timestamp;
int temperature;
GtkWeatherType weather_type;
};
struct _GtkWeatherInfoClass
{
GObjectClass parent_class;
};
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_list_item_set_child (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_list_item_get_child (list_item);
info = gtk_list_item_get_item (list_item);
child = gtk_widget_get_first_child (box);
timestamp = g_date_time_new_from_unix_utc (info->timestamp);
s = g_date_time_format (timestamp, "%R");
gtk_label_set_text (GTK_LABEL (child), s);
g_free (s);
g_date_time_unref (timestamp);
child = gtk_widget_get_next_sibling (child);
switch (info->weather_type)
{
case GTK_WEATHER_CLEAR:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-clear-symbolic");
break;
case GTK_WEATHER_FEW_CLOUDS:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-few-clouds-symbolic");
break;
case GTK_WEATHER_FOG:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-fog-symbolic");
break;
case GTK_WEATHER_OVERCAST:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-overcast-symbolic");
break;
case GTK_WEATHER_SCATTERED_SHOWERS:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-showers-scattered-symbolic");
break;
case GTK_WEATHER_SHOWERS:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-showers-symbolic");
break;
case GTK_WEATHER_SNOW:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-snow-symbolic");
break;
case GTK_WEATHER_STORM:
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-storm-symbolic");
break;
default:
gtk_image_clear (GTK_IMAGE (child));
break;
}
child = gtk_widget_get_next_sibling (child);
s = g_strdup_printf ("%d°", info->temperature);
gtk_label_set_text (GTK_LABEL (child), s);
g_free (s);
}
static GtkWidget *window = NULL;
GtkWidget *
create_weather_view (void)
{
GtkWidget *listview;
GListModel *model, *selection;
listview = gtk_list_view_new_with_factory (
gtk_functions_list_item_factory_new (setup_widget,
bind_widget,
NULL, NULL));
gtk_orientable_set_orientation (GTK_ORIENTABLE (listview), GTK_ORIENTATION_HORIZONTAL);
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
model = create_weather_model ();
selection = G_LIST_MODEL (gtk_no_selection_new (model));
gtk_list_view_set_model (GTK_LIST_VIEW (listview), selection);
g_object_unref (selection);
g_object_unref (model);
return listview;
}
GtkWidget *
do_listview_weather (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *listview, *sw;;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
gtk_window_set_title (GTK_WINDOW (window), "Weather");
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Weather");
g_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 = create_weather_view ();
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

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkTreeExpander" id="expander">
<binding name="list-row">
<lookup name="item">GtkListItem</lookup>
</binding>
<property name="child">
<object class="GtkLabel">
<property name="halign">start</property>
<binding name="label">
<lookup name="title" type="GtkDemo">
<lookup name="item">expander</lookup>
</lookup>
</binding>
</object>
</property>
</object>
</property>
</template>
</interface>

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.
@@ -893,81 +929,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
@@ -987,22 +1014,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)
{
@@ -1029,18 +1040,27 @@ scrollbar_popup (GtkWidget *scrollbar, GtkWidget *menu)
return TRUE;
}
void
main_activate_cb (GtkListView *listview,
guint position,
gpointer user_data)
{
GtkTreeListRow *row = g_list_model_get_item (gtk_list_view_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;
GtkWidget *sw;
GtkWidget *scrollbar;
GtkWidget *menu;
GtkWidget *item;
GListModel *listmodel;
GtkTreeListModel *treemodel;
static GActionEntry win_entries[] = {
{ "run", activate_run, NULL, NULL, NULL }
@@ -1048,8 +1068,8 @@ activate (GApplication *app)
builder = gtk_builder_new_from_resource ("/ui/main.ui");
window = (GtkWindow *)gtk_builder_get_object (builder, "window");
gtk_application_add_window (GTK_APPLICATION (app), window);
window = (GtkWidget *)gtk_builder_get_object (builder, "window");
gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (window));
g_action_map_add_action_entries (G_ACTION_MAP (window),
win_entries, G_N_ELEMENTS (win_entries),
window);
@@ -1059,8 +1079,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));
@@ -1079,17 +1098,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_list_view_set_model (GTK_LIST_VIEW (listview),
G_LIST_MODEL (selection));
g_signal_connect (treeview, "row-activated", G_CALLBACK (row_activated_cb), model);
widget = (GtkWidget *)gtk_builder_get_object (builder, "treeview-selection");
g_signal_connect (widget, "changed", G_CALLBACK (selection_cb), model);
gtk_tree_model_get_iter_first (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)), &iter);
gtk_tree_selection_select_iter (GTK_TREE_SELECTION (widget), &iter);
gtk_tree_view_collapse_all (GTK_TREE_VIEW (treeview));
award ("demo-start");
gtk_widget_show (GTK_WIDGET (window));
@@ -1106,7 +1127,7 @@ auto_quit (gpointer data)
static void
list_demos (void)
{
Demo *d, *c;
DemoData *d, *c;
d = gtk_demos;
@@ -1133,7 +1154,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;

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="GtkListView" id="listview">
<signal name="activate" handler="main_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>

View File

@@ -3,6 +3,7 @@
demos = files([
'application_demo.c',
'assistant.c',
'awardview.c',
'builder.c',
'changedisplay.c',
'clipboard.c',
@@ -43,6 +44,13 @@ demos = files([
'listbox.c',
'flowbox.c',
'list_store.c',
'listview_applauncher.c',
'listview_clocks.c',
'listview_colors.c',
'listview_filebrowser.c',
'listview_minesweeper.c',
'listview_settings.c',
'listview_weather.c',
'markup.c',
'menus.c',
'modelbutton.c',
@@ -86,6 +94,7 @@ demos = files([
gtkdemo_deps = [ libgtk_dep, ]
extra_demo_sources = files(['main.c',
'award.c',
'gtkfishbowl.c',
'fontplane.c',
'gtkgears.c',
@@ -96,7 +105,7 @@ extra_demo_sources = files(['main.c',
if harfbuzz_dep.found() and pangoft_dep.found()
demos += files('font_features.c')
extra_demo_sources += files(['script-names.c', 'language-names.c'])
gtkdemo_deps += [ harfbuzz_dep, ]
gtkdemo_deps += [ harfbuzz_dep, epoxy_dep ]
endif
if os_unix

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);

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);

View File

@@ -44,19 +44,51 @@
<chapter id="Lists">
<title>GListModel support</title>
<xi:include href="xml/gtkexpression.xml" />
<xi:include href="xml/gtkfilterlistmodel.xml" />
<section>
<xi:include href="xml/gtkfilter.xml" />
<xi:include href="xml/gtkcustomfilter.xml" />
<xi:include href="xml/gtkstringfilter.xml" />
<xi:include href="xml/gtkmultifilter.xml" />
</section>
<xi:include href="xml/gtkflattenlistmodel.xml" />
<xi:include href="xml/gtkmaplistmodel.xml" />
<xi:include href="xml/gtkslicelistmodel.xml" />
<xi:include href="xml/gtksortlistmodel.xml" />
<section>
<xi:include href="xml/gtksorter.xml" />
<xi:include href="xml/gtkcustomsorter.xml" />
<xi:include href="xml/gtkstringsorter.xml" />
<xi:include href="xml/gtknumericsorter.xml" />
<xi:include href="xml/gtkmultisorter.xml" />
</section>
<xi:include href="xml/gtkselectionmodel.xml" />
<xi:include href="xml/gtknoselection.xml" />
<xi:include href="xml/gtksingleselection.xml" />
<xi:include href="xml/gtkdirectorylist.xml" />
</chapter>
<chapter id="ListContainers">
<title>List-based Widgets</title>
<xi:include href="xml/gtklistitem.xml" />
<xi:include href="xml/gtklistitemfactory.xml" />
<section>
<xi:include href="xml/gtksignallistitemfactory.xml" />
<xi:include href="xml/gtkbuilderlistitemfactory.xml" />
</section>
<xi:include href="xml/gtklistview.xml" />
<xi:include href="xml/gtkgridview.xml" />
<xi:include href="xml/gtkcolumnview.xml" />
<xi:include href="xml/gtkcolumnviewcolumn.xml" />
<xi:include href="xml/gtkdropdown.xml" />
</chapter>
<chapter id="Trees">
<xi:include href="xml/gtktreelistrow.xml" />
<xi:include href="xml/gtktreelistmodel.xml" />
<xi:include href="xml/gtktreelistrowsorter.xml" />
<xi:include href="xml/gtktreeexpander.xml" />
</chapter>
<chapter id="Application">
@@ -234,6 +266,7 @@
<xi:include href="xml/gtkpopover.xml" />
<xi:include href="xml/gtkpopovermenu.xml" />
<xi:include href="xml/gtkpopovermenubar.xml" />
<xi:include href="xml/gtkdropdown.xml" />
</chapter>
<chapter id="SelectorWidgets">

View File

@@ -490,6 +490,190 @@ gtk_single_selection_set_can_unselect
gtk_single_selection_get_type
</SECTION>
<SECTION>
<FILE>gtklistitem</FILE>
<TITLE>GtkListItem</TITLE>
GtkListItem
gtk_list_item_get_item
gtk_list_item_get_position
gtk_list_item_get_child
gtk_list_item_set_child
gtk_list_item_get_selected
gtk_list_item_get_selectable
gtk_list_item_set_selectable
<SUBSECTION Standard>
GTK_LIST_ITEM
GTK_LIST_ITEM_CLASS
GTK_LIST_ITEM_GET_CLASS
GTK_IS_LIST_ITEM
GTK_IS_LIST_ITEM_CLASS
GTK_TYPE_LIST_ITEM
<SUBSECTION Private>
gtk_list_item_get_type
</SECTION>
<SECTION>
<FILE>gtklistitemfactory</FILE>
<TITLE>GtkListItemFactory</TITLE>
GtkListItemFactory
<SUBSECTION Standard>
GTK_LIST_ITEM_FACTORY
GTK_LIST_ITEM_FACTORY_CLASS
GTK_LIST_ITEM_FACTORY_GET_CLASS
GTK_IS_LIST_ITEM_FACTORY
GTK_IS_LIST_ITEM_FACTORY_CLASS
GTK_TYPE_LIST_ITEM_FACTORY
<SUBSECTION Private>
gtk_list_item_factory_get_type
</SECTION>
<SECTION>
<FILE>gtkbuilderlistitemfactory</FILE>
<TITLE>GtkBuilderListItemFactory</TITLE>
GtkBuilderListItemFactory
gtk_builder_list_item_factory_new_from_bytes
gtk_builder_list_item_factory_new_from_resource
gtk_builder_list_item_factory_get_bytes
gtk_builder_list_item_factory_get_resource
gtk_builder_list_item_factory_get_scope
<SUBSECTION Standard>
GTK_BUILDER_LIST_ITEM_FACTORY
GTK_BUILDER_LIST_ITEM_FACTORY_CLASS
GTK_BUILDER_LIST_ITEM_FACTORY_GET_CLASS
GTK_IS_BUILDER_LIST_ITEM_FACTORY
GTK_IS_BUILDER_LIST_ITEM_FACTORY_CLASS
GTK_TYPE_BUILDER_LIST_ITEM_FACTORY
<SUBSECTION Private>
gtk_builder_list_item_factory_get_type
</SECTION>
<SECTION>
<FILE>gtksignallistitemfactory</FILE>
<TITLE>GtkSignalListItemFactory</TITLE>
GtkSignalListItemFactory
gtk_signal_list_item_factory_new
<SUBSECTION Standard>
GTK_SIGNAL_LIST_ITEM_FACTORY
GTK_SIGNAL_LIST_ITEM_FACTORY_CLASS
GTK_SIGNAL_LIST_ITEM_FACTORY_GET_CLASS
GTK_IS_SIGNAL_LIST_ITEM_FACTORY
GTK_IS_SIGNAL_LIST_ITEM_FACTORY_CLASS
GTK_TYPE_SIGNAL_LIST_ITEM_FACTORY
<SUBSECTION Private>
gtk_signal_list_item_factory_get_type
</SECTION>
<SECTION>
<FILE>gtklistview</FILE>
<TITLE>GtkListView</TITLE>
GtkListView
gtk_list_view_new
gtk_list_view_new_with_factory
gtk_list_view_set_factory
gtk_list_view_get_factory
gtk_list_view_set_model
gtk_list_view_get_model
gtk_list_view_set_show_separators
gtk_list_view_get_show_separators
gtk_list_view_set_single_click_activate
gtk_list_view_get_single_click_activate
gtk_list_view_set_enable_rubberband
gtk_list_view_get_enable_rubberband
<SUBSECTION Standard>
GTK_LIST_VIEW
GTK_LIST_VIEW_CLASS
GTK_LIST_VIEW_GET_CLASS
GTK_IS_LIST_VIEW
GTK_IS_LIST_VIEW_CLASS
GTK_TYPE_LIST_VIEW
<SUBSECTION Private>
gtk_list_view_get_type
</SECTION>
<SECTION>
<FILE>gtkcolumnview</FILE>
<TITLE>GtkColumnView</TITLE>
GtkColumnView
gtk_column_view_new
gtk_column_view_append_column
gtk_column_view_insert_column
gtk_column_view_remove_column
gtk_column_view_get_columns
gtk_column_view_get_model
gtk_column_view_set_model
gtk_column_view_get_sorter
gtk_column_view_get_show_separators
gtk_column_view_set_show_separators
gtk_column_view_set_single_click_activate
gtk_column_view_get_single_click_activate
gtk_column_view_set_enable_rubberband
gtk_column_view_get_enable_rubberband
gtk_column_view_sort_by_column
<SUBSECTION Standard>
GTK_COLUMN_VIEW
GTK_COLUMN_VIEW_CLASS
GTK_COLUMN_VIEW_GET_CLASS
GTK_IS_COLUMN_VIEW
GTK_IS_COLUMN_VIEW_CLASS
GTK_TYPE_COLUMN_VIEW
<SUBSECTION Private>
gtk_column_view_get_type
</SECTION>
<SECTION>
<FILE>gtkcolumnviewcolumn</FILE>
<TITLE>GtkColumnViewColumn</TITLE>
GtkColumnViewColumn
gtk_column_view_column_new
gtk_column_view_column_new_with_factory
gtk_column_view_column_get_column_view
gtk_column_view_column_set_factory
gtk_column_view_column_get_factory
gtk_column_view_column_set_title
gtk_column_view_column_get_title
gtk_column_view_column_set_sorter
gtk_column_view_column_get_sorter
gtk_column_view_column_set_visible
gtk_column_view_column_get_visible
gtk_column_view_column_set_fixed_width
gtk_column_view_column_get_fixed_width
<SUBSECTION Standard>
GTK_COLUMN_VIEW_COLUMN
GTK_COLUMN_VIEW_COLUMN_CLASS
GTK_COLUMN_VIEW_COLUMN_GET_CLASS
GTK_IS_COLUMN_VIEW_COLUMN
GTK_IS_COLUMN_VIEW_COLUMN_CLASS
GTK_TYPE_COLUMN_VIEW_COLUMN
<SUBSECTION Private>
gtk_column_view_column_get_type
</SECTION>
<SECTION>
<FILE>gtkgridview</FILE>
<TITLE>GtkGridView</TITLE>
GtkGridView
gtk_grid_view_new
gtk_grid_view_set_model
gtk_grid_view_get_model
gtk_grid_view_set_max_columns
gtk_grid_view_get_max_columns
gtk_grid_view_set_min_columns
gtk_grid_view_get_min_columns
gtk_grid_view_set_single_click_activate
gtk_grid_view_get_single_click_activate
gtk_grid_view_set_enable_rubberband
gtk_grid_view_get_enable_rubberband
<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
@@ -1339,6 +1523,92 @@ GTK_TYPE_FILE_FILTER
gtk_file_filter_get_type
</SECTION>
<SECTION>
<FILE>gtkdirectorylist</FILE>
<TITLE>GtkDirectoryList</TITLE>
GtkDirectoryList
gtk_directory_list_new
gtk_directory_list_get_attributes
gtk_directory_list_set_attributes
gtk_directory_list_get_file
gtk_directory_list_set_file
gtk_directory_list_get_io_priority
gtk_directory_list_set_io_priority
gtk_directory_list_is_loading
gtk_directory_list_get_error
<SUBSECTION Standard>
GTK_DIRECTORY_LIST
GTK_IS_DIRECTORY_LIST
GTK_TYPE_DIRECTORY_LIST
GTK_DIRECTORY_LIST_CLASS
GTK_IS_DIRECTORY_LIST_CLASS
GTK_DIRECTORY_LIST_GET_CLASS
<SUBSECTION Private>
gtk_directory_list_get_type
</SECTION>
<SECTION>
<FILE>gtkfilter</FILE>
<TITLE>GtkFilter</TITLE>
GtkFilter
gtk_filter_match
gtk_filter_get_strictness
<SUBSECTION>
GtkFilterChange
gtk_filter_changed
<SUBSECTION>
gtk_custom_filter_new
<SUBSECTION Standard>
GTK_FILTER
GTK_IS_FILTER
GTK_TYPE_FILTER
GTK_FILTER_CLASS
GTK_IS_FILTER_CLASS
GTK_FILTER_GET_CLASS
<SUBSECTION Private>
gtk_filter_get_type
</SECTION>
<SECTION>
<FILE>gtkcustomfilter</FILE>
<TITLE>GtkCustomFilter</TITLE>
GtkCustomFilter
GtkCustomFilterFunc
gtk_custom_filter_new
<SUBSECTION Standard>
GTK_CUSTOM_FILTER
GTK_IS_CUSTOM_FILTER
GTK_TYPE_CUSTOM_FILTER
GTK_CUSTOM_FILTER_CLASS
GTK_IS_CUSTOM_FILTER_CLASS
GTK_CUSTOM_FILTER_GET_CLASS
<SUBSECTION Private>
gtk_custom_filter_get_type
</SECTION>
<SECTION>
<FILE>gtkmultifilter</FILE>
<TITLE>GtkMultiFilter</TITLE>
GtkMultiFilter
gtk_multi_filter_append
gtk_multi_filter_remove
<SUBSECTION>
GtkAnyFilter
gtk_any_filter_new
<SUBSECTION>
GtkEveryFilter
gtk_every_filter_new
<SUBSECTION Standard>
GTK_CUSTOM_FILTER
GTK_IS_CUSTOM_FILTER
GTK_TYPE_CUSTOM_FILTER
GTK_CUSTOM_FILTER_CLASS
GTK_IS_CUSTOM_FILTER_CLASS
GTK_CUSTOM_FILTER_GET_CLASS
<SUBSECTION Private>
gtk_custom_filter_get_type
</SECTION>
<SECTION>
<FILE>gtkfilterlistmodel</FILE>
<TITLE>GtkFilterListModel</TITLE>
@@ -1347,9 +1617,8 @@ gtk_filter_list_model_new
gtk_filter_list_model_new_for_type
gtk_filter_list_model_set_model
gtk_filter_list_model_get_model
gtk_filter_list_model_set_filter_func
gtk_filter_list_model_has_filter
gtk_filter_list_model_refilter
gtk_filter_list_model_set_filter
gtk_filter_list_model_get_filter
<SUBSECTION Standard>
GTK_FILTER_LIST_MODEL
GTK_IS_FILTER_LIST_MODEL
@@ -2340,8 +2609,6 @@ gtk_recent_manager_move_item
gtk_recent_manager_get_items
gtk_recent_manager_purge_items
<SUBSECTION>
gtk_recent_info_ref
gtk_recent_info_unref
gtk_recent_info_get_uri
gtk_recent_info_get_display_name
gtk_recent_info_get_description
@@ -2672,17 +2939,123 @@ GTK_SLICE_LIST_MODEL_GET_CLASS
gtk_slice_list_model_get_type
</SECTION>
<SECTION>
<FILE>gtksorter</FILE>
<TITLE>GtkSorter</TITLE>
GtkSorter
GtkSorterOrder
GtkSorterChange
gtk_sorter_compare
gtk_sorter_get_order
gtk_sorter_changed
<SUBSECTION Standard>
GTK_SORTER
GTK_IS_SORTER
GTK_TYPE_SORTER
GTK_SORTER_CLASS
GTK_IS_SORTER_CLASS
GTK_SORTER_GET_CLASS
<SUBSECTION Private>
gtk_sorter_get_type
</SECTION>
<SECTION>
<FILE>gtkstringsorter</FILE>
<TITLE>GtkStringSorter</TITLE>
GtkStringSorter
gtk_string_sorter_new
gtk_string_sorter_get_expression
gtk_string_sorter_set_expression
gtk_string_sorter_get_ignore_case
gtk_string_sorter_set_ignore_case
<SUBSECTION Standard>
GTK_STRING_SORTER
GTK_IS_STRING_SORTER
GTK_TYPE_STRING_SORTER
GTK_IS_STRING_SORTER_CLASS
GTK_STRING_SORTER_GET_CLASS
<SUBSECTION Private>
gtk_string_sorter_get_type
</SECTION>
<SECTION>
<FILE>gtknumericsorter</FILE>
<TITLE>GtkNumericSorter</TITLE>
GtkNumericSorter
gtk_numeric_sorter_new
gtk_numeric_sorter_get_expression
gtk_numeric_sorter_set_expression
gtk_numeric_sorter_get_sort_order
gtk_numeric_sorter_set_sort_order
<SUBSECTION Standard>
GTK_NUMERIC_SORTER
GTK_IS_NUMERIC_SORTER
GTK_TYPE_NUMERIC_SORTER
GTK_IS_NUMERIC_SORTER_CLASS
GTK_NUMERIC_SORTER_GET_CLASS
<SUBSECTION Private>
gtk_numeric_sorter_get_type
</SECTION>
<SECTION>
<FILE>gtkcustomsorter</FILE>
<TITLE>GtkCustomSorter</TITLE>
GtkCustomSorter
gtk_custom_sorter_new
<SUBSECTION Standard>
GTK_CUSTOM_SORTER
GTK_IS_CUSTOM_SORTER
GTK_TYPE_CUSTOM_SORTER
GTK_IS_CUSTOM_SORTER_CLASS
GTK_CUSTOM_SORTER_GET_CLASS
<SUBSECTION Private>
gtk_custom_sorter_get_type
</SECTION>
<SECTION>
<FILE>gtkmultisorter</FILE>
<TITLE>GtkMultiSorter</TITLE>
GtkMultiSorter
gtk_multi_sorter_new
gtk_multi_sorter_append
gtk_multi_sorter_remove
<SUBSECTION Standard>
GTK_MULTI_SORTER
GTK_IS_MULTI_SORTER
GTK_TYPE_MULTI_SORTER
GTK_IS_MULTI_SORTER_CLASS
GTK_MULTI_SORTER_GET_CLASS
<SUBSECTION Private>
gtk_multi_sorter_get_type
</SECTION>
<SECTION>
<FILE>gtktreelistrowsorter</FILE>
<TITLE>GtkTreeListRowSorter</TITLE>
GtkTreeListRowSorter
gtk_tree_list_row_sorter_new
gtk_tree_list_row_sorter_get_sorter
gtk_tree_list_row_sorter_set_sorter
<SUBSECTION Standard>
GTK_TREE_LIST_ROW_SORTER
GTK_IS_TREE_LIST_ROW_SORTER
GTK_TYPE_TREE_LIST_ROW_SORTER
GTK_IS_TREE_LIST_ROW_SORTER_CLASS
GTK_TREE_LIST_ROW_SORTER_GET_CLASS
<SUBSECTION Private>
gtk_tree_list_row_sorter_get_type
</SECTION>
<SECTION>
<FILE>gtksortlistmodel</FILE>
<TITLE>GtkSortListModel</TITLE>
GtkSortListModel
gtk_sort_list_model_new
gtk_sort_list_model_new_for_type
gtk_sort_list_model_set_sort_func
gtk_sort_list_model_has_sort
gtk_sort_list_model_set_sorter
gtk_sort_list_model_get_sorter
gtk_sort_list_model_set_model
gtk_sort_list_model_get_model
gtk_sort_list_model_resort
<SUBSECTION Standard>
GTK_SORT_LIST_MODEL
GTK_IS_SORT_LIST_MODEL
@@ -3481,6 +3854,26 @@ GTK_TREE_LIST_MODEL_GET_CLASS
gtk_tree_list_row_get_type
</SECTION>
<SECTION>
<FILE>gtktreeexpander</FILE>
<TITLE>GtkTreeExpander</TITLE>
gtk_tree_expander_new
gtk_tree_expander_get_child
gtk_tree_expander_set_child
gtk_tree_expander_get_item
gtk_tree_expander_get_list_row
gtk_tree_expander_set_list_row
<SUBSECTION Standard>
GTK_TREE_EXPANDER
GTK_IS_TREE_EXPANDER
GTK_TYPE_TREE_EXPANDER
GTK_TREE_EXPANDER_CLASS
GTK_IS_TREE_EXPANDER_CLASS
GTK_TREE_EXPANDER_GET_CLASS
<SUBSECTION Private>
gtk_tree_expander_get_type
</SECTION>
<SECTION>
<FILE>gtktreemodel</FILE>
<TITLE>GtkTreeModel</TITLE>
@@ -4563,6 +4956,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
@@ -5274,6 +5668,8 @@ GtkDeleteType
GtkDirectionType
GtkJustification
GtkMovementStep
GtkOrdering
gtk_ordering_from_cmpfunc
GtkOrientation
GtkPackType
GtkPositionType
@@ -7393,3 +7789,77 @@ gtk_constraint_guide_get_max_size
GTK_TYPE_CONSTRAINT_GUIDE
gtk_constraint_guide_get_tyoe
</SECTION>
<SECTION>
<FILE>gtkexpression</FILE>
GtkExpression
GtkExpressionWatch
GtkExpressionNotify
gtk_expression_ref
gtk_expression_unref
gtk_expression_get_value_type
gtk_expression_is_static
gtk_expression_evaluate
gtk_expression_watch
gtk_expression_bind
gtk_expression_watch_ref
gtk_expression_watch_unref
gtk_expression_watch_evaluate
gtk_expression_watch_unwatch
<SUBSECTION>
gtk_property_expression_new
gtk_constant_expression_new
gtk_constant_expression_new_for_value
gtk_object_expression_new
gtk_closure_expression_new
gtk_cclosure_expression_new
<SUBSECTION Standard>
GTK_IS_EXPRESSION
GTK_TYPE_EXPRESSION
<SUBSECTION Private>
gtk_expression_get_type
</SECTION>
<SECTION>
<FILE>gtkstringfilter</FILE>
GtkStringFilter
GtkStringFilterMatchMode
gtk_string_filter_new
gtk_string_filter_get_search
gtk_string_filter_set_search
gtk_string_filter_get_expression
gtk_string_filter_set_expression
gtk_string_filter_get_ignore_case
gtk_string_filter_set_ignore_case
gtk_string_filter_get_match_mode
gtk_string_filter_set_match_mode
<SUBSECTION Private>
gtk_string_filter_get_type
</SECTION>
<SECTION>
<FILE>gtkdropdown</FILE>
<TITLE>GtkDropDown</TITLE>
GtkDropDown
gtk_drop_down_new
gtk_drop_down_set_from_strings
gtk_drop_down_set_model
gtk_drop_down_get_model
gtk_drop_down_set_selected
gtk_drop_down_get_selected
gtk_drop_down_set_factory
gtk_drop_down_get_factory
gtk_drop_down_set_list_factory
gtk_drop_down_get_list_factory
gtk_drop_down_set_expression
gtk_drop_down_get_expression
gtk_drop_down_set_enable_search
gtk_drop_down_get_enable_search
<SUBSECTION Standard>
GTK_TYPE_DROP_DOWN
<SUBSECTION Private>
gtk_drop_down_get_type
</SECTION>

View File

@@ -9,6 +9,7 @@ gtk_accessible_get_type
gtk_actionable_get_type
gtk_action_bar_get_type
gtk_adjustment_get_type
gtk_any_filter_get_type
gtk_app_chooser_get_type
gtk_app_chooser_button_get_type
gtk_app_chooser_dialog_get_type
@@ -22,10 +23,11 @@ gtk_bin_get_type
gtk_bin_layout_get_type
gtk_box_get_type
gtk_box_layout_get_type
gtk_buildable_get_type
gtk_builder_cscope_get_type
gtk_builder_get_type
gtk_builder_list_item_factory_get_type
gtk_builder_scope_get_type
gtk_buildable_get_type
gtk_button_get_type
gtk_calendar_get_type
gtk_cell_area_get_type
@@ -49,6 +51,8 @@ gtk_color_button_get_type
gtk_color_chooser_get_type
gtk_color_chooser_dialog_get_type
gtk_color_chooser_widget_get_type
gtk_column_view_get_type
gtk_column_view_column_get_type
gtk_combo_box_get_type
gtk_combo_box_text_get_type
gtk_constraint_get_type
@@ -56,9 +60,13 @@ gtk_constraint_guide_get_type
gtk_constraint_layout_get_type
gtk_constraint_target_get_type
gtk_container_get_type
gtk_custom_sorter_get_type
gtk_css_provider_get_type
gtk_custom_filter_get_type
gtk_dialog_get_type
gtk_directory_list_get_type
gtk_drawing_area_get_type
gtk_drop_down_get_type
gtk_editable_get_type
gtk_entry_buffer_get_type
gtk_entry_completion_get_type
@@ -68,12 +76,14 @@ gtk_event_controller_key_get_type
gtk_event_controller_legacy_get_type
gtk_event_controller_motion_get_type
gtk_event_controller_scroll_get_type
gtk_every_filter_get_type
gtk_expander_get_type
gtk_file_chooser_button_get_type
gtk_file_chooser_dialog_get_type
gtk_file_chooser_get_type
gtk_file_chooser_widget_get_type
gtk_file_filter_get_type
gtk_filter_get_type
gtk_filter_list_model_get_type
gtk_fixed_get_type
gtk_fixed_layout_get_type
@@ -99,6 +109,7 @@ gtk_gl_area_get_type
gtk_grid_get_type
gtk_grid_layout_child_get_type
gtk_grid_layout_get_type
gtk_grid_view_get_type
gtk_header_bar_get_type
gtk_icon_theme_get_type
gtk_icon_view_get_type
@@ -111,9 +122,12 @@ gtk_label_get_type
gtk_layout_child_get_type
gtk_layout_manager_get_type
gtk_link_button_get_type
gtk_list_item_get_type
gtk_list_item_factory_get_type
gtk_list_store_get_type
gtk_list_box_get_type
gtk_list_box_row_get_type
gtk_list_view_get_type
gtk_lock_button_get_type
gtk_map_list_model_get_type
gtk_media_controls_get_type
@@ -127,10 +141,13 @@ gtk_menu_shell_get_type
gtk_menu_tool_button_get_type
gtk_message_dialog_get_type
gtk_mount_operation_get_type
gtk_multi_filter_get_type
gtk_multi_sorter_get_type
gtk_native_get_type
gtk_no_selection_get_type
gtk_notebook_get_type
gtk_notebook_page_get_type
gtk_numeric_sorter_get_type
gtk_orientable_get_type
gtk_overlay_get_type
gtk_pad_controller_get_type
@@ -175,11 +192,13 @@ gtk_shortcuts_window_get_type
gtk_shortcuts_section_get_type
gtk_shortcuts_group_get_type
gtk_shortcuts_shortcut_get_type
gtk_signal_list_item_factory_get_type
gtk_single_selection_get_type
gtk_size_group_get_type
gtk_slice_list_model_get_type
gtk_snapshot_get_type
gtk_sort_list_model_get_type
gtk_sorter_get_type
gtk_spin_button_get_type
gtk_spinner_get_type
gtk_stack_get_type
@@ -187,6 +206,8 @@ gtk_stack_page_get_type
gtk_stack_sidebar_get_type
gtk_stack_switcher_get_type
gtk_statusbar_get_type
gtk_string_filter_get_type
gtk_string_sorter_get_type
gtk_switch_get_type
gtk_level_bar_get_type
gtk_style_context_get_type
@@ -209,6 +230,7 @@ gtk_tree_drag_source_get_type
gtk_tree_expander_get_type
gtk_tree_list_model_get_type
gtk_tree_list_row_get_type
gtk_tree_list_row_sorter_get_type
gtk_tree_model_filter_get_type
gtk_tree_model_get_type
gtk_tree_model_sort_get_type

View File

@@ -21,6 +21,13 @@ private_headers = [
'gtkcolorplaneprivate.h',
'gtkcolorscaleprivate.h',
'gtkcolorswatchprivate.h',
'gtkcolumnviewcellprivate.h',
'gtkcolumnviewcolumnprivate.h',
'gtkcolumnviewlayoutprivate.h',
'gtkcolumnlistitemfactoryprivate.h',
'gtkcolumviewprivate.h',
'gtkcolumnviewsorterprivate.h',
'gtkcolumnviewtitleprivate.h',
'gtkcomboboxprivate.h',
'gtkcontainerprivate.h',
'gtkconstraintexpressionprivate.h',
@@ -131,6 +138,11 @@ private_headers = [
'gtkimmoduleprivate.h',
'gtkkineticscrollingprivate.h',
'gtklabelprivate.h',
'gtklistbaseprivate.h',
'gtklistitemprivate.h',
'gtklistitemfactoryprivate.h',
'gtklistitemmanagerprivate.h',
'gtklistitemwidgetprivate.h',
'gtklockbuttonprivate.h',
'gtkmagnifierprivate.h',
'gtkmediafileprivate.h',

View File

@@ -22,20 +22,12 @@
#ifndef __GI_SCANNER__
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkAppLaunchContext, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkClipboard, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkContentProvider, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkCursor, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkDevice, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkDisplay, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkDisplayManager, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkDrag, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkDrawContext, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkFrameClock, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkGLContext, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkKeymap, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkMonitor, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkSeat, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkVulkanContext, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkSurface, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkContentFormats, gdk_content_formats_unref)

View File

@@ -32,14 +32,11 @@
G_BEGIN_DECLS
#define GDK_TYPE_CAIRO_CONTEXT (gdk_cairo_context_get_type ())
#define GDK_CAIRO_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CAIRO_CONTEXT, GdkCairoContext))
#define GDK_IS_CAIRO_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_CAIRO_CONTEXT))
#define GDK_CAIRO_ERROR (gdk_cairo_error_quark ())
#define GDK_TYPE_CAIRO_CONTEXT (gdk_cairo_context_get_type ())
GDK_AVAILABLE_IN_ALL
GType gdk_cairo_context_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GdkCairoContext, gdk_cairo_context, GDK, CAIRO_CONTEXT)
GDK_AVAILABLE_IN_ALL
cairo_t * gdk_cairo_context_cairo_create (GdkCairoContext *self);

View File

@@ -29,12 +29,6 @@
G_BEGIN_DECLS
#define GDK_CAIRO_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CAIRO_CONTEXT, GdkCairoContextClass))
#define GDK_IS_CAIRO_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CAIRO_CONTEXT))
#define GDK_CAIRO_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CAIRO_CONTEXT, GdkCairoContextClass))
typedef struct _GdkCairoContextClass GdkCairoContextClass;
struct _GdkCairoContext
{
GdkDrawContext parent_instance;

View File

@@ -31,11 +31,8 @@
G_BEGIN_DECLS
#define GDK_TYPE_CLIPBOARD (gdk_clipboard_get_type ())
#define GDK_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CLIPBOARD, GdkClipboard))
#define GDK_IS_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_CLIPBOARD))
GDK_AVAILABLE_IN_ALL
GType gdk_clipboard_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GdkClipboard, gdk_clipboard, GDK, CLIPBOARD)
GDK_AVAILABLE_IN_ALL
GdkDisplay * gdk_clipboard_get_display (GdkClipboard *clipboard);

View File

@@ -22,12 +22,6 @@
G_BEGIN_DECLS
#define GDK_CLIPBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CLIPBOARD, GdkClipboardClass))
#define GDK_IS_CLIPBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CLIPBOARD))
#define GDK_CLIPBOARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CLIPBOARD, GdkClipboardClass))
typedef struct _GdkClipboardClass GdkClipboardClass;
struct _GdkClipboard
{
GObject parent;

View File

@@ -35,14 +35,8 @@
G_BEGIN_DECLS
#define GDK_TYPE_CURSOR (gdk_cursor_get_type ())
#define GDK_CURSOR(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_CURSOR, GdkCursor))
#define GDK_IS_CURSOR(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_CURSOR))
/* Cursors
*/
GDK_AVAILABLE_IN_ALL
GType gdk_cursor_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GdkCursor, gdk_cursor, GDK, CURSOR)
GDK_AVAILABLE_IN_ALL
GdkCursor* gdk_cursor_new_from_texture (GdkTexture *texture,

View File

@@ -29,12 +29,6 @@
G_BEGIN_DECLS
#define GDK_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CURSOR, GdkCursorClass))
#define GDK_IS_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CURSOR))
#define GDK_CURSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CURSOR, GdkCursorClass))
typedef struct _GdkCursorClass GdkCursorClass;
struct _GdkCursor
{
GObject parent_instance;

View File

@@ -29,8 +29,8 @@
G_BEGIN_DECLS
#define GDK_TYPE_DEVICE (gdk_device_get_type ())
#define GDK_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE, GdkDevice))
#define GDK_IS_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE))
GDK_AVAILABLE_IN_ALL
GDK_DECLARE_EXPORTED_TYPE (GdkDevice, gdk_device, GDK, DEVICE)
typedef struct _GdkTimeCoord GdkTimeCoord;
@@ -121,9 +121,6 @@ struct _GdkTimeCoord
gdouble axes[GDK_MAX_TIMECOORD_AXES];
};
GDK_AVAILABLE_IN_ALL
GType gdk_device_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
const gchar * gdk_device_get_name (GdkDevice *device);
GDK_AVAILABLE_IN_ALL

View File

@@ -25,11 +25,6 @@
G_BEGIN_DECLS
#define GDK_DEVICE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE, GdkDeviceClass))
#define GDK_IS_DEVICE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE))
#define GDK_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE, GdkDeviceClass))
typedef struct _GdkDeviceClass GdkDeviceClass;
typedef struct _GdkDeviceKey GdkDeviceKey;
struct _GdkDeviceKey

View File

@@ -34,12 +34,10 @@
G_BEGIN_DECLS
#define GDK_TYPE_DISPLAY (gdk_display_get_type ())
#define GDK_DISPLAY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_DISPLAY, GdkDisplay))
#define GDK_IS_DISPLAY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_DISPLAY))
#define GDK_TYPE_DISPLAY (gdk_display_get_type ())
GDK_AVAILABLE_IN_ALL
GType gdk_display_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GdkDisplay, gdk_display, GDK, DISPLAY)
GDK_AVAILABLE_IN_ALL
GdkDisplay *gdk_display_open (const gchar *display_name);

View File

@@ -30,13 +30,6 @@
G_BEGIN_DECLS
#define GDK_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_DISPLAY, GdkDisplayClass))
#define GDK_IS_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_DISPLAY))
#define GDK_DISPLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_DISPLAY, GdkDisplayClass))
typedef struct _GdkDisplayClass GdkDisplayClass;
/* Tracks information about the device grab on this display */
typedef struct
{

View File

@@ -36,8 +36,8 @@
G_BEGIN_DECLS
#define GDK_TYPE_DRAG (gdk_drag_get_type ())
#define GDK_DRAG(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_DRAG, GdkDrag))
#define GDK_IS_DRAG(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_DRAG))
GDK_AVAILABLE_IN_ALL
GDK_DECLARE_EXPORTED_TYPE (GdkDrag, gdk_drag, GDK, DRAG)
/**
* GdkDragCancelReason:
@@ -53,9 +53,6 @@ typedef enum {
GDK_DRAG_CANCEL_ERROR
} GdkDragCancelReason;
GDK_AVAILABLE_IN_ALL
GType gdk_drag_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GdkDisplay * gdk_drag_get_display (GdkDrag *drag);
GDK_AVAILABLE_IN_ALL

View File

@@ -23,13 +23,6 @@
G_BEGIN_DECLS
#define GDK_DRAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_DRAG, GdkDragClass))
#define GDK_IS_DRAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_DRAG))
#define GDK_DRAG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_DRAG, GdkDragClass))
typedef struct _GdkDragClass GdkDragClass;
struct _GdkDragClass {
GObjectClass parent_class;

View File

@@ -30,12 +30,9 @@
G_BEGIN_DECLS
#define GDK_TYPE_DRAW_CONTEXT (gdk_draw_context_get_type ())
#define GDK_DRAW_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_DRAW_CONTEXT, GdkDrawContext))
#define GDK_IS_DRAW_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_DRAW_CONTEXT))
#define GDK_TYPE_DRAW_CONTEXT (gdk_draw_context_get_type ())
GDK_AVAILABLE_IN_ALL
GType gdk_draw_context_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GdkDrawContext, gdk_draw_context, GDK, DRAW_CONTEXT)
GDK_AVAILABLE_IN_ALL
GdkDisplay * gdk_draw_context_get_display (GdkDrawContext *context);

View File

@@ -25,12 +25,6 @@
G_BEGIN_DECLS
#define GDK_DRAW_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_DRAW_CONTEXT, GdkDrawContextClass))
#define GDK_IS_DRAW_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_DRAW_CONTEXT))
#define GDK_DRAW_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_DRAW_CONTEXT, GdkDrawContextClass))
typedef struct _GdkDrawContextClass GdkDrawContextClass;
struct _GdkDrawContext
{
GObject parent_instance;

View File

@@ -30,14 +30,9 @@
G_BEGIN_DECLS
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkDrop, g_object_unref)
#define GDK_TYPE_DROP (gdk_drop_get_type ())
#define GDK_DROP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_DROP, GdkDrop))
#define GDK_IS_DROP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_DROP))
GDK_AVAILABLE_IN_ALL
GType gdk_drop_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GdkDrop, gdk_drop, GDK, DROP)
GDK_AVAILABLE_IN_ALL
GdkDisplay * gdk_drop_get_display (GdkDrop *self);

View File

@@ -25,13 +25,6 @@
G_BEGIN_DECLS
#define GDK_DROP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_DROP, GdkDropClass))
#define GDK_IS_DROP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_DROP))
#define GDK_DROP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_DROP, GdkDropClass))
typedef struct _GdkDropClass GdkDropClass;
struct _GdkDrop {
GObject parent_instance;
};

View File

@@ -30,18 +30,15 @@
G_BEGIN_DECLS
#define GDK_TYPE_GL_CONTEXT (gdk_gl_context_get_type ())
#define GDK_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_GL_CONTEXT, GdkGLContext))
#define GDK_IS_GL_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_GL_CONTEXT))
#define GDK_GL_ERROR (gdk_gl_error_quark ())
#define GDK_TYPE_GL_CONTEXT (gdk_gl_context_get_type ())
GDK_AVAILABLE_IN_ALL
GDK_DECLARE_EXPORTED_TYPE (GdkGLContext, gdk_gl_context, GDK, GL_CONTEXT)
GDK_AVAILABLE_IN_ALL
GQuark gdk_gl_error_quark (void);
GDK_AVAILABLE_IN_ALL
GType gdk_gl_context_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GdkDisplay * gdk_gl_context_get_display (GdkGLContext *context);
GDK_AVAILABLE_IN_ALL

View File

@@ -26,12 +26,6 @@
G_BEGIN_DECLS
#define GDK_GL_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_GL_CONTEXT, GdkGLContextClass))
#define GDK_IS_GL_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_GL_CONTEXT))
#define GDK_GL_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_GL_CONTEXT, GdkGLContextClass))
typedef struct _GdkGLContextClass GdkGLContextClass;
struct _GdkGLContext
{
GdkDrawContext parent_instance;

View File

@@ -29,17 +29,8 @@
G_BEGIN_DECLS
#define GDK_TYPE_GL_TEXTURE (gdk_gl_texture_get_type ())
#define GDK_GL_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_GL_TEXTURE, GdkGLTexture))
#define GDK_IS_GL_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_GL_TEXTURE))
typedef struct _GdkGLTexture GdkGLTexture;
typedef struct _GdkGLTextureClass GdkGLTextureClass;
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkGLTexture, g_object_unref)
GDK_AVAILABLE_IN_ALL
GType gdk_gl_texture_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GdkGLTexture, gdk_gl_texture, GDK, GL_TEXTURE)
GDK_AVAILABLE_IN_ALL
GdkTexture * gdk_gl_texture_new (GdkGLContext *context,

View File

@@ -87,18 +87,8 @@ typedef enum {
#endif
#define GDK_TYPE_MEMORY_TEXTURE (gdk_memory_texture_get_type ())
#define GDK_MEMORY_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_MEMORY_TEXTURE, GdkMemoryTexture))
#define GDK_IS_MEMORY_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_MEMORY_TEXTURE))
typedef struct _GdkMemoryTexture GdkMemoryTexture;
typedef struct _GdkMemoryTextureClass GdkMemoryTextureClass;
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkMemoryTexture, g_object_unref)
GDK_AVAILABLE_IN_ALL
GType gdk_memory_texture_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GdkMemoryTexture, gdk_memory_texture, GDK, MEMORY_TEXTURE)
GDK_AVAILABLE_IN_ALL
GdkTexture * gdk_memory_texture_new (int width,

View File

@@ -136,6 +136,16 @@ static double gdk_paintable_default_get_intrinsic_aspect_ratio (GdkPaintable *pa
return (double) width / height;
};
static void
g_value_object_transform_value (const GValue *src_value,
GValue *dest_value)
{
if (src_value->data[0].v_pointer && g_type_is_a (G_OBJECT_TYPE (src_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
dest_value->data[0].v_pointer = g_object_ref (src_value->data[0].v_pointer);
else
dest_value->data[0].v_pointer = NULL;
}
static void
gdk_paintable_default_init (GdkPaintableInterface *iface)
{
@@ -146,6 +156,9 @@ gdk_paintable_default_init (GdkPaintableInterface *iface)
iface->get_intrinsic_height = gdk_paintable_default_get_intrinsic_height;
iface->get_intrinsic_aspect_ratio = gdk_paintable_default_get_intrinsic_aspect_ratio;
g_value_register_transform_func (G_TYPE_OBJECT, GDK_TYPE_PAINTABLE, g_value_object_transform_value);
g_value_register_transform_func (GDK_TYPE_PAINTABLE, G_TYPE_OBJECT, g_value_object_transform_value);
/**
* GdkPaintable::invalidate-contents
* @paintable: a #GdkPaintable

View File

@@ -30,17 +30,8 @@
G_BEGIN_DECLS
#define GDK_TYPE_TEXTURE (gdk_texture_get_type ())
#define GDK_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_TEXTURE, GdkTexture))
#define GDK_IS_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_TEXTURE))
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkTexture, g_object_unref)
typedef struct _GdkTextureClass GdkTextureClass;
GDK_AVAILABLE_IN_ALL
GType gdk_texture_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GdkTexture, gdk_texture, GDK, TEXTURE)
GDK_AVAILABLE_IN_ALL
GdkTexture * gdk_texture_new_for_pixbuf (GdkPixbuf *pixbuf);

View File

@@ -5,10 +5,6 @@
G_BEGIN_DECLS
#define GDK_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_TEXTURE, GdkTextureClass))
#define GDK_IS_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_TEXTURE))
#define GDK_TEXTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_TEXTURE, GdkTextureClass))
struct _GdkTexture
{
GObject parent_instance;

View File

@@ -591,6 +591,26 @@ typedef enum
*/
#define GDK_ACTION_ALL (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)
#define GDK_DECLARE_EXPORTED_TYPE(ModuleObjName, module_obj_name, MODULE, OBJ_NAME) \
GType module_obj_name##_get_type (void) G_GNUC_CONST; \
G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
typedef struct _##ModuleObjName ModuleObjName; \
typedef struct _##ModuleObjName##Class ModuleObjName##Class; \
\
G_DEFINE_AUTOPTR_CLEANUP_FUNC(ModuleObjName, g_object_unref) \
\
G_GNUC_UNUSED static inline ModuleObjName * MODULE##_##OBJ_NAME (gconstpointer ptr) { \
return G_TYPE_CHECK_INSTANCE_CAST (ptr, module_obj_name##_get_type (), ModuleObjName); } \
G_GNUC_UNUSED static inline ModuleObjName##Class * MODULE##_##OBJ_NAME##_CLASS (gconstpointer ptr) { \
return G_TYPE_CHECK_CLASS_CAST (ptr, module_obj_name##_get_type (), ModuleObjName##Class); } \
G_GNUC_UNUSED static inline gboolean MODULE##_IS_##OBJ_NAME (gpointer ptr) { \
return G_TYPE_CHECK_INSTANCE_TYPE (ptr, module_obj_name##_get_type ()); } \
G_GNUC_UNUSED static inline gboolean MODULE##_IS_##OBJ_NAME##_CLASS (gpointer ptr) { \
return G_TYPE_CHECK_CLASS_TYPE (ptr, module_obj_name##_get_type ()); } \
G_GNUC_UNUSED static inline ModuleObjName##Class * MODULE##_##OBJ_NAME##_GET_CLASS (gpointer ptr) { \
return G_TYPE_INSTANCE_GET_CLASS (ptr, module_obj_name##_get_type (), ModuleObjName##Class); } \
G_GNUC_END_IGNORE_DEPRECATIONS
G_END_DECLS
#endif /* __GDK_TYPES_H__ */

View File

@@ -35,17 +35,14 @@
G_BEGIN_DECLS
#define GDK_TYPE_VULKAN_CONTEXT (gdk_vulkan_context_get_type ())
#define GDK_VULKAN_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_VULKAN_CONTEXT, GdkVulkanContext))
#define GDK_IS_VULKAN_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_VULKAN_CONTEXT))
GDK_AVAILABLE_IN_ALL
GDK_DECLARE_EXPORTED_TYPE (GdkVulkanContext, gdk_vulkan_context, GDK, VULKAN_CONTEXT)
#define GDK_VULKAN_ERROR (gdk_vulkan_error_quark ())
GDK_AVAILABLE_IN_ALL
GQuark gdk_vulkan_error_quark (void);
GDK_AVAILABLE_IN_ALL
GType gdk_vulkan_context_get_type (void) G_GNUC_CONST;
#ifndef __GI_SCANNER__
#ifdef GDK_RENDERING_VULKAN

View File

@@ -32,12 +32,6 @@
G_BEGIN_DECLS
#define GDK_VULKAN_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_VULKAN_CONTEXT, GdkVulkanContextClass))
#define GDK_IS_VULKAN_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_VULKAN_CONTEXT))
#define GDK_VULKAN_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_VULKAN_CONTEXT, GdkVulkanContextClass))
typedef struct _GdkVulkanContextClass GdkVulkanContextClass;
struct _GdkVulkanContext
{
GdkDrawContext parent_instance;

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);

View File

@@ -67,7 +67,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkEditable, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkEntry, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkEntryBuffer, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkEntryCompletion, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkEventController, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkExpander, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkFileChooserButton, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkFileChooserDialog, g_object_unref)
@@ -82,16 +81,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkFontChooserDialog, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkFontChooserWidget, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkFrame, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGLArea, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGesture, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureDrag, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureLongPress, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureClick, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGesturePan, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureRotate, g_object_unref)
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 +95,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)
@@ -177,7 +169,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkViewport, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkVolumeButton, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkPaperSize, gtk_paper_size_free)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkRecentInfo, gtk_recent_info_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkSelectionData, gtk_selection_data_free)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTextIter, gtk_text_iter_free)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeIter, gtk_tree_iter_free)

View File

@@ -55,6 +55,7 @@
#include <gtk/gtkbox.h>
#include <gtk/gtkbuildable.h>
#include <gtk/gtkbuilder.h>
#include <gtk/gtkbuilderlistitemfactory.h>
#include <gtk/gtkbuilderscope.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkcalendar.h>
@@ -82,19 +83,25 @@
#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/gtkcustomsorter.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>
#include <gtk/gtkdrawingarea.h>
#include <gtk/gtkdropdown.h>
#include <gtk/gtkeditable.h>
#include <gtk/gtkentry.h>
#include <gtk/gtkentrybuffer.h>
@@ -106,6 +113,7 @@
#include <gtk/gtkeventcontrollermotion.h>
#include <gtk/gtkeventcontrollerscroll.h>
#include <gtk/gtkexpander.h>
#include <gtk/gtkexpression.h>
#include <gtk/gtkfixed.h>
#include <gtk/gtkfixedlayout.h>
#include <gtk/gtkfilechooser.h>
@@ -114,7 +122,9 @@
#include <gtk/gtkfilechoosernative.h>
#include <gtk/gtkfilechooserwidget.h>
#include <gtk/gtkfilefilter.h>
#include <gtk/gtkfilter.h>
#include <gtk/gtkfilterlistmodel.h>
#include <gtk/gtkcustomfilter.h>
#include <gtk/gtkflattenlistmodel.h>
#include <gtk/gtkflowbox.h>
#include <gtk/gtkfontbutton.h>
@@ -122,6 +132,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>
@@ -135,6 +146,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>
@@ -147,9 +159,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>
@@ -164,10 +180,14 @@
#include <gtk/gtkmenutoolbutton.h>
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkmountoperation.h>
#include <gtk/gtkmultifilter.h>
#include <gtk/gtkmultiselection.h>
#include <gtk/gtkmultisorter.h>
#include <gtk/gtknative.h>
#include <gtk/gtknativedialog.h>
#include <gtk/gtknoselection.h>
#include <gtk/gtknotebook.h>
#include <gtk/gtknumericsorter.h>
#include <gtk/gtkorientable.h>
#include <gtk/gtkoverlay.h>
#include <gtk/gtkpadcontroller.h>
@@ -211,9 +231,11 @@
#include <gtk/gtkshortcutsshortcut.h>
#include <gtk/gtkshortcutswindow.h>
#include <gtk/gtkshow.h>
#include <gtk/gtksignallistitemfactory.h>
#include <gtk/gtksingleselection.h>
#include <gtk/gtkslicelistmodel.h>
#include <gtk/gtksnapshot.h>
#include <gtk/gtksorter.h>
#include <gtk/gtksortlistmodel.h>
#include <gtk/gtkstacksidebar.h>
#include <gtk/gtksizegroup.h>
@@ -223,6 +245,8 @@
#include <gtk/gtkstack.h>
#include <gtk/gtkstackswitcher.h>
#include <gtk/gtkstatusbar.h>
#include <gtk/gtkstringfilter.h>
#include <gtk/gtkstringsorter.h>
#include <gtk/gtkstylecontext.h>
#include <gtk/gtkstyleprovider.h>
#include <gtk/gtkswitch.h>
@@ -243,7 +267,9 @@
#include <gtk/gtktooltip.h>
#include <gtk/gtktestutils.h>
#include <gtk/gtktreednd.h>
#include <gtk/gtktreeexpander.h>
#include <gtk/gtktreelistmodel.h>
#include <gtk/gtktreelistrowsorter.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtktreemodelfilter.h>
#include <gtk/gtktreemodelsort.h>

View File

@@ -216,8 +216,10 @@
#include "gtkbuilderprivate.h"
#include "gtkbuildable.h"
#include "gtkbuilderlistitemfactory.h"
#include "gtkbuilderscopeprivate.h"
#include "gtkdebug.h"
#include "gtkexpression.h"
#include "gtkmain.h"
#include "gtkintl.h"
#include "gtkprivate.h"
@@ -520,7 +522,18 @@ gtk_builder_get_parameters (GtkBuilder *builder,
const char *property_name = g_intern_string (prop->pspec->name);
GValue property_value = G_VALUE_INIT;
if (G_IS_PARAM_SPEC_OBJECT (prop->pspec) &&
if (prop->value)
{
g_value_init (&property_value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec));
if (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) == GTK_TYPE_EXPRESSION)
g_value_set_boxed (&property_value, prop->value);
else
{
g_assert_not_reached();
}
}
else if (G_IS_PARAM_SPEC_OBJECT (prop->pspec) &&
(G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_PIXBUF) &&
(G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_TEXTURE) &&
(G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_PAINTABLE) &&
@@ -665,13 +678,43 @@ gtk_builder_take_bindings (GtkBuilder *builder,
for (l = bindings; l; l = l->next)
{
BindingInfo *info = l->data;
info->target = target;
CommonInfo *common_info = l->data;
if (common_info->tag_type == TAG_BINDING)
{
BindingInfo *info = l->data;
info->target = target;
}
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
{
BindingExpressionInfo *info = l->data;
info->target = target;
}
else
{
g_assert_not_reached ();
}
}
priv->bindings = g_slist_concat (priv->bindings, bindings);
}
static void
ensure_special_construct_parameters (GtkBuilder *builder,
GType object_type,
ObjectProperties *construct_parameters)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
GValue value = G_VALUE_INIT;
if (g_type_is_a (object_type, GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
{
g_value_init (&value, GTK_TYPE_BUILDER_SCOPE);
g_value_set_object (&value, priv->scope);
object_properties_add (construct_parameters, "scope", &value);
}
}
GObject *
_gtk_builder_construct (GtkBuilder *builder,
ObjectInfo *info,
@@ -769,6 +812,8 @@ _gtk_builder_construct (GtkBuilder *builder,
}
else
{
ensure_special_construct_parameters (builder, info->type, construct_parameters);
obj = g_object_new_with_properties (info->type,
construct_parameters->len,
(const char **) construct_parameters->names->pdata,
@@ -993,17 +1038,6 @@ gtk_builder_apply_delayed_properties (GtkBuilder *builder,
return result;
}
static inline void
free_binding_info (gpointer data,
gpointer user)
{
BindingInfo *info = data;
g_free (info->source);
g_free (info->source_property);
g_slice_free (BindingInfo, data);
}
static inline gboolean
gtk_builder_create_bindings (GtkBuilder *builder,
GError **error)
@@ -1014,26 +1048,68 @@ gtk_builder_create_bindings (GtkBuilder *builder,
for (l = priv->bindings; l; l = l->next)
{
BindingInfo *info = l->data;
GObject *source;
CommonInfo *common_info = l->data;
if (result)
if (common_info->tag_type == TAG_BINDING)
{
BindingInfo *info = l->data;
GObject *source;
source = gtk_builder_lookup_object (builder, info->source, info->line, info->col, error);
if (source)
g_object_bind_property (source, info->source_property,
info->target, info->target_pspec->name,
info->flags);
else
result = FALSE;
}
error = NULL;
free_binding_info (info, NULL);
_free_binding_info (info, NULL);
}
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
{
BindingExpressionInfo *info = l->data;
GtkExpression *expression;
GObject *object;
if (info->object_name)
{
object = gtk_builder_lookup_object (builder, info->object_name, info->line, info->col, error);
if (object == NULL)
{
error = NULL;
result = FALSE;
}
}
else if (priv->current_object)
{
object = priv->current_object;
}
else
{
object = info->target;
}
if (object)
{
expression = expression_info_construct (builder, info->expr, error);
if (expression == NULL)
{
g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col);
error = NULL;
result = FALSE;
}
else
{
gtk_expression_bind (expression, info->target, info->target_pspec->name, object);
}
}
free_binding_expression_info (info);
}
}
g_slist_free (priv->bindings);
priv->bindings = NULL;
return result;
}
@@ -1195,7 +1271,7 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder,
/**
* gtk_builder_extend_with_template:
* @builder: a #GtkBuilder
* @widget: the widget that is being extended
* @object: the object that is being extended
* @template_type: the type that the template is for
* @buffer: the string to parse
* @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
@@ -1211,7 +1287,7 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder,
*/
gboolean
gtk_builder_extend_with_template (GtkBuilder *builder,
GtkWidget *widget,
GObject *object,
GType template_type,
const gchar *buffer,
gssize length,
@@ -1221,9 +1297,9 @@ gtk_builder_extend_with_template (GtkBuilder *builder,
GError *tmp_error;
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
g_return_val_if_fail (G_IS_OBJECT (object), 0);
g_return_val_if_fail (g_type_name (template_type) != NULL, 0);
g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (widget), template_type), 0);
g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (object), template_type), 0);
g_return_val_if_fail (buffer && buffer[0], 0);
tmp_error = NULL;
@@ -1234,7 +1310,7 @@ gtk_builder_extend_with_template (GtkBuilder *builder,
priv->resource_prefix = NULL;
priv->template_type = template_type;
gtk_builder_expose_object (builder, g_type_name (template_type), G_OBJECT (widget));
gtk_builder_expose_object (builder, g_type_name (template_type), object);
_gtk_builder_parser_parse_buffer (builder, "<input>",
buffer, length,
NULL,

View File

@@ -197,7 +197,7 @@ GClosure * gtk_builder_create_closure (GtkBuilder *builder,
GDK_AVAILABLE_IN_ALL
gboolean gtk_builder_extend_with_template (GtkBuilder *builder,
GtkWidget *widget,
GObject *object,
GType template_type,
const gchar *buffer,
gssize length,

View File

@@ -0,0 +1,401 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkbuilderlistitemfactory.h"
#include "gtkbuilder.h"
#include "gtkbuilderprivate.h"
#include "gtkintl.h"
#include "gtklistitemfactoryprivate.h"
#include "gtklistitemprivate.h"
/**
* SECTION:gtkbuilderlistitemfactory
* @Tiitle: GtkBuilderListItemFactory
* @Short_description: A listitem factory using ui files
*
* #GtkBuilderListItemFactory is a #GtkListItemFactory that creates
* widgets by instantiating #GtkBuilder UI templates. The templates
* must be extending #GtkListItem, and typically use #GtkExpressions
* to obtain data from the items in the model.
*
* Example:
* |[
* <interface>
* <template class="GtkListItem">
* <property name="child">
* <object class="GtkLabel">
* <property name="xalign">0</property>
* <binding name="label">
* <lookup name="name" type="SettingsKey">
* <lookup name="item">GtkListItem</lookup>
* </lookup>
* </binding>
* </object>
* </property>
* </template>
* </interface>
* ]|
*/
struct _GtkBuilderListItemFactory
{
GtkListItemFactory parent_instance;
GtkBuilderScope *scope;
GBytes *bytes;
GBytes *data;
char *resource;
};
struct _GtkBuilderListItemFactoryClass
{
GtkListItemFactoryClass parent_class;
};
enum {
PROP_0,
PROP_BYTES,
PROP_RESOURCE,
PROP_SCOPE,
N_PROPS
};
G_DEFINE_TYPE (GtkBuilderListItemFactory, gtk_builder_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_builder_list_item_factory_setup (GtkListItemFactory *factory,
GtkListItemWidget *widget,
GtkListItem *list_item)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (factory);
GtkBuilder *builder;
GError *error = NULL;
GTK_LIST_ITEM_FACTORY_CLASS (gtk_builder_list_item_factory_parent_class)->setup (factory, widget, list_item);
builder = gtk_builder_new ();
gtk_builder_set_current_object (builder, G_OBJECT (list_item));
if (self->scope)
gtk_builder_set_scope (builder, self->scope);
if (!gtk_builder_extend_with_template (builder, G_OBJECT (list_item), GTK_TYPE_LIST_ITEM,
(const gchar *)g_bytes_get_data (self->data, NULL),
g_bytes_get_size (self->data),
&error))
{
g_critical ("Error building template for list item: %s", error->message);
g_error_free (error);
/* This should never happen, if the template XML cannot be built
* then it is a critical programming error.
*/
g_object_unref (builder);
return;
}
g_object_unref (builder);
}
static void
gtk_builder_list_item_factory_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
switch (property_id)
{
case PROP_BYTES:
g_value_set_boxed (value, self->bytes);
break;
case PROP_RESOURCE:
g_value_set_string (value, self->resource);
break;
case PROP_SCOPE:
g_value_set_object (value, self->scope);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gboolean
gtk_builder_list_item_factory_set_bytes (GtkBuilderListItemFactory *self,
GBytes *bytes)
{
if (bytes == NULL)
return FALSE;
if (self->bytes)
{
g_critical ("Data for GtkBuilderListItemFactory has already been set.");
return FALSE;
}
self->bytes = g_bytes_ref (bytes);
if (!_gtk_buildable_parser_is_precompiled (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)))
{
GError *error = NULL;
GBytes *data;
data = _gtk_buildable_parser_precompile (g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes),
&error);
if (data == NULL)
{
g_warning ("Failed to precompile template for GtkBuilderListItemFactory: %s", error->message);
g_error_free (error);
self->data = g_bytes_ref (bytes);
}
else
{
self->data = data;
}
}
return TRUE;
}
static void
gtk_builder_list_item_factory_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
switch (property_id)
{
case PROP_BYTES:
gtk_builder_list_item_factory_set_bytes (self, g_value_get_boxed (value));
break;
case PROP_RESOURCE:
{
GError *error = NULL;
GBytes *bytes;
const char *resource;
resource = g_value_get_string (value);
if (resource == NULL)
break;
bytes = g_resources_lookup_data (resource, 0, &error);
if (bytes)
{
if (gtk_builder_list_item_factory_set_bytes (self, bytes))
self->resource = g_strdup (resource);
g_bytes_unref (bytes);
}
else
{
g_critical ("Unable to load resource for list item template: %s", error->message);
g_error_free (error);
}
}
break;
case PROP_SCOPE:
self->scope = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_builder_list_item_factory_finalize (GObject *object)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
g_clear_object (&self->scope);
g_bytes_unref (self->bytes);
g_bytes_unref (self->data);
g_free (self->resource);
G_OBJECT_CLASS (gtk_builder_list_item_factory_parent_class)->finalize (object);
}
static void
gtk_builder_list_item_factory_class_init (GtkBuilderListItemFactoryClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
gobject_class->finalize = gtk_builder_list_item_factory_finalize;
gobject_class->get_property = gtk_builder_list_item_factory_get_property;
gobject_class->set_property = gtk_builder_list_item_factory_set_property;
factory_class->setup = gtk_builder_list_item_factory_setup;
/**
* GtkBuilderListItemFactory:bytes:
*
* bytes containing the UI definition
*/
properties[PROP_BYTES] =
g_param_spec_boxed ("bytes",
P_("Bytes"),
P_("bytes containing the UI definition"),
G_TYPE_BYTES,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
/**
* GtkBuilderListItemFactory:resource:
*
* resource containing the UI definition
*/
properties[PROP_RESOURCE] =
g_param_spec_string ("resource",
P_("Resource"),
P_("resource containing the UI definition"),
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
/**
* GtkBuilderListItemFactory:scope:
*
* scope to use when instantiating listitems
*/
properties[PROP_SCOPE] =
g_param_spec_object ("scope",
P_("Scope"),
P_("scope to use when instantiating listitems"),
GTK_TYPE_BUILDER_SCOPE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_builder_list_item_factory_init (GtkBuilderListItemFactory *self)
{
}
/**
* gtk_builder_list_item_factory_new_from_bytes:
* @scope: (nullable) (transfer none): A scope to use when instantiating
* @bytes: the bytes containing the ui file to instantiate
*
* Creates s new #GtkBuilderListItemFactory that instantiates widgets
* using @bytes as the data to pass to #GtkBuilder.
*
* Returns: a new #GtkBuilderListItemFactory
**/
GtkListItemFactory *
gtk_builder_list_item_factory_new_from_bytes (GtkBuilderScope *scope,
GBytes *bytes)
{
g_return_val_if_fail (bytes != NULL, NULL);
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
"bytes", bytes,
"scope", scope,
NULL);
}
/**
* gtk_builder_list_item_factory_new_from_resource:
* @scope: (nullable) (transfer none): A scope to use when instantiating
* @resource_path: valid path to a resource that contains the data
*
* Creates s new #GtkBuilderListItemFactory that instantiates widgets
* using data read from the given @resource_path to pass to #GtkBuilder.
*
* Returns: a new #GtkBuilderListItemFactory
**/
GtkListItemFactory *
gtk_builder_list_item_factory_new_from_resource (GtkBuilderScope *scope,
const char *resource_path)
{
g_return_val_if_fail (scope == NULL || GTK_IS_BUILDER_SCOPE (scope), NULL);
g_return_val_if_fail (resource_path != NULL, NULL);
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
"resource", resource_path,
"scope", scope,
NULL);
}
/**
* gtk_builder_list_item_factory_get_bytes:
* @self: a #GtkBuilderListItemFactory
*
* Gets the data used as the #GtkBuilder UI template for constructing
* listitems.
*
* Returns: (transfer none): The GtkBuilder data
*
**/
GBytes *
gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self)
{
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
return self->bytes;
}
/**
* gtk_builder_list_item_factory_get_resource:
* @self: a #GtkBuilderListItemFactory
*
* If the data references a resource, gets the path of that resource.
*
* Returns: (transfer none) (nullable): The path to the resource or %NULL
* if none
**/
const char *
gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self)
{
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
return self->resource;
}
/**
* gtk_builder_list_item_factory_get_scope:
* @self: a #GtkBuilderListItemFactory
*
* Gets the scope used when constructing listitems.
*
* Returns: (transfer none) (nullable): The scope used when constructing listitems
**/
GtkBuilderScope *
gtk_builder_list_item_factory_get_scope (GtkBuilderListItemFactory *self)
{
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
return self->scope;
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_BUILDER_LIST_ITEM_FACTORY_H__
#define __GTK_BUILDER_LIST_ITEM_FACTORY_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtklistitemfactory.h>
G_BEGIN_DECLS
#define GTK_TYPE_BUILDER_LIST_ITEM_FACTORY (gtk_builder_list_item_factory_get_type ())
#define GTK_BUILDER_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactory))
#define GTK_BUILDER_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactoryClass))
#define GTK_IS_BUILDER_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
#define GTK_IS_BUILDER_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
#define GTK_BUILDER_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactoryClass))
typedef struct _GtkBuilderListItemFactory GtkBuilderListItemFactory;
typedef struct _GtkBuilderListItemFactoryClass GtkBuilderListItemFactoryClass;
GDK_AVAILABLE_IN_ALL
GType gtk_builder_list_item_factory_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkListItemFactory * gtk_builder_list_item_factory_new_from_bytes (GtkBuilderScope *scope,
GBytes *bytes);
GDK_AVAILABLE_IN_ALL
GtkListItemFactory * gtk_builder_list_item_factory_new_from_resource (GtkBuilderScope *scope,
const char *resource_path);
GDK_AVAILABLE_IN_ALL
GBytes * gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
const char * gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
GtkBuilderScope * gtk_builder_list_item_factory_get_scope (GtkBuilderListItemFactory *self) G_GNUC_PURE;
G_END_DECLS
#endif /* __GTK_BUILDER_LIST_ITEM_FACTORY_H__ */

View File

@@ -913,7 +913,8 @@ parse_property (ParserData *data,
{
BindingInfo *binfo;
binfo = g_slice_new (BindingInfo);
binfo = g_slice_new0 (BindingInfo);
binfo->tag_type = TAG_BINDING;
binfo->target = NULL;
binfo->target_pspec = pspec;
binfo->source = g_strdup (bind_source);
@@ -932,7 +933,7 @@ parse_property (ParserData *data,
return;
}
info = g_slice_new (PropertyInfo);
info = g_slice_new0 (PropertyInfo);
info->tag_type = TAG_PROPERTY;
info->pspec = pspec;
info->text = g_string_new ("");
@@ -945,14 +946,504 @@ parse_property (ParserData *data,
state_push (data, info);
}
static void
parse_binding (ParserData *data,
const gchar *element_name,
const gchar **names,
const gchar **values,
GError **error)
{
BindingExpressionInfo *info;
const char *name = NULL;
const char *object_name = NULL;
ObjectInfo *object_info;
GParamSpec *pspec = NULL;
object_info = state_peek_info (data, ObjectInfo);
if (!object_info ||
!(object_info->tag_type == TAG_OBJECT ||
object_info->tag_type == TAG_TEMPLATE))
{
error_invalid_tag (data, element_name, NULL, error);
return;
}
if (!g_markup_collect_attributes (element_name, names, values, error,
G_MARKUP_COLLECT_STRING, "name", &name,
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", &object_name,
G_MARKUP_COLLECT_INVALID))
{
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
pspec = g_object_class_find_property (object_info->oclass, name);
if (!pspec)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_PROPERTY,
"Invalid property: %s.%s",
g_type_name (object_info->type), name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
else if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_PROPERTY,
"%s.%s is a construct-only property",
g_type_name (object_info->type), name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
else if (!(pspec->flags & G_PARAM_WRITABLE))
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_PROPERTY,
"%s.%s is a non-writable property",
g_type_name (object_info->type), name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
info = g_slice_new0 (BindingExpressionInfo);
info->tag_type = TAG_BINDING_EXPRESSION;
info->target = NULL;
info->target_pspec = pspec;
info->object_name = g_strdup (object_name);
gtk_buildable_parse_context_get_position (&data->ctx, &info->line, &info->col);
state_push (data, info);
}
static void
free_property_info (PropertyInfo *info)
{
if (info->value)
{
if (G_PARAM_SPEC_VALUE_TYPE (info->pspec) == GTK_TYPE_EXPRESSION)
gtk_expression_unref (info->value);
else
g_assert_not_reached();
}
g_string_free (info->text, TRUE);
g_free (info->context);
g_slice_free (PropertyInfo, info);
}
static void
free_expression_info (ExpressionInfo *info)
{
switch (info->expression_type)
{
case EXPRESSION_EXPRESSION:
g_clear_pointer (&info->expression, gtk_expression_unref);
break;
case EXPRESSION_CONSTANT:
g_string_free (info->constant.text, TRUE);
break;
case EXPRESSION_CLOSURE:
g_free (info->closure.function_name);
g_free (info->closure.object_name);
g_slist_free_full (info->closure.params, (GDestroyNotify) free_expression_info);
break;
case EXPRESSION_PROPERTY:
g_clear_pointer (&info->property.expression, free_expression_info);
g_free (info->property.property_name);
break;
default:
g_assert_not_reached ();
break;
}
g_slice_free (ExpressionInfo, info);
}
static gboolean
check_expression_parent (ParserData *data)
{
CommonInfo *common_info = state_peek_info (data, CommonInfo);
if (common_info == NULL)
return FALSE;
if (common_info->tag_type == TAG_PROPERTY)
{
PropertyInfo *prop_info = (PropertyInfo *) common_info;
return G_PARAM_SPEC_VALUE_TYPE (prop_info->pspec) == GTK_TYPE_EXPRESSION;
}
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
{
BindingExpressionInfo *expr_info = (BindingExpressionInfo *) common_info;
return expr_info->expr == NULL;
}
else if (common_info->tag_type == TAG_EXPRESSION)
{
ExpressionInfo *expr_info = (ExpressionInfo *) common_info;
switch (expr_info->expression_type)
{
case EXPRESSION_CLOSURE:
return TRUE;
case EXPRESSION_CONSTANT:
return FALSE;
case EXPRESSION_PROPERTY:
return expr_info->property.expression == NULL;
case EXPRESSION_EXPRESSION:
default:
g_assert_not_reached ();
return FALSE;
}
}
return FALSE;
}
static void
parse_constant_expression (ParserData *data,
const gchar *element_name,
const gchar **names,
const gchar **values,
GError **error)
{
ExpressionInfo *info;
const char *type_name = NULL;
GType type;
if (!check_expression_parent (data))
{
error_invalid_tag (data, element_name, NULL, error);
return;
}
if (!g_markup_collect_attributes (element_name, names, values, error,
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "type", &type_name,
G_MARKUP_COLLECT_INVALID))
{
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
if (type_name == NULL)
type = G_TYPE_INVALID;
else
{
type = gtk_builder_get_type_from_name (data->builder, type_name);
if (type == G_TYPE_INVALID)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_VALUE,
"Invalid type '%s'", type_name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
}
info = g_slice_new0 (ExpressionInfo);
info->tag_type = TAG_EXPRESSION;
info->expression_type = EXPRESSION_CONSTANT;
info->constant.type = type;
info->constant.text = g_string_new (NULL);
state_push (data, info);
}
static void
parse_closure_expression (ParserData *data,
const gchar *element_name,
const gchar **names,
const gchar **values,
GError **error)
{
ExpressionInfo *info;
const char *type_name;
const char *function_name;
const char *object_name = NULL;
gboolean swapped = -1;
GType type;
if (!check_expression_parent (data))
{
error_invalid_tag (data, element_name, NULL, error);
return;
}
if (!g_markup_collect_attributes (element_name, names, values, error,
G_MARKUP_COLLECT_STRING, "type", &type_name,
G_MARKUP_COLLECT_STRING, "function", &function_name,
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", &object_name,
G_MARKUP_COLLECT_TRISTATE|G_MARKUP_COLLECT_OPTIONAL, "swapped", &swapped,
G_MARKUP_COLLECT_INVALID))
{
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
type = gtk_builder_get_type_from_name (data->builder, type_name);
if (type == G_TYPE_INVALID)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_VALUE,
"Invalid type '%s'", type_name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
/* Swapped defaults to FALSE except when object is set */
if (swapped == -1)
{
if (object_name)
swapped = TRUE;
else
swapped = FALSE;
}
info = g_slice_new0 (ExpressionInfo);
info->tag_type = TAG_EXPRESSION;
info->expression_type = EXPRESSION_CLOSURE;
info->closure.type = type;
info->closure.swapped = swapped;
info->closure.function_name = g_strdup (function_name);
info->closure.object_name = g_strdup (object_name);
state_push (data, info);
}
static void
parse_lookup_expression (ParserData *data,
const gchar *element_name,
const gchar **names,
const gchar **values,
GError **error)
{
ExpressionInfo *info;
const char *property_name;
const char *type_name = NULL;
GType type;
if (!check_expression_parent (data))
{
error_invalid_tag (data, element_name, NULL, error);
return;
}
if (!g_markup_collect_attributes (element_name, names, values, error,
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "type", &type_name,
G_MARKUP_COLLECT_STRING, "name", &property_name,
G_MARKUP_COLLECT_INVALID))
{
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
if (type_name == NULL)
{
type = G_TYPE_INVALID;
}
else
{
type = gtk_builder_get_type_from_name (data->builder, type_name);
if (type == G_TYPE_INVALID)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_VALUE,
"Invalid type '%s'", type_name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
}
info = g_slice_new0 (ExpressionInfo);
info->tag_type = TAG_EXPRESSION;
info->expression_type = EXPRESSION_PROPERTY;
info->property.this_type = type;
info->property.property_name = g_strdup (property_name);
state_push (data, info);
}
GtkExpression *
expression_info_construct (GtkBuilder *builder,
ExpressionInfo *info,
GError **error)
{
switch (info->expression_type)
{
case EXPRESSION_EXPRESSION:
break;
case EXPRESSION_CONSTANT:
{
GtkExpression *expr;
if (info->constant.type == G_TYPE_INVALID)
{
GObject *o = gtk_builder_lookup_object (builder, info->constant.text->str, 0, 0, error);
if (o == NULL)
return NULL;
expr = gtk_object_expression_new (o);
}
else
{
GValue value = G_VALUE_INIT;
if (!gtk_builder_value_from_string_type (builder,
info->constant.type,
info->constant.text->str,
&value,
error))
return NULL;
if (G_VALUE_HOLDS_OBJECT (&value))
expr = gtk_object_expression_new (g_value_get_object (&value));
else
expr = gtk_constant_expression_new_for_value (&value);
g_value_unset (&value);
}
g_string_free (info->constant.text, TRUE);
info->expression_type = EXPRESSION_EXPRESSION;
info->expression = expr;
}
break;
case EXPRESSION_CLOSURE:
{
GObject *object;
GClosure *closure;
guint i, n_params;
GtkExpression **params;
GtkExpression *expression;
GSList *l;
if (info->closure.object_name)
{
object = gtk_builder_lookup_object (builder, info->closure.object_name, 0, 0, error);
if (object == NULL)
return NULL;
}
else
{
object = NULL;
}
closure = gtk_builder_create_closure (builder,
info->closure.function_name,
info->closure.swapped,
object,
error);
if (closure == NULL)
return NULL;
n_params = g_slist_length (info->closure.params);
params = g_newa (GtkExpression *, n_params);
i = n_params;
for (l = info->closure.params; l; l = l->next)
{
params[--i] = expression_info_construct (builder, l->data, error);
if (params[i] == NULL)
return NULL;
}
expression = gtk_closure_expression_new (info->closure.type, closure, n_params, params);
g_free (info->closure.function_name);
g_free (info->closure.object_name);
g_slist_free_full (info->closure.params, (GDestroyNotify) free_expression_info);
info->expression_type = EXPRESSION_EXPRESSION;
info->expression = expression;
}
break;
case EXPRESSION_PROPERTY:
{
GtkExpression *expression;
GType type;
GParamSpec *pspec;
if (info->property.expression)
{
expression = expression_info_construct (builder, info->property.expression, error);
if (expression == NULL)
return NULL;
free_expression_info (info->property.expression);
info->property.expression = NULL;
}
else
expression = NULL;
if (info->property.this_type != G_TYPE_INVALID)
type = info->property.this_type;
else if (expression != NULL)
type = gtk_expression_get_value_type (expression);
else
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
"Lookups require a type attribute if they don't have an expression.");
return NULL;
}
if (g_type_is_a (type, G_TYPE_OBJECT))
{
GObjectClass *class = g_type_class_ref (type);
pspec = g_object_class_find_property (class, info->property.property_name);
g_type_class_unref (class);
}
else if (g_type_is_a (type, G_TYPE_INTERFACE))
{
GTypeInterface *iface = g_type_default_interface_ref (type);
pspec = g_object_interface_find_property (iface, info->property.property_name);
g_type_default_interface_unref (iface);
}
else
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
"Type `%s` does not support properties",
g_type_name (type));
return NULL;
}
if (pspec == NULL)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
"Type `%s` does not have a property name `%s`",
g_type_name (type), info->property.property_name);
return NULL;
}
expression = gtk_property_expression_new_for_pspec (expression, pspec);
g_free (info->property.property_name);
info->expression_type = EXPRESSION_EXPRESSION;
info->expression = expression;
}
break;
default:
g_return_val_if_reached (NULL);
}
return gtk_expression_ref (info->expression);
}
static void
parse_signal (ParserData *data,
const gchar *element_name,
@@ -1037,6 +1528,24 @@ _free_signal_info (SignalInfo *info,
g_slice_free (SignalInfo, info);
}
void
_free_binding_info (BindingInfo *info,
gpointer user)
{
g_free (info->source);
g_free (info->source_property);
g_slice_free (BindingInfo, info);
}
void
free_binding_expression_info (BindingExpressionInfo *info)
{
if (info->expr)
free_expression_info (info->expr);
g_free (info->object_name);
g_slice_free (BindingExpressionInfo, info);
}
static void
free_requires_info (RequiresInfo *info,
gpointer user_data)
@@ -1277,6 +1786,8 @@ start_element (GtkBuildableParseContext *context,
}
else if (strcmp (element_name, "property") == 0)
parse_property (data, element_name, names, values, error);
else if (strcmp (element_name, "binding") == 0)
parse_binding (data, element_name, names, values, error);
else if (strcmp (element_name, "child") == 0)
parse_child (data, element_name, names, values, error);
else if (strcmp (element_name, "signal") == 0)
@@ -1287,6 +1798,12 @@ start_element (GtkBuildableParseContext *context,
parse_requires (data, element_name, names, values, error);
else if (strcmp (element_name, "interface") == 0)
parse_interface (data, element_name, names, values, error);
else if (strcmp (element_name, "constant") == 0)
parse_constant_expression (data, element_name, names, values, error);
else if (strcmp (element_name, "closure") == 0)
parse_closure_expression (data, element_name, names, values, error);
else if (strcmp (element_name, "lookup") == 0)
parse_lookup_expression (data, element_name, names, values, error);
else if (strcmp (element_name, "menu") == 0)
_gtk_builder_menu_start (data, element_name, names, values, error);
else if (strcmp (element_name, "placeholder") == 0)
@@ -1362,6 +1879,30 @@ end_element (GtkBuildableParseContext *context,
else
g_assert_not_reached ();
}
else if (strcmp (element_name, "binding") == 0)
{
BindingExpressionInfo *binfo = state_pop_info (data, BindingExpressionInfo);
CommonInfo *info = state_peek_info (data, CommonInfo);
g_assert (info != NULL);
if (binfo->expr == NULL)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_TAG,
"Binding tag requires an expression");
free_binding_expression_info (binfo);
}
else if (info->tag_type == TAG_OBJECT ||
info->tag_type == TAG_TEMPLATE)
{
ObjectInfo *object_info = (ObjectInfo*)info;
object_info->bindings = g_slist_prepend (object_info->bindings, binfo);
}
else
g_assert_not_reached ();
}
else if (strcmp (element_name, "object") == 0 ||
strcmp (element_name, "template") == 0)
{
@@ -1422,6 +1963,49 @@ end_element (GtkBuildableParseContext *context,
signal_info->object_name = g_strdup (object_info->id);
object_info->signals = g_slist_prepend (object_info->signals, signal_info);
}
else if (strcmp (element_name, "constant") == 0 ||
strcmp (element_name, "closure") == 0 ||
strcmp (element_name, "lookup") == 0)
{
ExpressionInfo *expression_info = state_pop_info (data, ExpressionInfo);
CommonInfo *parent_info = state_peek_info (data, CommonInfo);
if (parent_info->tag_type == TAG_BINDING_EXPRESSION)
{
BindingExpressionInfo *expr_info = (BindingExpressionInfo *) parent_info;
expr_info->expr = expression_info;
}
else if (parent_info->tag_type == TAG_PROPERTY)
{
PropertyInfo *prop_info = (PropertyInfo *) parent_info;
prop_info->value = expression_info_construct (data->builder, expression_info, error);
}
else if (parent_info->tag_type == TAG_EXPRESSION)
{
ExpressionInfo *expr_info = (ExpressionInfo *) parent_info;
switch (expr_info->expression_type)
{
case EXPRESSION_CLOSURE:
expr_info->closure.params = g_slist_prepend (expr_info->closure.params, expression_info);
break;
case EXPRESSION_PROPERTY:
expr_info->property.expression = expression_info;
break;
case EXPRESSION_EXPRESSION:
case EXPRESSION_CONSTANT:
default:
g_assert_not_reached ();
break;
}
}
else
{
g_assert_not_reached ();
}
}
else if (strcmp (element_name, "requires") == 0)
{
RequiresInfo *req_info = state_pop_info (data, RequiresInfo);
@@ -1502,6 +2086,33 @@ text (GtkBuildableParseContext *context,
g_string_append_len (prop_info->text, text, text_len);
}
else if (strcmp (gtk_buildable_parse_context_get_element (context), "constant") == 0)
{
ExpressionInfo *expr_info = (ExpressionInfo *) info;
g_string_append_len (expr_info->constant.text, text, text_len);
}
else if (strcmp (gtk_buildable_parse_context_get_element (context), "lookup") == 0)
{
ExpressionInfo *expr_info = (ExpressionInfo *) info;
while (g_ascii_isspace (*text) && text_len > 0)
{
text++;
text_len--;
}
while (text_len > 0 && g_ascii_isspace (text[text_len - 1]))
text_len--;
if (expr_info->property.expression == NULL && text_len > 0)
{
ExpressionInfo *constant = g_slice_new0 (ExpressionInfo);
constant->tag_type = TAG_EXPRESSION;
constant->expression_type = EXPRESSION_CONSTANT;
constant->constant.type = G_TYPE_INVALID;
constant->constant.text = g_string_new_len (text, text_len);
expr_info->property.expression = constant;
}
}
}
static void
@@ -1516,6 +2127,12 @@ free_info (CommonInfo *info)
case TAG_CHILD:
free_child_info ((ChildInfo *)info);
break;
case TAG_BINDING:
_free_binding_info ((BindingInfo *)info, NULL);
break;
case TAG_BINDING_EXPRESSION:
free_binding_expression_info ((BindingExpressionInfo *) info);
break;
case TAG_PROPERTY:
free_property_info ((PropertyInfo *)info);
break;
@@ -1525,6 +2142,9 @@ free_info (CommonInfo *info)
case TAG_REQUIRES:
free_requires_info ((RequiresInfo *)info, NULL);
break;
case TAG_EXPRESSION:
free_expression_info ((ExpressionInfo *)info);
break;
default:
g_assert_not_reached ();
}

View File

@@ -21,16 +21,19 @@
#include "gtkbuilder.h"
#include "gtkbuildable.h"
#include "gtkexpression.h"
enum {
TAG_PROPERTY,
TAG_MENU,
TAG_BINDING,
TAG_BINDING_EXPRESSION,
TAG_REQUIRES,
TAG_OBJECT,
TAG_CHILD,
TAG_SIGNAL,
TAG_INTERFACE,
TAG_TEMPLATE,
TAG_EXPRESSION,
};
typedef struct {
@@ -64,6 +67,7 @@ typedef struct {
typedef struct {
guint tag_type;
GParamSpec *pspec;
gpointer value;
GString *text;
gboolean translatable:1;
gboolean bound:1;
@@ -72,6 +76,36 @@ typedef struct {
gint col;
} PropertyInfo;
typedef struct _ExpressionInfo ExpressionInfo;
struct _ExpressionInfo {
guint tag_type;
enum {
EXPRESSION_EXPRESSION,
EXPRESSION_CONSTANT,
EXPRESSION_CLOSURE,
EXPRESSION_PROPERTY
} expression_type;
union {
GtkExpression *expression;
struct {
GType type;
GString *text;
} constant;
struct {
GType type;
char *function_name;
char *object_name;
gboolean swapped;
GSList *params;
} closure;
struct {
GType this_type;
char *property_name;
ExpressionInfo *expression;
} property;
};
};
typedef struct {
guint tag_type;
gchar *object_name;
@@ -84,6 +118,7 @@ typedef struct {
typedef struct
{
guint tag_type;
GObject *target;
GParamSpec *target_pspec;
gchar *source;
@@ -93,6 +128,17 @@ typedef struct
gint col;
} BindingInfo;
typedef struct
{
guint tag_type;
GObject *target;
GParamSpec *target_pspec;
char *object_name;
ExpressionInfo *expr;
gint line;
gint col;
} BindingExpressionInfo;
typedef struct {
guint tag_type;
gchar *library;
@@ -179,6 +225,12 @@ gboolean _gtk_builder_finish (GtkBuilder *builder,
GError **error);
void _free_signal_info (SignalInfo *info,
gpointer user_data);
void _free_binding_info (BindingInfo *info,
gpointer user_data);
void free_binding_expression_info (BindingExpressionInfo *info);
GtkExpression * expression_info_construct (GtkBuilder *builder,
ExpressionInfo *info,
GError **error);
/* Internal API which might be made public at some point */
gboolean _gtk_builder_boolean_from_string (const gchar *string,

View File

@@ -0,0 +1,148 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcolumnlistitemfactoryprivate.h"
#include "gtkboxlayout.h"
#include "gtkcolumnviewcolumnprivate.h"
#include "gtkcolumnviewlayoutprivate.h"
#include "gtklistitemfactoryprivate.h"
#include "gtklistitemprivate.h"
struct _GtkColumnListItemFactory
{
GtkListItemFactory parent_instance;
GtkColumnView *view; /* no reference, the view references us */
};
struct _GtkColumnListItemFactoryClass
{
GtkListItemFactoryClass parent_class;
};
G_DEFINE_TYPE (GtkColumnListItemFactory, gtk_column_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
static void
gtk_column_list_item_factory_setup (GtkListItemFactory *factory,
GtkListItemWidget *widget,
GtkListItem *list_item)
{
GtkColumnListItemFactory *self = GTK_COLUMN_LIST_ITEM_FACTORY (factory);
GListModel *columns;
guint i;
/* FIXME: evil */
gtk_widget_set_layout_manager (GTK_WIDGET (widget),
gtk_column_view_layout_new (self->view));
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->setup (factory, widget, list_item);
columns = gtk_column_view_get_columns (self->view);
for (i = 0; i < g_list_model_get_n_items (columns); i++)
{
GtkColumnViewColumn *column = g_list_model_get_item (columns, i);
gtk_column_list_item_factory_add_column (self,
list_item->owner,
column,
FALSE);
}
}
static void
gtk_column_list_item_factory_teardown (GtkListItemFactory *factory,
GtkListItemWidget *widget,
GtkListItem *list_item)
{
GtkWidget *child;
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->teardown (factory, widget, list_item);
while ((child = gtk_widget_get_first_child (GTK_WIDGET (widget))))
{
gtk_list_item_widget_remove_child (GTK_LIST_ITEM_WIDGET (widget), child);
}
}
static void
gtk_column_list_item_factory_update (GtkListItemFactory *factory,
GtkListItemWidget *widget,
GtkListItem *list_item,
guint position,
gpointer item,
gboolean selected)
{
GtkWidget *child;
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->update (factory, widget, list_item, position, item, selected);
for (child = gtk_widget_get_first_child (GTK_WIDGET (widget));
child;
child = gtk_widget_get_next_sibling (child))
{
gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (child), position, item, selected);
}
}
static void
gtk_column_list_item_factory_class_init (GtkColumnListItemFactoryClass *klass)
{
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
factory_class->setup = gtk_column_list_item_factory_setup;
factory_class->teardown = gtk_column_list_item_factory_teardown;
factory_class->update = gtk_column_list_item_factory_update;
}
static void
gtk_column_list_item_factory_init (GtkColumnListItemFactory *self)
{
}
GtkColumnListItemFactory *
gtk_column_list_item_factory_new (GtkColumnView *view)
{
GtkColumnListItemFactory *result;
result = g_object_new (GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, NULL);
result->view = view;
return result;
}
void
gtk_column_list_item_factory_add_column (GtkColumnListItemFactory *factory,
GtkListItemWidget *list_item,
GtkColumnViewColumn *column,
gboolean check_bind)
{
GtkWidget *cell;
cell = gtk_column_view_cell_new (column);
gtk_list_item_widget_add_child (GTK_LIST_ITEM_WIDGET (list_item), GTK_WIDGET (cell));
gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (cell),
gtk_list_item_widget_get_position (list_item),
gtk_list_item_widget_get_item (list_item),
gtk_list_item_widget_get_selected (list_item));
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COLUMN_LIST_ITEM_FACTORY_H__
#define __GTK_COLUMN_LIST_ITEM_FACTORY_H__
#include <gtk/gtklistitemwidgetprivate.h>
#include <gtk/gtkcolumnview.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLUMN_LIST_ITEM_FACTORY (gtk_column_list_item_factory_get_type ())
#define GTK_COLUMN_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactory))
#define GTK_COLUMN_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactoryClass))
#define GTK_IS_COLUMN_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY))
#define GTK_IS_COLUMN_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY))
#define GTK_COLUMN_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactoryClass))
/**
* GtkColumnListItemFactory:
*
* The object for the #GtkColumnListItemFactory.
**/
typedef struct _GtkColumnListItemFactory GtkColumnListItemFactory;
typedef struct _GtkColumnListItemFactoryClass GtkColumnListItemFactoryClass;
GType gtk_column_list_item_factory_get_type (void) G_GNUC_CONST;
GtkColumnListItemFactory *
gtk_column_list_item_factory_new (GtkColumnView *view);
void gtk_column_list_item_factory_add_column (GtkColumnListItemFactory *factory,
GtkListItemWidget *list_item,
GtkColumnViewColumn *column,
gboolean check_bind);
void gtk_column_list_item_factory_remove_column
(GtkColumnListItemFactory *factory,
GtkListItemWidget *list_item,
guint col_pos,
GtkColumnViewColumn *column);
G_END_DECLS
#endif /* __GTK_COLUMN_LIST_ITEM_FACTORY_H__ */

1445
gtk/gtkcolumnview.c Normal file

File diff suppressed because it is too large Load Diff

114
gtk/gtkcolumnview.h Normal file
View File

@@ -0,0 +1,114 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COLUMN_VIEW_H__
#define __GTK_COLUMN_VIEW_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtktypes.h>
#include <gtk/gtksortlistmodel.h>
#include <gtk/gtksorter.h>
#include <gtk/gtkfilter.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLUMN_VIEW (gtk_column_view_get_type ())
#define GTK_COLUMN_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW, GtkColumnView))
#define GTK_COLUMN_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW, GtkColumnViewClass))
#define GTK_IS_COLUMN_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW))
#define GTK_IS_COLUMN_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW))
#define GTK_COLUMN_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW, GtkColumnViewClass))
/**
* GtkColumnView:
*
* GtkColumnView is the simple list implementation for GTK's list widgets.
*/
typedef struct _GtkColumnView GtkColumnView;
typedef struct _GtkColumnViewClass GtkColumnViewClass;
/* forward declaration */
typedef struct _GtkColumnViewColumn GtkColumnViewColumn;
GDK_AVAILABLE_IN_ALL
GType gtk_column_view_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_column_view_new (void);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_column_view_get_columns (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_append_column (GtkColumnView *self,
GtkColumnViewColumn *column);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_remove_column (GtkColumnView *self,
GtkColumnViewColumn *column);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_insert_column (GtkColumnView *self,
guint position,
GtkColumnViewColumn *column);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_column_view_get_model (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_set_model (GtkColumnView *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_get_show_separators (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_set_show_separators (GtkColumnView *self,
gboolean show_separators);
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_column_view_get_sorter (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_sort_by_column (GtkColumnView *self,
GtkColumnViewColumn *column,
GtkSortType direction);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_set_single_click_activate (GtkColumnView *self,
gboolean single_click_activate);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_get_single_click_activate (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_set_enable_rubberband (GtkColumnView *self,
gboolean enable_rubberband);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_get_enable_rubberband (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_set_search_filter (GtkColumnView *self,
GtkFilter *filter);
GDK_AVAILABLE_IN_ALL
GtkFilter * gtk_column_view_get_search_filter (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_select_next_match (GtkColumnView *self,
gboolean forward);
G_END_DECLS
#endif /* __GTK_COLUMN_VIEW_H__ */

199
gtk/gtkcolumnviewcell.c Normal file
View File

@@ -0,0 +1,199 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcolumnviewcellprivate.h"
#include "gtkcolumnviewcolumnprivate.h"
#include "gtkintl.h"
#include "gtklistitemwidgetprivate.h"
#include "gtkwidgetprivate.h"
struct _GtkColumnViewCell
{
GtkListItemWidget parent_instance;
GtkColumnViewColumn *column;
/* This list isn't sorted - next/prev refer to list elements, not rows in the list */
GtkColumnViewCell *next_cell;
GtkColumnViewCell *prev_cell;
};
struct _GtkColumnViewCellClass
{
GtkListItemWidgetClass parent_class;
};
G_DEFINE_TYPE (GtkColumnViewCell, gtk_column_view_cell, GTK_TYPE_LIST_ITEM_WIDGET)
static void
gtk_column_view_cell_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkColumnViewCell *cell = GTK_COLUMN_VIEW_CELL (widget);
GtkWidget *child = gtk_widget_get_first_child (widget);
if (child)
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
int fixed_width = gtk_column_view_column_get_fixed_width (cell->column);
if (fixed_width > -1)
*minimum = *natural = fixed_width;
}
}
static void
gtk_column_view_cell_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkWidget *child = gtk_widget_get_first_child (widget);
if (child)
gtk_widget_allocate (child, width, height, baseline, NULL);
}
static void
gtk_column_view_cell_root (GtkWidget *widget)
{
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget);
GTK_WIDGET_CLASS (gtk_column_view_cell_parent_class)->root (widget);
self->next_cell = gtk_column_view_column_get_first_cell (self->column);
if (self->next_cell)
self->next_cell->prev_cell = self;
gtk_column_view_column_add_cell (self->column, self);
}
static void
gtk_column_view_cell_unroot (GtkWidget *widget)
{
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget);
gtk_column_view_column_remove_cell (self->column, self);
if (self->prev_cell)
self->prev_cell->next_cell = self->next_cell;
if (self->next_cell)
self->next_cell->prev_cell = self->prev_cell;
self->prev_cell = NULL;
self->next_cell = NULL;
GTK_WIDGET_CLASS (gtk_column_view_cell_parent_class)->unroot (widget);
}
static void
gtk_column_view_cell_dispose (GObject *object)
{
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (object);
g_clear_object (&self->column);
G_OBJECT_CLASS (gtk_column_view_cell_parent_class)->dispose (object);
}
static void
gtk_column_view_cell_class_init (GtkColumnViewCellClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
widget_class->root = gtk_column_view_cell_root;
widget_class->unroot = gtk_column_view_cell_unroot;
widget_class->measure = gtk_column_view_cell_measure;
widget_class->size_allocate = gtk_column_view_cell_size_allocate;
gobject_class->dispose = gtk_column_view_cell_dispose;
gtk_widget_class_set_css_name (widget_class, I_("cell"));
}
static void
gtk_column_view_cell_resize_func (GtkWidget *widget)
{
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget);
if (self->column)
gtk_column_view_column_queue_resize (self->column);
}
static void
gtk_column_view_cell_init (GtkColumnViewCell *self)
{
GtkWidget *widget = GTK_WIDGET (self);
gtk_widget_set_can_focus (widget, FALSE);
/* FIXME: Figure out if settting the manager class to INVALID should work */
gtk_widget_set_layout_manager (widget, NULL);
widget->priv->resize_func = gtk_column_view_cell_resize_func;
}
GtkWidget *
gtk_column_view_cell_new (GtkColumnViewColumn *column)
{
GtkColumnViewCell *cell;
cell = g_object_new (GTK_TYPE_COLUMN_VIEW_CELL,
"factory", gtk_column_view_column_get_factory (column),
"visible", gtk_column_view_column_get_visible (column),
NULL);
cell->column = g_object_ref (column);
return GTK_WIDGET (cell);
}
void
gtk_column_view_cell_remove (GtkColumnViewCell *self)
{
GtkWidget *widget = GTK_WIDGET (self);
gtk_list_item_widget_remove_child (GTK_LIST_ITEM_WIDGET (gtk_widget_get_parent (widget)), widget);
}
GtkColumnViewCell *
gtk_column_view_cell_get_next (GtkColumnViewCell *self)
{
return self->next_cell;
}
GtkColumnViewCell *
gtk_column_view_cell_get_prev (GtkColumnViewCell *self)
{
return self->prev_cell;
}
GtkColumnViewColumn *
gtk_column_view_cell_get_column (GtkColumnViewCell *self)
{
return self->column;
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COLUMN_VIEW_CELL_PRIVATE_H__
#define __GTK_COLUMN_VIEW_CELL_PRIVATE_H__
#include "gtkcolumnviewcolumn.h"
G_BEGIN_DECLS
#define GTK_TYPE_COLUMN_VIEW_CELL (gtk_column_view_cell_get_type ())
#define GTK_COLUMN_VIEW_CELL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCell))
#define GTK_COLUMN_VIEW_CELL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCellClass))
#define GTK_IS_COLUMN_VIEW_CELL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_CELL))
#define GTK_IS_COLUMN_VIEW_CELL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_CELL))
#define GTK_COLUMN_VIEW_CELL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCellClass))
typedef struct _GtkColumnViewCell GtkColumnViewCell;
typedef struct _GtkColumnViewCellClass GtkColumnViewCellClass;
GType gtk_column_view_cell_get_type (void) G_GNUC_CONST;
GtkWidget * gtk_column_view_cell_new (GtkColumnViewColumn *column);
void gtk_column_view_cell_remove (GtkColumnViewCell *self);
GtkColumnViewCell * gtk_column_view_cell_get_next (GtkColumnViewCell *self);
GtkColumnViewCell * gtk_column_view_cell_get_prev (GtkColumnViewCell *self);
GtkColumnViewColumn * gtk_column_view_cell_get_column (GtkColumnViewCell *self);
G_END_DECLS
#endif /* __GTK_COLUMN_VIEW_CELL_PRIVATE_H__ */

1095
gtk/gtkcolumnviewcolumn.c Normal file

File diff suppressed because it is too large Load Diff

112
gtk/gtkcolumnviewcolumn.h Normal file
View File

@@ -0,0 +1,112 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COLUMN_VIEW_COLUMN_H__
#define __GTK_COLUMN_VIEW_COLUMN_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkcolumnview.h>
#include <gtk/gtksorter.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLUMN_VIEW_COLUMN (gtk_column_view_column_get_type ())
#define GTK_COLUMN_VIEW_COLUMN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumn))
#define GTK_COLUMN_VIEW_COLUMN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumnClass))
#define GTK_IS_COLUMN_VIEW_COLUMN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_COLUMN))
#define GTK_IS_COLUMN_VIEW_COLUMN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_COLUMN))
#define GTK_COLUMN_VIEW_COLUMN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumnClass))
/**
* GtkColumnViewColumn:
*
* GtkColumnViewColumns are added to #GtkColumnViews.
*/
typedef struct _GtkColumnViewColumnClass GtkColumnViewColumnClass;
GDK_AVAILABLE_IN_ALL
GType gtk_column_view_column_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkColumnViewColumn * gtk_column_view_column_new (const char *title);
GDK_AVAILABLE_IN_ALL
GtkColumnViewColumn * gtk_column_view_column_new_with_factory (const char *title,
GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GtkColumnView * gtk_column_view_column_get_column_view (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_factory (GtkColumnViewColumn *self,
GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GtkListItemFactory * gtk_column_view_column_get_factory (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_title (GtkColumnViewColumn *self,
const char *title);
GDK_AVAILABLE_IN_ALL
const char * gtk_column_view_column_get_title (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_sorter (GtkColumnViewColumn *self,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_column_view_column_get_sorter (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_visible (GtkColumnViewColumn *self,
gboolean visible);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_column_get_visible (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_fixed_width (GtkColumnViewColumn *self,
int fixed_width);
GDK_AVAILABLE_IN_ALL
int gtk_column_view_column_get_fixed_width (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_resizable (GtkColumnViewColumn *self,
gboolean resizable);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_column_get_resizable (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_expand (GtkColumnViewColumn *self,
gboolean expand);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_column_get_expand (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_reorderable (GtkColumnViewColumn *self,
gboolean reorderable);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_column_get_reorderable (GtkColumnViewColumn *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_column_set_header_menu (GtkColumnViewColumn *self,
GMenuModel *menu);
GDK_AVAILABLE_IN_ALL
GMenuModel * gtk_column_view_column_get_header_menu (GtkColumnViewColumn *self);
G_END_DECLS
#endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */

View File

@@ -0,0 +1,57 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__
#define __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__
#include "gtk/gtkcolumnviewcolumn.h"
#include "gtk/gtkcolumnviewcellprivate.h"
void gtk_column_view_column_set_column_view (GtkColumnViewColumn *self,
GtkColumnView *view);
void gtk_column_view_column_add_cell (GtkColumnViewColumn *self,
GtkColumnViewCell *cell);
void gtk_column_view_column_remove_cell (GtkColumnViewColumn *self,
GtkColumnViewCell *cell);
GtkColumnViewCell * gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self);
GtkWidget * gtk_column_view_column_get_header (GtkColumnViewColumn *self);
void gtk_column_view_column_queue_resize (GtkColumnViewColumn *self);
void gtk_column_view_column_measure (GtkColumnViewColumn *self,
int *minimum,
int *natural);
void gtk_column_view_column_allocate (GtkColumnViewColumn *self,
int offset,
int size);
void gtk_column_view_column_get_allocation (GtkColumnViewColumn *self,
int *offset,
int *size);
void gtk_column_view_column_notify_sort (GtkColumnViewColumn *self);
void gtk_column_view_column_set_header_position (GtkColumnViewColumn *self,
int offset);
void gtk_column_view_column_get_header_allocation (GtkColumnViewColumn *self,
int *offset,
int *size);
#endif /* __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__ */

159
gtk/gtkcolumnviewlayout.c Normal file
View File

@@ -0,0 +1,159 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcolumnviewlayoutprivate.h"
#include "gtkcolumnviewcellprivate.h"
#include "gtkcolumnviewcolumnprivate.h"
#include "gtkcolumnviewprivate.h"
#include "gtkcolumnviewtitleprivate.h"
#include "gtkwidgetprivate.h"
struct _GtkColumnViewLayout
{
GtkLayoutManager parent_instance;
GtkColumnView *view; /* no reference */
};
G_DEFINE_TYPE (GtkColumnViewLayout, gtk_column_view_layout, GTK_TYPE_LAYOUT_MANAGER)
static void
gtk_column_view_layout_measure_along (GtkColumnViewLayout *self,
GtkWidget *widget,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkOrientation orientation = GTK_ORIENTATION_VERTICAL;
GtkWidget *child;
for (child = _gtk_widget_get_first_child (widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
int child_min = 0;
int child_nat = 0;
int child_min_baseline = -1;
int child_nat_baseline = -1;
gtk_widget_measure (child, orientation, for_size,
&child_min, &child_nat,
&child_min_baseline, &child_nat_baseline);
*minimum = MAX (*minimum, child_min);
*natural = MAX (*natural, child_nat);
if (child_min_baseline > -1)
*minimum_baseline = MAX (*minimum_baseline, child_min_baseline);
if (child_nat_baseline > -1)
*natural_baseline = MAX (*natural_baseline, child_nat_baseline);
}
}
static void
gtk_column_view_layout_measure (GtkLayoutManager *layout,
GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkColumnViewLayout *self = GTK_COLUMN_VIEW_LAYOUT (layout);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
gtk_column_view_measure_across (GTK_COLUMN_VIEW (self->view),
minimum,
natural);
}
else
{
gtk_column_view_layout_measure_along (self,
widget,
for_size,
minimum,
natural,
minimum_baseline,
natural_baseline);
}
}
static void
gtk_column_view_layout_allocate (GtkLayoutManager *layout_manager,
GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkWidget *child;
for (child = _gtk_widget_get_first_child (widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkColumnViewColumn *column;
int col_x, col_width;
if (GTK_IS_COLUMN_VIEW_CELL (child))
{
column = gtk_column_view_cell_get_column (GTK_COLUMN_VIEW_CELL (child));
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
}
else
{
column = gtk_column_view_title_get_column (GTK_COLUMN_VIEW_TITLE (child));
gtk_column_view_column_get_header_allocation (column, &col_x, &col_width);
}
gtk_widget_size_allocate (child, &(GtkAllocation) { col_x, 0, col_width, height }, baseline);
}
}
static void
gtk_column_view_layout_class_init (GtkColumnViewLayoutClass *klass)
{
GtkLayoutManagerClass *layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (klass);
layout_manager_class->measure = gtk_column_view_layout_measure;
layout_manager_class->allocate = gtk_column_view_layout_allocate;
}
static void
gtk_column_view_layout_init (GtkColumnViewLayout *self)
{
}
GtkLayoutManager *
gtk_column_view_layout_new (GtkColumnView *view)
{
GtkColumnViewLayout *result;
result = g_object_new (GTK_TYPE_COLUMN_VIEW_LAYOUT, NULL);
result->view = view;
return GTK_LAYOUT_MANAGER (result);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__
#define __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__
#include <gtk/gtkcolumnview.h>
#include <gtk/gtklayoutmanager.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLUMN_VIEW_LAYOUT (gtk_column_view_layout_get_type ())
G_DECLARE_FINAL_TYPE (GtkColumnViewLayout, gtk_column_view_layout, GTK, COLUMN_VIEW_LAYOUT, GtkLayoutManager)
GtkLayoutManager * gtk_column_view_layout_new (GtkColumnView *view);
#endif /* __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__ */

View File

@@ -0,0 +1,36 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COLUMN_VIEW_PRIVATE_H__
#define __GTK_COLUMN_VIEW_PRIVATE_H__
#include "gtk/gtkcolumnview.h"
#include "gtk/gtklistview.h"
#include "gtk/gtkcolumnviewsorterprivate.h"
#include "gtk/gtklistitemwidgetprivate.h"
GtkListItemWidget * gtk_column_view_get_header_widget (GtkColumnView *self);
GtkListView * gtk_column_view_get_list_view (GtkColumnView *self);
void gtk_column_view_measure_across (GtkColumnView *self,
int *minimum,
int *natural);
#endif /* __GTK_COLUMN_VIEW_PRIVATE_H__ */

323
gtk/gtkcolumnviewsorter.c Normal file
View File

@@ -0,0 +1,323 @@
/*
* Copyright © 2019 Matthias Clasen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include "gtkcolumnviewsorterprivate.h"
#include "gtkcolumnviewcolumnprivate.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
typedef struct
{
GtkColumnViewColumn *column;
GtkSorter *sorter;
gboolean inverted;
gulong changed_id;
} Sorter;
static void
free_sorter (gpointer data)
{
Sorter *s = data;
g_signal_handler_disconnect (s->sorter, s->changed_id);
g_object_unref (s->sorter);
g_object_unref (s->column);
g_free (s);
}
struct _GtkColumnViewSorter
{
GtkSorter parent_instance;
GSequence *sorters;
};
G_DEFINE_TYPE (GtkColumnViewSorter, gtk_column_view_sorter, GTK_TYPE_SORTER)
static GtkOrdering
gtk_column_view_sorter_compare (GtkSorter *sorter,
gpointer item1,
gpointer item2)
{
GtkColumnViewSorter *self = GTK_COLUMN_VIEW_SORTER (sorter);
GtkOrdering result = GTK_ORDERING_EQUAL;
GSequenceIter *iter;
for (iter = g_sequence_get_begin_iter (self->sorters);
!g_sequence_iter_is_end (iter);
iter = g_sequence_iter_next (iter))
{
Sorter *s = g_sequence_get (iter);
result = gtk_sorter_compare (s->sorter, item1, item2);
if (s->inverted)
result = - result;
if (result != GTK_ORDERING_EQUAL)
break;
}
return result;
}
static GtkSorterOrder
gtk_column_view_sorter_get_order (GtkSorter *sorter)
{
GtkColumnViewSorter *self = GTK_COLUMN_VIEW_SORTER (sorter);
GtkSorterOrder result = GTK_SORTER_ORDER_NONE;
GSequenceIter *iter;
for (iter = g_sequence_get_begin_iter (self->sorters);
!g_sequence_iter_is_end (iter);
iter = g_sequence_iter_next (iter))
{
Sorter *s = g_sequence_get (iter);
switch (gtk_sorter_get_order (s->sorter))
{
case GTK_SORTER_ORDER_PARTIAL:
result = GTK_SORTER_ORDER_PARTIAL;
break;
case GTK_SORTER_ORDER_NONE:
break;
case GTK_SORTER_ORDER_TOTAL:
return GTK_SORTER_ORDER_TOTAL;
default:
g_assert_not_reached ();
break;
}
}
return result;
}
static void
gtk_column_view_sorter_dispose (GObject *object)
{
GtkColumnViewSorter *self = GTK_COLUMN_VIEW_SORTER (object);
g_clear_pointer (&self->sorters, g_sequence_free);
G_OBJECT_CLASS (gtk_column_view_sorter_parent_class)->dispose (object);
}
static void
gtk_column_view_sorter_class_init (GtkColumnViewSorterClass *class)
{
GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
sorter_class->compare = gtk_column_view_sorter_compare;
sorter_class->get_order = gtk_column_view_sorter_get_order;
object_class->dispose = gtk_column_view_sorter_dispose;
}
static void
gtk_column_view_sorter_init (GtkColumnViewSorter *self)
{
self->sorters = g_sequence_new (free_sorter);
}
GtkSorter *
gtk_column_view_sorter_new (void)
{
return g_object_new (GTK_TYPE_COLUMN_VIEW_SORTER, NULL);
}
static void
gtk_column_view_sorter_changed_cb (GtkSorter *sorter, int change, gpointer data)
{
gtk_sorter_changed (GTK_SORTER (data), GTK_SORTER_CHANGE_DIFFERENT);
}
static gboolean
remove_column (GtkColumnViewSorter *self,
GtkColumnViewColumn *column)
{
GSequenceIter *iter;
for (iter = g_sequence_get_begin_iter (self->sorters);
!g_sequence_iter_is_end (iter);
iter = g_sequence_iter_next (iter))
{
Sorter *s = g_sequence_get (iter);
if (s->column == column)
{
g_sequence_remove (iter);
return TRUE;
}
}
return FALSE;
}
gboolean
gtk_column_view_sorter_add_column (GtkColumnViewSorter *self,
GtkColumnViewColumn *column)
{
GSequenceIter *iter;
GtkSorter *sorter;
Sorter *s, *first;
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), FALSE);
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column), FALSE);
sorter = gtk_column_view_column_get_sorter (column);
if (sorter == NULL)
return FALSE;
iter = g_sequence_get_begin_iter (self->sorters);
if (!g_sequence_iter_is_end (iter))
{
first = g_sequence_get (iter);
if (first->column == column)
{
first->inverted = !first->inverted;
goto out;
}
}
else
first = NULL;
remove_column (self, column);
s = g_new (Sorter, 1);
s->column = g_object_ref (column);
s->sorter = g_object_ref (sorter);
s->changed_id = g_signal_connect (sorter, "changed", G_CALLBACK (gtk_column_view_sorter_changed_cb), self);
s->inverted = FALSE;
g_sequence_insert_before (iter, s);
/* notify the previous first column to stop drawing an arrow */
if (first)
gtk_column_view_column_notify_sort (first->column);
out:
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
gtk_column_view_column_notify_sort (column);
return TRUE;
}
gboolean
gtk_column_view_sorter_remove_column (GtkColumnViewSorter *self,
GtkColumnViewColumn *column)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), FALSE);
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column), FALSE);
if (remove_column (self, column))
{
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
gtk_column_view_column_notify_sort (column);
return TRUE;
}
return FALSE;
}
gboolean
gtk_column_view_sorter_set_column (GtkColumnViewSorter *self,
GtkColumnViewColumn *column,
gboolean inverted)
{
GtkSorter *sorter;
Sorter *s;
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), FALSE);
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column), FALSE);
sorter = gtk_column_view_column_get_sorter (column);
if (sorter == NULL)
return FALSE;
g_object_ref (column);
g_sequence_remove_range (g_sequence_get_begin_iter (self->sorters),
g_sequence_get_end_iter (self->sorters));
s = g_new (Sorter, 1);
s->column = g_object_ref (column);
s->sorter = g_object_ref (sorter);
s->changed_id = g_signal_connect (sorter, "changed", G_CALLBACK (gtk_column_view_sorter_changed_cb), self);
s->inverted = inverted;
g_sequence_prepend (self->sorters, s);
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
gtk_column_view_column_notify_sort (column);
g_object_unref (column);
return TRUE;
}
void
gtk_column_view_sorter_clear (GtkColumnViewSorter *self)
{
GSequenceIter *iter;
Sorter *s;
GtkColumnViewColumn *column;
g_return_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self));
if (g_sequence_is_empty (self->sorters))
return;
iter = g_sequence_get_begin_iter (self->sorters);
s = g_sequence_get (iter);
column = g_object_ref (s->column);
g_sequence_remove_range (iter, g_sequence_get_end_iter (self->sorters));
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
gtk_column_view_column_notify_sort (column);
g_object_unref (column);
}
GtkColumnViewColumn *
gtk_column_view_sorter_get_sort_column (GtkColumnViewSorter *self,
gboolean *inverted)
{
GSequenceIter *iter;
Sorter *s;
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), NULL);
if (g_sequence_is_empty (self->sorters))
return NULL;
iter = g_sequence_get_begin_iter (self->sorters);
s = g_sequence_get (iter);
*inverted = s->inverted;
return s->column;
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright © 2019 Matthias Clasen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#ifndef __GTK_COLUMN_VIEW_SORTER_H__
#define __GTK_COLUMN_VIEW_SORTER_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
#include <gtk/gtksorter.h>
#include <gtk/gtkcolumnviewcolumn.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLUMN_VIEW_SORTER (gtk_column_view_sorter_get_type ())
G_DECLARE_FINAL_TYPE (GtkColumnViewSorter, gtk_column_view_sorter, GTK, COLUMN_VIEW_SORTER, GtkSorter)
GtkSorter * gtk_column_view_sorter_new (void);
gboolean gtk_column_view_sorter_add_column (GtkColumnViewSorter *self,
GtkColumnViewColumn *column);
gboolean gtk_column_view_sorter_remove_column (GtkColumnViewSorter *self,
GtkColumnViewColumn *column);
void gtk_column_view_sorter_clear (GtkColumnViewSorter *self);
GtkColumnViewColumn * gtk_column_view_sorter_get_sort_column (GtkColumnViewSorter *self,
gboolean *inverted);
gboolean gtk_column_view_sorter_set_column (GtkColumnViewSorter *self,
GtkColumnViewColumn *column,
gboolean inverted);
G_END_DECLS
#endif /* __GTK_SORTER_H__ */

273
gtk/gtkcolumnviewtitle.c Normal file
View File

@@ -0,0 +1,273 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcolumnviewtitleprivate.h"
#include "gtkcolumnviewprivate.h"
#include "gtkcolumnviewcolumnprivate.h"
#include "gtkcolumnviewsorterprivate.h"
#include "gtkintl.h"
#include "gtklabel.h"
#include "gtkwidgetprivate.h"
#include "gtkbox.h"
#include "gtkimage.h"
#include "gtkgestureclick.h"
#include "gtkpopovermenu.h"
#include "gtknative.h"
struct _GtkColumnViewTitle
{
GtkWidget parent_instance;
GtkColumnViewColumn *column;
GtkWidget *box;
GtkWidget *title;
GtkWidget *sort;
GtkWidget *popup_menu;
};
struct _GtkColumnViewTitleClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (GtkColumnViewTitle, gtk_column_view_title, GTK_TYPE_WIDGET)
static void
gtk_column_view_title_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
GtkWidget *child = gtk_widget_get_first_child (widget);
if (child)
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
int fixed_width = gtk_column_view_column_get_fixed_width (self->column);
if (fixed_width > -1)
*minimum = *natural = fixed_width;
}
}
static void
gtk_column_view_title_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
GtkWidget *child = gtk_widget_get_first_child (widget);
if (child)
gtk_widget_allocate (child, width, height, baseline, NULL);
if (self->popup_menu)
gtk_native_check_resize (GTK_NATIVE (self->popup_menu));
}
static void
gtk_column_view_title_dispose (GObject *object)
{
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (object);
g_clear_pointer (&self->box, gtk_widget_unparent);
g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
g_clear_object (&self->column);
G_OBJECT_CLASS (gtk_column_view_title_parent_class)->dispose (object);
}
static void
gtk_column_view_title_class_init (GtkColumnViewTitleClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
widget_class->measure = gtk_column_view_title_measure;
widget_class->size_allocate = gtk_column_view_title_size_allocate;
gobject_class->dispose = gtk_column_view_title_dispose;
gtk_widget_class_set_css_name (widget_class, I_("button"));
}
static void
gtk_column_view_title_resize_func (GtkWidget *widget)
{
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
if (self->column)
gtk_column_view_column_queue_resize (self->column);
}
static void
activate_sort (GtkColumnViewTitle *self)
{
GtkSorter *sorter;
GtkColumnView *view;
GtkColumnViewSorter *view_sorter;
sorter = gtk_column_view_column_get_sorter (self->column);
if (sorter == NULL)
return;
view = gtk_column_view_column_get_column_view (self->column);
view_sorter = GTK_COLUMN_VIEW_SORTER (gtk_column_view_get_sorter (view));
gtk_column_view_sorter_add_column (view_sorter, self->column);
}
static void
show_menu (GtkColumnViewTitle *self,
double x,
double y)
{
g_print ("show menu\n");
if (!self->popup_menu)
{
GMenuModel *model;
model = gtk_column_view_column_get_header_menu (self->column);
if (!model)
return;
g_print ("got a model\n");
self->popup_menu = gtk_popover_menu_new_from_model (GTK_WIDGET (self), model);
gtk_popover_set_position (GTK_POPOVER (self->popup_menu), GTK_POS_BOTTOM);
gtk_popover_set_has_arrow (GTK_POPOVER (self->popup_menu), FALSE);
gtk_widget_set_halign (self->popup_menu, GTK_ALIGN_START);
}
if (x != -1 && y != -1)
{
GdkRectangle rect = { x, y, 1, 1 };
gtk_popover_set_pointing_to (GTK_POPOVER (self->popup_menu), &rect);
}
else
gtk_popover_set_pointing_to (GTK_POPOVER (self->popup_menu), NULL);
gtk_popover_popup (GTK_POPOVER (self->popup_menu));
}
static void
click_released_cb (GtkGestureClick *gesture,
guint n_press,
double x,
double y,
GtkWidget *widget)
{
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
guint button;
button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
if (button == GDK_BUTTON_PRIMARY)
activate_sort (self);
else if (button == GDK_BUTTON_SECONDARY)
show_menu (self, x, y);
}
static void
gtk_column_view_title_init (GtkColumnViewTitle *self)
{
GtkWidget *widget = GTK_WIDGET (self);
GtkGesture *gesture;
widget->priv->resize_func = gtk_column_view_title_resize_func;
self->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_parent (self->box, widget);
self->title = gtk_label_new (NULL);
gtk_container_add (GTK_CONTAINER (self->box), self->title);
self->sort = gtk_image_new ();
gtk_container_add (GTK_CONTAINER (self->box), self->sort);
gesture = gtk_gesture_click_new ();
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
g_signal_connect (gesture, "released", G_CALLBACK (click_released_cb), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
}
GtkWidget *
gtk_column_view_title_new (GtkColumnViewColumn *column)
{
GtkColumnViewTitle *title;
title = g_object_new (GTK_TYPE_COLUMN_VIEW_TITLE, NULL);
title->column = g_object_ref (column);
gtk_column_view_title_update (title);
return GTK_WIDGET (title);
}
void
gtk_column_view_title_update (GtkColumnViewTitle *self)
{
GtkSorter *sorter;
GtkColumnView *view;
GtkColumnViewSorter *view_sorter;
gboolean inverted;
GtkColumnViewColumn *active;
gtk_label_set_label (GTK_LABEL (self->title), gtk_column_view_column_get_title (self->column));
sorter = gtk_column_view_column_get_sorter (self->column);
if (sorter)
{
view = gtk_column_view_column_get_column_view (self->column);
view_sorter = GTK_COLUMN_VIEW_SORTER (gtk_column_view_get_sorter (view));
active = gtk_column_view_sorter_get_sort_column (view_sorter, &inverted);
gtk_widget_show (self->sort);
if (self->column == active)
{
if (inverted)
gtk_image_set_from_icon_name (GTK_IMAGE (self->sort), "pan-down-symbolic");
else
gtk_image_set_from_icon_name (GTK_IMAGE (self->sort), "pan-up-symbolic");
}
else
gtk_image_clear (GTK_IMAGE (self->sort));
}
else
gtk_widget_hide (self->sort);
g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
}
GtkColumnViewColumn *
gtk_column_view_title_get_column (GtkColumnViewTitle *self)
{
return self->column;
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_COLUMN_VIEW_TITLE_PRIVATE_H__
#define __GTK_COLUMN_VIEW_TITLE_PRIVATE_H__
#include "gtkcolumnviewcolumn.h"
G_BEGIN_DECLS
#define GTK_TYPE_COLUMN_VIEW_TITLE (gtk_column_view_title_get_type ())
#define GTK_COLUMN_VIEW_TITLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_TITLE, GtkColumnViewTitle))
#define GTK_COLUMN_VIEW_TITLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_TITLE, GtkColumnViewTitleClass))
#define GTK_IS_COLUMN_VIEW_TITLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_TITLE))
#define GTK_IS_COLUMN_VIEW_TITLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_TITLE))
#define GTK_COLUMN_VIEW_TITLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_TITLE, GtkColumnViewTitleClass))
typedef struct _GtkColumnViewTitle GtkColumnViewTitle;
typedef struct _GtkColumnViewTitleClass GtkColumnViewTitleClass;
GType gtk_column_view_title_get_type (void) G_GNUC_CONST;
GtkWidget * gtk_column_view_title_new (GtkColumnViewColumn *column);
void gtk_column_view_title_update (GtkColumnViewTitle *self);
GtkColumnViewColumn * gtk_column_view_title_get_column (GtkColumnViewTitle *self);
G_END_DECLS
#endif /* __GTK_COLUMN_VIEW_TITLE_PRIVATE_H__ */

704
gtk/gtkcoverflow.c Normal file
View File

@@ -0,0 +1,704 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcoverflow.h"
#include "gtkintl.h"
#include "gtklistbaseprivate.h"
#include "gtkmain.h"
#include "gtkorientable.h"
#include "gtkprivate.h"
#include "gtkrbtreeprivate.h"
#include "gtkwidgetprivate.h"
/* Extra items to display at most above + below the central item */
#define GTK_COVER_FLOW_DISPLAY_ITEMS 5
/* Number of extra space we leave around the covers */
#define GTK_COVER_FLOW_SCALE_ALONG 3
#define GTK_COVER_FLOW_SCALE_ACROSS 2
/**
* SECTION:gtkcoverflow
* @title: GtkCoverFlow
* @short_description: A coverflow list widget
* @see_also: #GListModel
*
* GtkCoverFlow is a widget to present a coverflow list
*/
struct _GtkCoverFlow
{
GtkListBase parent_instance;
int size_across;
int size_along;
};
struct _GtkCoverFlowClass
{
GtkListBaseClass parent_class;
};
enum
{
PROP_0,
PROP_FACTORY,
PROP_MODEL,
N_PROPS
};
enum {
ACTIVATE,
LAST_SIGNAL
};
G_DEFINE_TYPE (GtkCoverFlow, gtk_cover_flow, GTK_TYPE_LIST_BASE)
static GParamSpec *properties[N_PROPS] = { NULL, };
static guint signals[LAST_SIGNAL] = { 0 };
static gboolean
gtk_cover_flow_get_allocation_along (GtkListBase *base,
guint pos,
int *offset,
int *size)
{
GtkCoverFlow *self = GTK_COVER_FLOW (base);
if (offset)
*offset = pos * self->size_along;
if (size)
*size = self->size_along;
return TRUE;
}
static gboolean
gtk_cover_flow_get_allocation_across (GtkListBase *base,
guint pos,
int *offset,
int *size)
{
GtkCoverFlow *self = GTK_COVER_FLOW (base);
if (offset)
*offset = pos * self->size_across;
if (size)
*size = self->size_across;
return TRUE;
}
static guint
gtk_cover_flow_move_focus_along (GtkListBase *base,
guint pos,
int steps)
{
if (steps < 0)
return pos - MIN (pos, -steps);
else
{
pos += MIN (gtk_list_base_get_n_items (base) - pos - 1, steps);
}
return pos;
}
static guint
gtk_cover_flow_move_focus_across (GtkListBase *base,
guint pos,
int steps)
{
return pos;
}
static gboolean
gtk_cover_flow_get_position_from_allocation (GtkListBase *base,
int across,
int along,
guint *pos,
cairo_rectangle_int_t *area)
{
GtkCoverFlow *self = GTK_COVER_FLOW (base);
if (across >= self->size_across ||
along >= self->size_along * gtk_list_base_get_n_items (base))
return FALSE;
*pos = along / self->size_along;
if (area)
{
area->x = 0;
area->width = self->size_across;
area->y = *pos * self->size_along;
area->height = self->size_along;
}
return TRUE;
}
static void
gtk_cover_flow_measure_children (GtkCoverFlow *self,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural)
{
GtkListItemManagerItem *item;
int min, nat, child_min, child_nat;
min = 0;
nat = 0;
for (item = gtk_list_item_manager_get_first (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
item != NULL;
item = gtk_rb_tree_node_get_next (item))
{
/* ignore unavailable items */
if (item->widget == NULL)
continue;
gtk_widget_measure (item->widget,
orientation, for_size,
&child_min, &child_nat, NULL, NULL);
min = MAX (min, child_min);
nat = MAX (nat, child_nat);
}
*minimum = min;
*natural = nat;
}
static void
gtk_cover_flow_measure_across (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
if (for_size > 0)
for_size /= GTK_COVER_FLOW_SCALE_ALONG;
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
*minimum *= GTK_COVER_FLOW_SCALE_ACROSS;
*natural *= GTK_COVER_FLOW_SCALE_ACROSS;
}
static void
gtk_cover_flow_measure_along (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
if (for_size > 0)
for_size /= GTK_COVER_FLOW_SCALE_ACROSS;
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
*minimum *= GTK_COVER_FLOW_SCALE_ALONG;
*natural *= GTK_COVER_FLOW_SCALE_ALONG;
}
static void
gtk_cover_flow_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
if (orientation == gtk_list_base_get_orientation (GTK_LIST_BASE (self)))
gtk_cover_flow_measure_along (widget, orientation, for_size, minimum, natural);
else
gtk_cover_flow_measure_across (widget, orientation, for_size, minimum, natural);
}
static GskTransform *
transform_translate_oriented (GskTransform *transform,
GtkOrientation orientation,
GtkTextDirection dir,
float across,
float along)
{
if (orientation == GTK_ORIENTATION_VERTICAL)
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (across, along));
else if (dir == GTK_TEXT_DIR_LTR)
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (along, across));
else
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-along, across));
}
static void
gtk_cover_flow_size_allocate_child (GtkWidget *child,
GtkOrientation orientation,
GtkTextDirection dir,
GskTransform *transform,
int width,
int height)
{
if (orientation == GTK_ORIENTATION_VERTICAL)
{
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-width / 2, -height / 2));
gtk_widget_allocate (child, width, height, -1, transform);
}
else
{
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-height / 2, -width / 2));
gtk_widget_allocate (child, height, width, -1, transform);
}
}
static void
gtk_cover_flow_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
GtkOrientation orientation, opposite_orientation;
GtkTextDirection dir;
GtkListItemManagerItem *item;
GtkListItemManager *manager;
guint i, pos;
int x, y, along, across;
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
/* step 0: exit early if list is empty */
if (gtk_list_item_manager_get_root (manager) == NULL)
return;
/* step 1: determine size of children */
along = orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
across = opposite_orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
self->size_along = along / GTK_COVER_FLOW_SCALE_ALONG;
self->size_across = across / GTK_COVER_FLOW_SCALE_ACROSS;
/* step 2: update the adjustments */
gtk_list_base_update_adjustments (GTK_LIST_BASE (self),
self->size_across,
self->size_along * gtk_list_base_get_n_items (GTK_LIST_BASE (self)),
self->size_across,
self->size_along,
&x, &y);
pos = gtk_list_base_get_anchor (GTK_LIST_BASE (self));
/* step 4: actually allocate the widgets */
dir = _gtk_widget_get_direction (widget);
i = 0;
for (item = gtk_list_item_manager_get_first (manager);
item != NULL;
item = gtk_rb_tree_node_get_next (item))
{
if (item->widget)
{
/* start at the center */
GskTransform *transform = transform_translate_oriented (NULL, orientation, dir, across / 2, along / 2);
if (i == pos)
{
/* nothing to do, we're already centered */
}
else if (MAX (pos, i) - MIN (pos, i) < GTK_COVER_FLOW_DISPLAY_ITEMS) /* ABS() doesn't work with guint */
{
int diff = i - pos;
transform = gsk_transform_perspective (transform, MAX (width, height) * 2);
transform = transform_translate_oriented (transform,
orientation, dir,
0,
diff * self->size_along / 4);
transform = transform_translate_oriented (transform,
orientation, dir,
0,
(diff < 0 ? -1 : 1) * self->size_along / 2);
if (orientation == GTK_ORIENTATION_VERTICAL)
transform = gsk_transform_rotate_3d (transform,
diff > 0 ? 60 : -60,
graphene_vec3_x_axis ());
else
transform = gsk_transform_rotate_3d (transform,
diff < 0 ? 60 : -60,
graphene_vec3_y_axis ());
transform = transform_translate_oriented (transform,
orientation, dir,
0,
(diff < 0 ? 1 : -1) * self->size_along / 4);
}
else
{
transform = transform_translate_oriented (transform,
orientation, dir,
- 2 * self->size_across,
- 2 * self->size_along);
}
gtk_cover_flow_size_allocate_child (item->widget,
orientation, dir,
transform,
self->size_across,
self->size_along);
}
i += item->n_items;
}
}
static void
gtk_cover_flow_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkListBase *base = GTK_LIST_BASE (object);
switch (property_id)
{
case PROP_FACTORY:
g_value_set_object (value, gtk_list_item_manager_get_factory (gtk_list_base_get_manager (base)));
break;
case PROP_MODEL:
g_value_set_object (value, gtk_list_base_get_model (base));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_cover_flow_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkWidget *child;
GtkListItemManagerItem *item;
item = gtk_list_item_manager_get_nth (gtk_list_base_get_manager (GTK_LIST_BASE (widget)),
gtk_list_base_get_anchor (GTK_LIST_BASE (widget)),
NULL);
if (item == NULL || item->widget == NULL)
{
GTK_WIDGET_CLASS (gtk_cover_flow_parent_class)->snapshot (widget, snapshot);
return;
}
for (child = _gtk_widget_get_first_child (widget);
child != item->widget;
child = _gtk_widget_get_next_sibling (child))
gtk_widget_snapshot_child (widget, child, snapshot);
for (child = _gtk_widget_get_last_child (widget);
child != item->widget;
child = _gtk_widget_get_prev_sibling (child))
gtk_widget_snapshot_child (widget, child, snapshot);
gtk_widget_snapshot_child (widget, item->widget, snapshot);
}
static void
gtk_cover_flow_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkCoverFlow *self = GTK_COVER_FLOW (object);
switch (property_id)
{
case PROP_FACTORY:
gtk_cover_flow_set_factory (self, g_value_get_object (value));
break;
case PROP_MODEL:
gtk_cover_flow_set_model (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_cover_flow_activate_item (GtkWidget *widget,
const char *action_name,
GVariant *parameter)
{
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
guint pos;
if (!g_variant_check_format_string (parameter, "u", FALSE))
return;
g_variant_get (parameter, "u", &pos);
if (pos >= gtk_list_base_get_n_items (GTK_LIST_BASE (self)))
return;
g_signal_emit (widget, signals[ACTIVATE], 0, pos);
}
static void
gtk_cover_flow_class_init (GtkCoverFlowClass *klass)
{
GtkListBaseClass *list_base_class = GTK_LIST_BASE_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
list_base_class->list_item_name = "cover";
list_base_class->list_item_size = sizeof (GtkListItemManagerItem);
list_base_class->list_item_augment_size = sizeof (GtkListItemManagerItemAugment);
list_base_class->list_item_augment_func = gtk_list_item_manager_augment_node;
list_base_class->get_allocation_along = gtk_cover_flow_get_allocation_along;
list_base_class->get_allocation_across = gtk_cover_flow_get_allocation_across;
list_base_class->get_position_from_allocation = gtk_cover_flow_get_position_from_allocation;
list_base_class->move_focus_along = gtk_cover_flow_move_focus_along;
list_base_class->move_focus_across = gtk_cover_flow_move_focus_across;
widget_class->measure = gtk_cover_flow_measure;
widget_class->size_allocate = gtk_cover_flow_size_allocate;
widget_class->snapshot = gtk_cover_flow_snapshot;
gobject_class->get_property = gtk_cover_flow_get_property;
gobject_class->set_property = gtk_cover_flow_set_property;
/**
* GtkCoverFlow:factory:
*
* Factory for populating list items
*/
properties[PROP_FACTORY] =
g_param_spec_object ("factory",
P_("Factory"),
P_("Factory for populating list items"),
GTK_TYPE_LIST_ITEM_FACTORY,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkCoverFlow:model:
*
* Model for the items displayed
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("Model for the items displayed"),
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
/**
* GtkCoverFlow::activate:
* @self: The #GtkCoverFlow
* @position: position of item to activate
*
* The ::activate signal is emitted when an item has been activated by the user,
* usually via activating the GtkCoverFlow|list.activate-item action.
*
* This allows for a convenient way to handle activation in a listview.
* See gtk_list_item_set_activatable() for details on how to use this signal.
*/
signals[ACTIVATE] =
g_signal_new (I_("activate"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
g_signal_set_va_marshaller (signals[ACTIVATE],
G_TYPE_FROM_CLASS (gobject_class),
g_cclosure_marshal_VOID__UINTv);
/**
* GtkCoverFlow|list.activate-item:
* @position: position of item to activate
*
* Activates the item given in @position by emitting the GtkCoverFlow::activate
* signal.
*/
gtk_widget_class_install_action (widget_class,
"list.activate-item",
"u",
gtk_cover_flow_activate_item);
gtk_widget_class_set_css_name (widget_class, I_("coverflow"));
}
static void
gtk_cover_flow_init (GtkCoverFlow *self)
{
gtk_list_base_set_anchor_max_widgets (GTK_LIST_BASE (self),
0,
GTK_COVER_FLOW_DISPLAY_ITEMS);
/* FIXME: this should overwrite the property default, but gobject properties... */
gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_HORIZONTAL);
}
/**
* gtk_cover_flow_new:
*
* Creates a new empty #GtkCoverFlow.
*
* You most likely want to call gtk_cover_flow_set_factory() to
* set up a way to map its items to widgets and gtk_cover_flow_set_model()
* to set a model to provide items next.
*
* Returns: a new #GtkCoverFlow
**/
GtkWidget *
gtk_cover_flow_new (void)
{
return g_object_new (GTK_TYPE_COVER_FLOW, NULL);
}
/**
* gtk_cover_flow_new_with_factory:
* @factory: (transfer full): The factory to populate items with
*
* Creates a new #GtkCoverFlow that uses the given @factory for
* mapping items to widgets.
*
* You most likely want to call gtk_cover_flow_set_model() to set
* a model next.
*
* The function takes ownership of the
* argument, so you can write code like
* ```
* cover_flow = gtk_cover_flow_new_with_factory (
* gtk_builder_list_item_factory_newfrom_resource ("/resource.ui"));
* ```
*
* Returns: a new #GtkCoverFlow using the given @factory
**/
GtkWidget *
gtk_cover_flow_new_with_factory (GtkListItemFactory *factory)
{
GtkWidget *result;
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
result = g_object_new (GTK_TYPE_COVER_FLOW,
"factory", factory,
NULL);
g_object_unref (factory);
return result;
}
/**
* gtk_cover_flow_get_model:
* @self: a #GtkCoverFlow
*
* Gets the model that's currently used to read the items displayed.
*
* Returns: (nullable) (transfer none): The model in use
**/
GListModel *
gtk_cover_flow_get_model (GtkCoverFlow *self)
{
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
return gtk_list_base_get_model (GTK_LIST_BASE (self));
}
/**
* gtk_cover_flow_set_model:
* @self: a #GtkCoverFlow
* @model: (allow-none) (transfer none): the model to use or %NULL for none
*
* Sets the #GListModel to use.
*
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
* Otherwise, @self creates a #GtkSingleSelection for the selection.
**/
void
gtk_cover_flow_set_model (GtkCoverFlow *self,
GListModel *model)
{
g_return_if_fail (GTK_IS_COVER_FLOW (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (!gtk_list_base_set_model (GTK_LIST_BASE (self), model))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_cover_flow_get_factory:
* @self: a #GtkCoverFlow
*
* Gets the factory that's currently used to populate list items.
*
* Returns: (nullable) (transfer none): The factory in use
**/
GtkListItemFactory *
gtk_cover_flow_get_factory (GtkCoverFlow *self)
{
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
return gtk_list_item_manager_get_factory (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
}
/**
* gtk_cover_flow_set_factory:
* @self: a #GtkCoverFlow
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
*
* Sets the #GtkListItemFactory to use for populating list items.
**/
void
gtk_cover_flow_set_factory (GtkCoverFlow *self,
GtkListItemFactory *factory)
{
GtkListItemManager *manager;
g_return_if_fail (GTK_IS_COVER_FLOW (self));
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
if (factory == gtk_list_item_manager_get_factory (manager))
return;
gtk_list_item_manager_set_factory (manager, factory);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
}

68
gtk/gtkcoverflow.h Normal file
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__ */

157
gtk/gtkcustomfilter.c Normal file
View File

@@ -0,0 +1,157 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcustomfilter.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
/**
* SECTION:gtkcustomfilter
* @Title: GtkCustomFilter
* @Short_description: Filtering with callbacks
*
* #GtkCustomFilter is a #GtkFilter that uses a callback to determine whether
* to include an item or not.
*/
struct _GtkCustomFilter
{
GtkFilter parent_instance;
GtkCustomFilterFunc match_func;
gpointer user_data;
GDestroyNotify user_destroy;
};
G_DEFINE_TYPE (GtkCustomFilter, gtk_custom_filter, GTK_TYPE_FILTER)
static gboolean
gtk_custom_filter_match (GtkFilter *filter,
gpointer item)
{
GtkCustomFilter *self = GTK_CUSTOM_FILTER (filter);
if (!self->match_func)
return TRUE;
return self->match_func (item, self->user_data);
}
static GtkFilterMatch
gtk_custom_filter_get_strictness (GtkFilter *filter)
{
GtkCustomFilter *self = GTK_CUSTOM_FILTER (filter);
if (!self->match_func)
return GTK_FILTER_MATCH_ALL;
return GTK_FILTER_MATCH_SOME;
}
static void
gtk_custom_filter_dispose (GObject *object)
{
GtkCustomFilter *self = GTK_CUSTOM_FILTER (object);
if (self->user_destroy)
self->user_destroy (self->user_data);
G_OBJECT_CLASS (gtk_custom_filter_parent_class)->dispose (object);
}
static void
gtk_custom_filter_class_init (GtkCustomFilterClass *class)
{
GtkFilterClass *filter_class = GTK_FILTER_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
filter_class->match = gtk_custom_filter_match;
filter_class->get_strictness = gtk_custom_filter_get_strictness;
object_class->dispose = gtk_custom_filter_dispose;
}
static void
gtk_custom_filter_init (GtkCustomFilter *self)
{
}
/**
* gtk_custom_filter_new:
* @match_func: (nullable): function to filter items
* @user_data: (nullable): user data to pass to @match_func
* @user_destroy: destory notify
*
* Creates a new filter using the given @match_func to filter
* items.
*
* If the filter func changes its filtering behavior,
* gtk_filter_changed() needs to be called.
*
* Returns: a new #GtkFilter
**/
GtkFilter *
gtk_custom_filter_new (GtkCustomFilterFunc match_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
GtkCustomFilter *result;
result = g_object_new (GTK_TYPE_CUSTOM_FILTER, NULL);
gtk_custom_filter_set_filter_func (result, match_func, user_data, user_destroy);
return GTK_FILTER (result);
}
/**
* gtk_custom_filter_set_filter_func:
* @self: a #GtkCustomFilter
* @match_func: (nullable): function to filter items
* @user_data: (nullable): user data to pass to @match_func
* @user_destroy: destory notify
*
* Sets (or unsets) the function used for filtering items.
*
* If the filter func changes its filtering behavior,
* gtk_filter_changed() needs to be called.
*
* If a previous function was set, its @user_destroy will be
* called now.
**/
void
gtk_custom_filter_set_filter_func (GtkCustomFilter *self,
GtkCustomFilterFunc match_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
g_return_if_fail (GTK_IS_CUSTOM_FILTER (self));
g_return_if_fail (match_func || (user_data == NULL && !user_destroy));
if (self->user_destroy)
self->user_destroy (self->user_data);
self->match_func = match_func;
self->user_data = user_data;
self->user_destroy = user_destroy;
gtk_filter_changed (GTK_FILTER (self), GTK_FILTER_CHANGE_DIFFERENT);
}

60
gtk/gtkcustomfilter.h Normal file
View File

@@ -0,0 +1,60 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_CUSTOM_FILTER_H__
#define __GTK_CUSTOM_FILTER_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkfilter.h>
G_BEGIN_DECLS
/**
* GtkCustomFilterFunc:
* @item: (type GObject): The item to be matched
* @user_data: user data
*
* User function that is called to determine if the @item should be matched.
* If the filter matches the item, this function must return %TRUE. If the
* item should be filtered out, %FALSE must be returned.
*
* Returns: %TRUE to keep the item around
*/
typedef gboolean (* GtkCustomFilterFunc) (gpointer item, gpointer user_data);
#define GTK_TYPE_CUSTOM_FILTER (gtk_custom_filter_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkCustomFilter, gtk_custom_filter, GTK, CUSTOM_FILTER, GtkFilter)
GDK_AVAILABLE_IN_ALL
GtkFilter * gtk_custom_filter_new (GtkCustomFilterFunc match_func,
gpointer user_data,
GDestroyNotify user_destroy);
GDK_AVAILABLE_IN_ALL
void gtk_custom_filter_set_filter_func (GtkCustomFilter *self,
GtkCustomFilterFunc match_func,
gpointer user_data,
GDestroyNotify user_destroy);
G_END_DECLS
#endif /* __GTK_CUSTOM_FILTER_H__ */

159
gtk/gtkcustomsorter.c Normal file
View File

@@ -0,0 +1,159 @@
/*
* Copyright © 2019 Matthias Clasen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include "gtkcustomsorter.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
/**
* SECTION:gtkcustomsorter
* @Title: GtkCustomSorter
* @Short_description: Sorting with a callback
*
* GtkCustomSorter is a #GtkSorter implementation that sorts
* via a traditional #GCompareDataFunc callback.
*/
struct _GtkCustomSorter
{
GtkSorter parent_instance;
GCompareDataFunc sort_func;
gpointer user_data;
GDestroyNotify user_destroy;
};
G_DEFINE_TYPE (GtkCustomSorter, gtk_custom_sorter, GTK_TYPE_SORTER)
static GtkOrdering
gtk_custom_sorter_compare (GtkSorter *sorter,
gpointer item1,
gpointer item2)
{
GtkCustomSorter *self = GTK_CUSTOM_SORTER (sorter);
if (!self->sort_func)
return GTK_ORDERING_EQUAL;
return gtk_ordering_from_cmpfunc (self->sort_func (item1, item2, self->user_data));
}
static GtkSorterOrder
gtk_custom_sorter_get_order (GtkSorter *sorter)
{
GtkCustomSorter *self = GTK_CUSTOM_SORTER (sorter);
if (!self->sort_func)
return GTK_SORTER_ORDER_NONE;
return GTK_SORTER_ORDER_PARTIAL;
}
static void
gtk_custom_sorter_dispose (GObject *object)
{
GtkCustomSorter *self = GTK_CUSTOM_SORTER (object);
if (self->user_destroy)
self->user_destroy (self->user_data);
self->sort_func = NULL;
self->user_destroy = NULL;
self->user_data = NULL;
G_OBJECT_CLASS (gtk_custom_sorter_parent_class)->dispose (object);
}
static void
gtk_custom_sorter_class_init (GtkCustomSorterClass *class)
{
GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
sorter_class->compare = gtk_custom_sorter_compare;
sorter_class->get_order = gtk_custom_sorter_get_order;
object_class->dispose = gtk_custom_sorter_dispose;
}
static void
gtk_custom_sorter_init (GtkCustomSorter *self)
{
}
/**
* gtk_custom_sorter_new:
* @sort_func: the #GCompareDataFunc to use for sorting
* @user_data: (nullable): user data to pass to @sort_func
* @user_destroy: (nullable): destroy notify for @user_data
*
* Creates a new #GtkSorter that works by calling
* @sort_func to compare items.
*
* Returns: a new #GTkSorter
*/
GtkSorter *
gtk_custom_sorter_new (GCompareDataFunc sort_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
GtkCustomSorter *sorter;
sorter = g_object_new (GTK_TYPE_CUSTOM_SORTER, NULL);
gtk_custom_sorter_set_sort_func (sorter, sort_func, user_data, user_destroy);
return GTK_SORTER (sorter);
}
/**
* gtk_custom_sorter_set_sort_func:
* @self: a #GtkCustomSorter
* @sort_func: (nullable): function to sort items
* @user_data: (nullable): user data to pass to @match_func
* @user_destroy: destory notify
*
* Sets (or unsets) the function used for sorting items.
*
* If the sort func changes its sorting behavior,
* gtk_sorter_changed() needs to be called.
*
* If a previous function was set, its @user_destroy will be
* called now.
**/
void
gtk_custom_sorter_set_sort_func (GtkCustomSorter *self,
GCompareDataFunc sort_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
g_return_if_fail (GTK_IS_CUSTOM_SORTER (self));
g_return_if_fail (sort_func || (user_data == NULL && !user_destroy));
if (self->user_destroy)
self->user_destroy (self->user_data);
self->sort_func = sort_func;
self->user_data = user_data;
self->user_destroy = user_destroy;
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
}

49
gtk/gtkcustomsorter.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* Copyright © 2019 Matthias Clasen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#ifndef __GTK_CUSTOM_SORTER_H__
#define __GTK_CUSTOM_SORTER_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkexpression.h>
#include <gtk/gtksorter.h>
G_BEGIN_DECLS
#define GTK_TYPE_CUSTOM_SORTER (gtk_custom_sorter_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkCustomSorter, gtk_custom_sorter, GTK, CUSTOM_SORTER, GtkSorter)
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_custom_sorter_new (GCompareDataFunc sort_func,
gpointer user_data,
GDestroyNotify user_destroy);
GDK_AVAILABLE_IN_ALL
void gtk_custom_sorter_set_sort_func (GtkCustomSorter *self,
GCompareDataFunc sort_func,
gpointer user_data,
GDestroyNotify user_destroy);
G_END_DECLS
#endif /* __GTK_CUSTOM_SORTER_H__ */

681
gtk/gtkdirectorylist.c Normal file
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
gtk/gtkdirectorylist.h Normal file
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__ */

1207
gtk/gtkdropdown.c Normal file

File diff suppressed because it is too large Load Diff

80
gtk/gtkdropdown.h Normal file
View File

@@ -0,0 +1,80 @@
/*
* Copyright © 2018 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen
*/
#ifndef __GTK_DROP_DOWN_H__
#define __GTK_DROP_DOWN_H__
#include <gtk/gtkwidget.h>
#include <gtk/gtkexpression.h>
G_BEGIN_DECLS
#define GTK_TYPE_DROP_DOWN (gtk_drop_down_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkDropDown, gtk_drop_down, GTK, DROP_DOWN, GtkWidget)
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_drop_down_new (void);
GDK_AVAILABLE_IN_ALL
void gtk_drop_down_set_from_strings (GtkDropDown *self,
const char *const *texts);
GDK_AVAILABLE_IN_ALL
void gtk_drop_down_set_model (GtkDropDown *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_drop_down_get_model (GtkDropDown *self);
GDK_AVAILABLE_IN_ALL
void gtk_drop_down_set_selected (GtkDropDown *self,
guint position);
GDK_AVAILABLE_IN_ALL
guint gtk_drop_down_get_selected (GtkDropDown *self);
GDK_AVAILABLE_IN_ALL
void gtk_drop_down_set_factory (GtkDropDown *self,
GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GtkListItemFactory *
gtk_drop_down_get_factory (GtkDropDown *self);
GDK_AVAILABLE_IN_ALL
void gtk_drop_down_set_list_factory (GtkDropDown *self,
GtkListItemFactory *factory);
GDK_AVAILABLE_IN_ALL
GtkListItemFactory *
gtk_drop_down_get_list_factory (GtkDropDown *self);
GDK_AVAILABLE_IN_ALL
void gtk_drop_down_set_expression (GtkDropDown *self,
GtkExpression *expression);
GDK_AVAILABLE_IN_ALL
GtkExpression * gtk_drop_down_get_expression (GtkDropDown *self);
GDK_AVAILABLE_IN_ALL
void gtk_drop_down_set_enable_search (GtkDropDown *self,
gboolean enable_search);
GDK_AVAILABLE_IN_ALL
gboolean gtk_drop_down_get_enable_search (GtkDropDown *self);
G_END_DECLS
#endif /* __GTK_DROP_DOWN_H__ */

Some files were not shown because too many files have changed in this diff Show More