Compare commits

...

165 Commits

Author SHA1 Message Date
Matthias Clasen
11eb6e97c0 builder-tool: Pass through CDATA where it makes sense
This avoids a ton of escaping for
GtkBuilderListItemFactory::bytes.
2019-12-13 14:18:39 -05:00
Matthias Clasen
431a28b06b builder: Improve type name mangling
When looking for the get_type function for GThemedIcon,
try both g_themed_icon_get_type and gthemed_icon_get_type
The former is what gio has, the latter is still supported
to avoid breaking gweather_location_get_type.

Update tests to cover this new case.
2019-12-13 13:35:57 -05:00
Matthias Clasen
9788f9c493 builder-tool: Don't try to find default boxed values
This doesn't work and yields an ugly, unnecessary
error message.
2019-12-13 13:11:59 -05:00
Matthias Clasen
a7d81347d2 gtk-builder-tool: More helpful error messages
Include line numbers in the error messages we
report, so it is possible to find the error.
2019-12-13 02:05:11 -05:00
Matthias Clasen
4ac7108128 docs: Reorganize list widgets in their own chapter 2019-12-13 06:12:09 +01:00
Benjamin Otte
91f77ee1d1 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-13 06:11:58 +01:00
Benjamin Otte
58a3396560 expression: Allow passing a this object to bind()
This gives a bit more control over the arguments passed to expressions.
2019-12-13 06:11:58 +01:00
Benjamin Otte
e06a6241a6 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-13 06:11:58 +01:00
Benjamin Otte
9201399a0d 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-13 06:11:58 +01:00
Benjamin Otte
e0236b401a xxx: Add a hack to make paintables transform to/from objects
See also: https://gitlab.gnome.org/GNOME/glib/merge_requests/1251
2019-12-13 06:11:58 +01:00
Benjamin Otte
564a01ffee inspector: Remove private struct for prop editor 2019-12-13 06:11:58 +01:00
Benjamin Otte
1b0640970b inspector: Make Controller page a GtkWidget 2019-12-13 06:11:58 +01:00
Benjamin Otte
89680a7410 inspector: Remove private struct from controllers 2019-12-13 06:11:58 +01:00
Benjamin Otte
43c7270f58 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-13 06:11:58 +01:00
Benjamin Otte
657e376b64 tests: Add testcolumnview 2019-12-13 06:11:57 +01:00
Benjamin Otte
d7cb078947 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-13 06:11:57 +01:00
Benjamin Otte
630778bbed constraint-editor: Don't poke around in widget internals 2019-12-13 06:11:57 +01:00
Benjamin Otte
a1ebd0c3b1 columnview: Fix styling with Adwaita
- Use "treeview" as the node name
- Add .view style class
2019-12-13 06:11:57 +01:00
Benjamin Otte
a821aff383 inspector: Port object tree to GtkColumnView 2019-12-13 06:11:57 +01:00
Benjamin Otte
21603f683a 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-13 06:11:57 +01:00
Benjamin Otte
3e4b6b728e 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-13 06:11:57 +01:00
Benjamin Otte
05e7f96e00 columnview: Implement GtkScrollable
Just forward it to the listview for now.
2019-12-13 06:11:57 +01:00
Benjamin Otte
ea18bae6cc columnview: Add listitems for the columns
They are not aligned in columns yet, but they do exist.
2019-12-13 06:11:57 +01:00
Benjamin Otte
34c27ffe4a 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-13 06:11:57 +01:00
Benjamin Otte
6e2324ea9c 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-13 06:11:57 +01:00
Benjamin Otte
36e798e8a5 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-13 06:11:57 +01:00
Benjamin Otte
b5fd69b69a 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-13 06:11:57 +01:00
Benjamin Otte
54159c2a15 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-13 06:11:57 +01:00
Benjamin Otte
84f398531d 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-13 06:11:57 +01:00
Benjamin Otte
c860406797 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-13 06:11:57 +01:00
Benjamin Otte
c307850dcf 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-13 06:11:57 +01:00
Benjamin Otte
2b2aef72d9 Add GtkSignalListItemFactory
So the poor Rust users can actually use this.

I would totally not use this ever!
2019-12-13 06:11:57 +01:00
Benjamin Otte
9f0d107a18 columnview: Allow adding/removing columns
... and make that work in UI files via <child>, too.
2019-12-13 06:11:57 +01:00
Benjamin Otte
5470a3c6b4 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-13 06:11:57 +01:00
Benjamin Otte
2b733dfa08 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-13 06:11:57 +01:00
Benjamin Otte
e5e376ff7b 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-13 06:11:57 +01:00
Benjamin Otte
71ace6763d listbase: Take over anchor handling
With that, pretty much all code but allocating the widgets is gone from
the gridview and listview.
2019-12-13 06:11:57 +01:00
Benjamin Otte
ad6a6cd2f3 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-13 06:11:57 +01:00
Benjamin Otte
4d565a35aa 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-13 06:11:57 +01:00
Benjamin Otte
7c44a95cea 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-13 06:11:57 +01:00
Benjamin Otte
cb74edc958 listbase: Move orientable implementation here 2019-12-13 06:11:57 +01:00
Benjamin Otte
65b638ac7d listbase: Move selection handling here 2019-12-13 06:11:57 +01:00
Benjamin Otte
f6355cca76 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-13 06:11:57 +01:00
Benjamin Otte
18eb44ee6b listbase: Move GtkScrollable implementation
Shared code between GtkGridView and GtkListView.
2019-12-13 06:11:57 +01:00
Benjamin Otte
9bfe96e397 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-13 06:11:57 +01:00
Benjamin Otte
a9d3dace47 gridview: Simplify allocation code
It doesn't fix the bug I'm after, but it looks a lot better.
2019-12-13 06:11:57 +01:00
Benjamin Otte
ec1d462e1f 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-13 06:11:57 +01:00
Benjamin Otte
22fd291d1f demo: Add a file browser demo 2019-12-13 06:11:57 +01:00
Benjamin Otte
fcd7964a3b gridview: Add move keybindings 2019-12-13 06:11:57 +01:00
Benjamin Otte
0121876c4a 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-13 06:11:57 +01:00
Benjamin Otte
65d8941425 gridview: Add a focus tracker
... and use that to properly update selections when moving around with
the arrow keys.
2019-12-13 06:11:57 +01:00
Benjamin Otte
655bdcf95f gridview: Implement list.scroll-to action 2019-12-13 06:11:57 +01:00
Benjamin Otte
7b41bef20e gridview: Add activation 2019-12-13 06:11:57 +01:00
Benjamin Otte
f23b392b2d 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-13 06:11:57 +01:00
Benjamin Otte
875d676ad1 gridview: Implement the list.select-item action 2019-12-13 06:11:57 +01:00
Benjamin Otte
872bb2a43c 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-13 06:11:57 +01:00
Benjamin Otte
99913833ea gridview: Implement anchors and scrolling 2019-12-13 06:11:57 +01:00
Benjamin Otte
5cbfadf65c widget: Add gtk_widget_get_size()
A little bit of convenience.
2019-12-13 06:11:57 +01:00
Benjamin Otte
737b91ae35 listitemmanager: Handle NULL factory
Just don't call it and create empty listitems.
2019-12-13 06:11:57 +01:00
Timm Bäder
6c13c8b0af demo: Use a listview as sidebar 2019-12-13 06:11:57 +01:00
Benjamin Otte
69fc00b4b6 gtk-demo: Introduce awards
We need a way to get a useful listbox, so here we go!
2019-12-13 06:11:57 +01:00
Benjamin Otte
bb71155bfc 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-13 06:11:57 +01:00
Benjamin Otte
60d07488be 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-13 06:11:57 +01:00
Benjamin Otte
3c3de02a3e 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-13 06:11:57 +01:00
Benjamin Otte
36e791fccf listview: Add move keybindings
My god, these are a lot.

And my god, these are complicated to get right.
2019-12-13 06:11:57 +01:00
Benjamin Otte
07d964ece6 listview: Add gtk_list_view_get_position_at_y() 2019-12-13 06:11:57 +01:00
Benjamin Otte
7f4ab1db86 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-13 06:11:57 +01:00
Benjamin Otte
27f5c1b148 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-13 06:11:57 +01:00
Benjamin Otte
764760ee5d 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-13 06:11:57 +01:00
Benjamin Otte
41c93153bf listview: Track focus movements and update selection
When focus moves via tab/arrow, we need to select the new item.
2019-12-13 06:11:56 +01:00
Benjamin Otte
8eee9897a8 listview: Implement activation
- a GtkListview::activate signal
- a GtkListItem::activatable property
- activate list items on double clicks and <Enter> presses
2019-12-13 06:11:56 +01:00
Benjamin Otte
1d2b8af320 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-13 06:11:56 +01:00
Benjamin Otte
e5a4ea71a4 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-13 06:11:56 +01:00
Benjamin Otte
6a496603bd popover: Remove unneeded vfunc
The vfunc is identical to the GtkWidget implementation it replaces. So
just keep using that one.
2019-12-13 06:11:56 +01:00
Benjamin Otte
36621a7f7d 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-13 06:11:56 +01:00
Benjamin Otte
ae86e6fe54 inspector: Use a GtkTreeExpander in the object tree 2019-12-13 06:11:56 +01:00
Benjamin Otte
9d1509671b inspector: Use a treeexpander in the recorder 2019-12-13 06:11:56 +01:00
Benjamin Otte
bd426eb89d 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-13 06:11:56 +01:00
Benjamin Otte
be96154c85 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-13 06:11:56 +01:00
Benjamin Otte
5d4dc52ff5 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-13 06:11:56 +01:00
Benjamin Otte
d45422328a listview: Pass the CSS name of listitems to the manager
... instead of hardcoding "row".
2019-12-13 06:11:56 +01:00
Benjamin Otte
007c70310b gridview: Implement GtkOrientable
Again, this is just the skeleton, because the Gridview does nothing yet.
2019-12-13 06:11:56 +01:00
Benjamin Otte
9bf3725d20 gridview: Add factory handling
Just copy the listview APIs.

Code still doesn't do anything with it.
2019-12-13 06:11:56 +01:00
Benjamin Otte
9dc88eed5a 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-13 06:11:56 +01:00
Benjamin Otte
1612741f32 textview: Make cursor work when blinking is disabled 2019-12-13 06:11:56 +01:00
Benjamin Otte
9f35883ee1 gtk-demo: Add a rough start at a Weather demo
This demos a horizontal listview.
2019-12-13 06:11:56 +01:00
Benjamin Otte
7c3db57a59 listview: Implement GtkOrientable 2019-12-13 06:11:56 +01:00
Benjamin Otte
ddf35d09e0 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-13 06:11:56 +01:00
Benjamin Otte
37906a099b 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-13 06:11:56 +01:00
Benjamin Otte
bd42d344a3 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-13 06:11:56 +01:00
Benjamin Otte
233db07715 testlistview: Load icons async
Speeds up loading by 4x, because out of view icons aren't loaded
anymore.
2019-12-13 06:11:56 +01:00
Benjamin Otte
84e1a21e05 testlistview: Port to directory list 2019-12-13 06:11:56 +01:00
Benjamin Otte
3f2f7bf074 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-13 06:11:56 +01:00
Benjamin Otte
8b67057c19 listitemfactory: Split implementation out
.. into gtkfunctionslistitemfactory.c

Now we can add a different implmenetation.
2019-12-13 06:11:56 +01:00
Benjamin Otte
e5a5f2bd90 listitemfactory: vfuncify
No functional changes other than a new indirection.
2019-12-13 06:11:56 +01:00
Benjamin Otte
aea62f9f77 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-13 06:11:56 +01:00
Benjamin Otte
b22acbcff6 listview: Add gtk_list_view_set_show_separators()
Do the same thing that GtkListBox does in commit
0249bd4f8a
2019-12-13 06:11:56 +01:00
Benjamin Otte
dfbcc9c50d 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-13 06:11:56 +01:00
Benjamin Otte
c1e1e0e683 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-13 06:11:56 +01:00
Benjamin Otte
bfb3da854d gridview: Implement GtkScrollable
We can now scroll all the nothing we display.

We also clip it properly.
2019-12-13 06:11:56 +01:00
Benjamin Otte
b077952901 listitemmanager: Move list of listitems here
All the listview infrastructure moved with it, so the next step is
moving that back...
2019-12-13 06:11:56 +01:00
Benjamin Otte
f1b300324e wayland: Remove function declaration for nonexisting function 2019-12-13 06:11:56 +01:00
Benjamin Otte
6a32109aed gridview: Add API for setting number of columns
The API isn't used yet.
2019-12-13 06:11:56 +01:00
Benjamin Otte
bd5562fe89 gtk: Add a GtkGridView skeleton 2019-12-13 06:11:56 +01:00
Benjamin Otte
0556f13532 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-13 06:10:08 +01:00
Benjamin Otte
34e6840d0a listview: Add initial support for displaying selections 2019-12-13 06:10:08 +01:00
Benjamin Otte
922991a3d7 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-13 06:10:08 +01:00
Benjamin Otte
37e4a0eca9 listview: Add selection properties to ListItem
This just brings the infrastructure into place, we're not using the
properties yet.
2019-12-13 06:10:08 +01:00
Benjamin Otte
345a18b1a1 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-13 06:10:08 +01:00
Benjamin Otte
6a3a936c4e 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-13 06:10:08 +01:00
Benjamin Otte
4e98bd33a5 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-13 06:10:08 +01:00
Benjamin Otte
2d89a8380f 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-13 06:10:08 +01:00
Benjamin Otte
d3be13875d testlistview: Show the row number
Always show the current row. This is mostly useful for debugging, not
for beauty.
2019-12-13 06:10:08 +01:00
Benjamin Otte
40b8b9742e 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-13 06:10:08 +01:00
Benjamin Otte
c06fee7460 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-13 06:10:08 +01:00
Benjamin Otte
7d152619bc 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-13 06:10:08 +01:00
Benjamin Otte
c65be391db 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-13 06:10:08 +01:00
Benjamin Otte
d516dd523e tests: Make animating listview do random resorts 2019-12-13 06:10:08 +01:00
Benjamin Otte
e431cd720f 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-13 06:10:08 +01:00
Benjamin Otte
9098382b76 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-13 06:10:08 +01:00
Benjamin Otte
a3cd145cbd listview: Add GtkListItem
GtkListItem is a generic row widget that is supposed to replace
GtkListBoxRow and GtkFlowBoxChild.
2019-12-13 06:10:08 +01:00
Benjamin Otte
08b225dd13 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-13 06:10:08 +01:00
Benjamin Otte
cbea54bb5b 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-13 06:10:08 +01:00
Benjamin Otte
058c7002d4 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-13 06:10:08 +01:00
Benjamin Otte
9722aef506 listview: Implement GtkScrollable
Scrolling in a very basic form is also supported
2019-12-13 06:10:08 +01:00
Benjamin Otte
2fb4df8973 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-13 06:10:08 +01:00
Benjamin Otte
bc637da95c 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-13 06:10:08 +01:00
Benjamin Otte
9575d54d4d gtk: Add a GtkListView skeleton 2019-12-13 06:09:56 +01:00
Benjamin Otte
c6a76c86c3 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-13 05:12:57 +01:00
Benjamin Otte
c04d63ff64 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-13 05:12:57 +01:00
Benjamin Otte
cde3f3bc9e 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-13 05:12:57 +01:00
Benjamin Otte
c7344eef76 builder: Make <lookup> type optional
If no type is set, use the type of the expression.
2019-12-13 05:12:57 +01:00
Benjamin Otte
d2088059d7 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-13 05:12:57 +01:00
Matthias Clasen
052a3c531f sorter: Add tests
Some basic tests for GtkSorter.
2019-12-13 05:12:57 +01:00
Benjamin Otte
8a07c5533e 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-13 05:12:57 +01:00
Benjamin Otte
54a9c07138 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-13 05:12:57 +01:00
Matthias Clasen
bbc30a5810 Redo sort list model with GtkSorter
Reshuffle the api to take full advantage
of GtkSorter. Update all callers.
2019-12-13 05:12:57 +01:00
Matthias Clasen
3760289bbf Add GtkNumericSorter
This sorter compares numbers obtained from items
by evaluating an expression.
2019-12-13 05:12:57 +01:00
Matthias Clasen
f618c1b3ec Add GtkMultiSorter
This is a sorter that tries multiple sorters in turn.
2019-12-13 05:12:57 +01:00
Matthias Clasen
c0f31f4236 Add GtkStringSorter
This is a GtkSorter implementation collating strings
2019-12-13 05:12:57 +01:00
Matthias Clasen
5aeb9d3d8f Add GtkCustomSorter
This is a GtkSorter implementation which uses a GCompareDataFunc.
2019-12-13 05:12:57 +01:00
Matthias Clasen
38ee3ab8b6 Add GtkSorter
This is a helper object for sorting, similar to GtkFilter.
2019-12-13 05:12:57 +01:00
Benjamin Otte
f50cc76655 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-13 05:12:57 +01:00
Matthias Clasen
7a52d8d8eb More expression tests
Test type mismatches, and the this pointer
during evaluation.
2019-12-13 05:12:57 +01:00
Benjamin Otte
c40fb49e38 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-13 05:12:57 +01:00
Benjamin Otte
196a64c380 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-13 05:12:57 +01:00
Benjamin Otte
211e8e6aec testsuite: Add expression tests 2019-12-13 05:12:57 +01:00
Benjamin Otte
0094aca2c3 expression: Add the ability to watch an expression 2019-12-13 05:12:57 +01:00
Benjamin Otte
6d204848ca builder: Add support for parsing expressions 2019-12-13 05:12:57 +01:00
Benjamin Otte
0a1cde18bb filter: Add tests
Some basic tests for GtkFilter
2019-12-13 05:12:57 +01:00
Benjamin Otte
c13e615f70 Add GtkMultiFilter, GtkAnyFilter, GtkEveryFilter
GtkMultiFilter is the abstract base class for managing multiple child
filter.
GtkAnyFilter and GtkEveryFilter are the actual implementations.
2019-12-13 05:12:57 +01:00
Benjamin Otte
3dc4257506 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-13 05:10:14 +01:00
Benjamin Otte
9b704bcda1 expression: Make property expression allow subexpressions 2019-12-13 05:09:30 +01:00
Benjamin Otte
2f8537fe0d expression: Add GtkObjectExpression
Weak refs break cycles...
2019-12-13 05:09:30 +01:00
Benjamin Otte
9bb71846ae 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-13 05:09:30 +01:00
Benjamin Otte
bb42b71252 filterlistmodel: Rewrite to use GtkFilter 2019-12-13 05:09:30 +01:00
Benjamin Otte
46451a5aa6 tests: Remove testtreemodel test
testlistview does everything this test does.
2019-12-13 05:09:30 +01:00
Benjamin Otte
f06914e84a Add GtkCustomFilter 2019-12-13 05:09:30 +01:00
Benjamin Otte
789a80f905 Add GtkFilter 2019-12-13 05:09:29 +01:00
Benjamin Otte
fbcc8a0afd 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-13 05:09:29 +01:00
Benjamin Otte
41e19757db 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-13 02:09:01 +01:00
Benjamin Otte
b7f99db780 builder: Turn last dlsym() function into a scope API
Looking up a get_type function by its name is now also part of
GtkBuilderScope.
2019-12-12 19:52:15 +01:00
Benjamin Otte
51a535027f builder: Add GtkBuilderScope
GtkBuilderScope is an interface that provides the scope that a builder
instance operates in.
It creates closures and resolves types. Language bindings are meant to
use this interface to customize the behavior of builder files, in
particular when instantiating templates.

A default implementation for C is provided via GtkBuilderCScope (to keep
with the awkward naming that glib uses for closures). It is derivable on
purpose so that languages or extensions that extend C can use it.

The reftest code in fact does derive GtkBuilderCScope for its own scope
implementation that implements looking up symbols in modules.

gtk-widget-factory was updated to use the new GtkBuilderCScope to add
its custom callback symbols.
So it does it different from gtk-demo, which uses the normal way of
exporting symbols for dlsym() and thereby makes the 2 demos test the 2
ways GtkBuilder uses for looking up symbols.
2019-12-12 19:52:15 +01:00
Benjamin Otte
ed076838b5 builder: Add gtk_builder_set_current_object()
Use it as the default object for expression binds and when connecting
signals. It is intended to work kind of as the "this" object while
parsing. In fact, the term "current object" was stolen from the Java
docs and various C++ tutorials for the this pointer.

Set the current object in gtk_widget_init_template() and
GtkListItemBuilder.

This more-or-less replaces the object passed to
gtk_builder_connect_signals() in GTK3.
2019-12-12 19:52:15 +01:00
Benjamin Otte
5d2de89207 builder: Add gtk_builder_lookup_object()
... and use it. This function looks up an object like
gtk_builder_get_object() but generates an error on failure.

Unlike the evil function _gtk_builder_lookup_object() which also
generates an error but hides it for later lookup.

Use this to avoid continuing applying properties when an error was
encountered.
2019-12-12 19:52:15 +01:00
196 changed files with 97586 additions and 3197 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;
}

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,19 @@
<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="/shortcuts">
<file>shortcuts.ui</file>
<file>shortcuts-builder.ui</file>
@@ -147,6 +167,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 +210,12 @@
<file>infobar.c</file>
<file>links.c</file>
<file>listbox.c</file>
<file>listview_applauncher.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

@@ -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

@@ -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,199 @@
/* 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 it support change
* notifications, but various older APIs do not support one.
*/
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 implemntations 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 being the #GtkBuilderListItemFactory which
* will to everything without requiring any code and just using a
* #GtkBuilder .ui file.
*/
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 widgets, 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 quite
* the same.
*/
coverflow = gtk_cover_flow_new_with_factory (factory);
/* We connect the activate signal here. It's the function you remember
* from 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 and 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,488 @@
/* Lists/Clocks
*
* This demo displays the time in different timezones.
*
* 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, location_label, "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, picture, "paintable");
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, time_label, "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_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,255 @@
/* Lists/File browser
*
* This demo shows off the different layouts that are quickly achievable
* with GtkGridView by implementing a file browser with different views.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
static GtkWidget *window = NULL;
/* Create a simple object that holds the data for the different views */
typedef struct _FileBrowserView FileBrowserView;
struct _FileBrowserView
{
GObject parent_instance;
GtkListItemFactory *factory;
char *icon_name;
GtkOrientation orientation;
};
enum {
PROP_0,
PROP_FACTORY,
PROP_ICON_NAME,
PROP_ORIENTATION,
N_PROPS
};
#define FILE_BROWSER_TYPE_VIEW (file_browser_view_get_type ())
G_DECLARE_FINAL_TYPE (FileBrowserView, file_browser_view, FILE_BROWSER, VIEW, GObject);
G_DEFINE_TYPE (FileBrowserView, file_browser_view, G_TYPE_OBJECT);
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
file_browser_view_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
FileBrowserView *self = FILE_BROWSER_VIEW (object);
switch (property_id)
{
case PROP_FACTORY:
g_value_set_object (value, self->factory);
break;
case PROP_ICON_NAME:
g_value_set_string (value, self->icon_name);
break;
case PROP_ORIENTATION:
g_value_set_enum (value, self->orientation);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
file_browser_view_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
FileBrowserView *self = FILE_BROWSER_VIEW (object);
switch (prop_id)
{
case PROP_FACTORY:
g_set_object (&self->factory, g_value_get_object (value));
break;
case PROP_ICON_NAME:
g_free (self->icon_name);
self->icon_name = g_value_dup_string (value);
break;
case PROP_ORIENTATION:
self->orientation = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
file_browser_view_finalize (GObject *object)
{
FileBrowserView *self = FILE_BROWSER_VIEW (object);
g_object_unref (self->factory);
g_free (self->icon_name);
G_OBJECT_CLASS (file_browser_view_parent_class)->dispose (object);
}
static void
file_browser_view_class_init (FileBrowserViewClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = file_browser_view_get_property;
gobject_class->set_property = file_browser_view_set_property;
gobject_class->finalize = file_browser_view_finalize;
properties[PROP_FACTORY] =
g_param_spec_object ("factory",
"factory",
"factory to use in the main view",
GTK_TYPE_LIST_ITEM_FACTORY,
G_PARAM_READWRITE);
properties[PROP_ICON_NAME] =
g_param_spec_string ("icon-name",
"icon name",
"icon to display for selecting this view",
NULL,
G_PARAM_READWRITE);
properties[PROP_ORIENTATION] =
g_param_spec_enum ("orientation",
"orientation",
"orientation of the view",
GTK_TYPE_ORIENTATION,
GTK_ORIENTATION_VERTICAL,
G_PARAM_READWRITE);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void file_browser_view_init (FileBrowserView *self)
{
}
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,256 @@
/* Lists/Settings
*
* This demo shows a settings viewer for GSettings.
*
* It demonstrates how to implement support for trees with listview.
*/
#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_class_init (SettingsKeyClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
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 gboolean
transform_settings_to_keys (GBinding *binding,
const GValue *from_value,
GValue *to_value,
gpointer unused)
{
GtkTreeListRow *treelistrow;
GSettings *settings;
GSettingsSchema *schema;
GListStore *store;
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);
qsort (keys, g_strv_length (keys), sizeof (char *), strvcmp);
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);
g_value_take_object (to_value, store);
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);
}
return G_LIST_MODEL (result);
}
static GtkWidget *window = NULL;
GtkWidget *
do_listview_settings (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *listview, *columnview;
GListModel *model;
GtkTreeListModel *treemodel;
GtkSingleSelection *selection;
GtkBuilder *builder;
g_type_ensure (SETTINGS_TYPE_KEY);
builder = gtk_builder_new_from_resource ("/listview_settings/listview_settings.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);
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,
NULL, 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);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkWindow" id="window">
<property name="title" translatable="yes">Settings</property>
<property name="default-width">600</property>
<property name="default-height">400</property>
<child>
<object class="GtkPaned">
<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="GtkScrolledWindow">
<child>
<object class="GtkColumnView" id="columnview">
<child>
<object class="GtkColumnViewColumn">
<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">
<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>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>

View File

@@ -0,0 +1,316 @@
/* Lists/Weather
*
* This demo shows a few of the rarer features of GtkListView and
* how they can be used to display weather information.
*
* The hourly weather info uses a horizontal listview. This is easy
* to achieve because GtkListView implements the GtkOrientable interface.
*
* To make the items in the list stand out more, the listview uses
* separators.
*
* A GtkNoSelectionModel is used to make sure no item in the list can be
* selected. All other interactions with the items is still possible.
*/
#include <gtk/gtk.h>
#define GTK_TYPE_WEATHER_INFO (gtk_weather_info_get_type ())
G_DECLARE_FINAL_TYPE (GtkWeatherInfo, gtk_weather_info, GTK, WEATHER_INFO, GObject)
typedef enum {
GTK_WEATHER_CLEAR,
GTK_WEATHER_FEW_CLOUDS,
GTK_WEATHER_FOG,
GTK_WEATHER_OVERCAST,
GTK_WEATHER_SCATTERED_SHOWERS,
GTK_WEATHER_SHOWERS,
GTK_WEATHER_SNOW,
GTK_WEATHER_STORM
} GtkWeatherType;
struct _GtkWeatherInfo
{
GObject parent_instance;
gint64 timestamp;
int temperature;
GtkWeatherType weather_type;
};
struct _GtkWeatherInfoClass
{
GObjectClass parent_class;
};
static void
gtk_weather_info_class_init (GtkWeatherInfoClass *klass)
{
}
static void
gtk_weather_info_init (GtkWeatherInfo *self)
{
}
G_DEFINE_TYPE (GtkWeatherInfo, gtk_weather_info, G_TYPE_OBJECT);
static GtkWeatherInfo *
gtk_weather_info_new (GDateTime *timestamp,
GtkWeatherInfo *copy_from)
{
GtkWeatherInfo *result;
result = g_object_new (GTK_TYPE_WEATHER_INFO, NULL);
result->timestamp = g_date_time_to_unix (timestamp);
if (copy_from)
{
result->temperature = copy_from->temperature;
result->weather_type = copy_from->weather_type;
g_object_unref (copy_from);
}
return result;
}
static GDateTime *
parse_timestamp (const char *string,
GTimeZone *timezone)
{
char *with_seconds;
GDateTime *result;
with_seconds = g_strconcat (string, ":00", NULL);
result = g_date_time_new_from_iso8601 (with_seconds, timezone);
g_free (with_seconds);
return result;
}
static GtkWeatherType
parse_weather_type (const char *clouds,
const char *precip,
GtkWeatherType fallback)
{
if (strstr (precip, "SN"))
return GTK_WEATHER_SNOW;
if (strstr (precip, "TS"))
return GTK_WEATHER_STORM;
if (strstr (precip, "DZ"))
return GTK_WEATHER_SCATTERED_SHOWERS;
if (strstr (precip, "SH") || strstr (precip, "RA"))
return GTK_WEATHER_SHOWERS;
if (strstr (precip, "FG"))
return GTK_WEATHER_FOG;
if (g_str_equal (clouds, "M") ||
g_str_equal (clouds, ""))
return fallback;
if (strstr (clouds, "OVC") ||
strstr (clouds, "BKN"))
return GTK_WEATHER_OVERCAST;
if (strstr (clouds, "BKN") ||
strstr (clouds, "SCT"))
return GTK_WEATHER_FEW_CLOUDS;
if (strstr (clouds, "VV"))
return GTK_WEATHER_FOG;
return GTK_WEATHER_CLEAR;
}
static double
parse_temperature (const char *s,
double fallback)
{
char *endptr;
double d;
d = g_ascii_strtod (s, &endptr);
if (*endptr != '\0')
return fallback;
return d;
}
static GListModel *
create_weather_model (void)
{
GListStore *store;
GTimeZone *utc;
GDateTime *timestamp;
GtkWeatherInfo *info;
GBytes *data;
char **lines;
guint i;
store = g_list_store_new (GTK_TYPE_WEATHER_INFO);
data = g_resources_lookup_data ("/listview_weather/listview_weather.txt", 0, NULL);
lines = g_strsplit (g_bytes_get_data (data, NULL), "\n", 0);
utc = g_time_zone_new_utc ();
timestamp = g_date_time_new (utc, 2011, 1, 1, 0, 0, 0);
info = gtk_weather_info_new (timestamp, NULL);
g_list_store_append (store, info);
for (i = 0; lines[i] != NULL && *lines[i]; i++)
{
char **fields;
GDateTime *date;
fields = g_strsplit (lines[i], ",", 0);
date = parse_timestamp (fields[0], utc);
while (g_date_time_difference (date, timestamp) > 30 * G_TIME_SPAN_MINUTE)
{
GDateTime *new_timestamp = g_date_time_add_hours (timestamp, 1);
g_date_time_unref (timestamp);
timestamp = new_timestamp;
info = gtk_weather_info_new (timestamp, info);
g_list_store_append (store, info);
}
info->temperature = parse_temperature (fields[1], info->temperature);
info->weather_type = parse_weather_type (fields[2], fields[3], info->weather_type);
g_date_time_unref (date);
g_strfreev (fields);
}
g_strfreev (lines);
g_bytes_unref (data);
g_time_zone_unref (utc);
return G_LIST_MODEL (store);
}
static void
setup_widget (GtkListItem *list_item,
gpointer unused)
{
GtkWidget *box, *child;
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_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 *
do_listview_weather (GtkWidget *do_widget)
{
if (window == NULL)
{
GtkWidget *listview, *sw;;
GListModel *model, *selection;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Weather");
g_signal_connect (window, "destroy",
G_CALLBACK(gtk_widget_destroyed), &window);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (window), sw);
listview = gtk_list_view_new_with_factory (
gtk_functions_list_item_factory_new (setup_widget,
bind_widget,
NULL, NULL));
gtk_orientable_set_orientation (GTK_ORIENTABLE (listview), GTK_ORIENTATION_HORIZONTAL);
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
model = create_weather_model ();
selection = G_LIST_MODEL (gtk_no_selection_new (model));
gtk_list_view_set_model (GTK_LIST_VIEW (listview), selection);
g_object_unref (selection);
g_object_unref (model);
gtk_container_add (GTK_CONTAINER (sw), listview);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}

File diff suppressed because it is too large Load Diff

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,12 @@ demos = files([
'listbox.c',
'flowbox.c',
'list_store.c',
'listview_applauncher.c',
'listview_clocks.c',
'listview_filebrowser.c',
'listview_minesweeper.c',
'listview_settings.c',
'listview_weather.c',
'markup.c',
'menus.c',
'modelbutton.c',
@@ -86,6 +93,7 @@ demos = files([
gtkdemo_deps = [ libgtk_dep, ]
extra_demo_sources = files(['main.c',
'award.c',
'gtkfishbowl.c',
'fontplane.c',
'gtkgears.c',
@@ -96,7 +104,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

@@ -1663,6 +1663,7 @@ static void
activate (GApplication *app)
{
GtkBuilder *builder;
GtkBuilderScope *scope;
GtkWindow *window;
GtkWidget *widget;
GtkWidget *widget2;
@@ -1716,18 +1717,23 @@ activate (GApplication *app)
g_object_unref (provider);
builder = gtk_builder_new ();
gtk_builder_add_callback_symbol (builder, "on_entry_icon_release", (GCallback)on_entry_icon_release);
gtk_builder_add_callback_symbol (builder, "on_scale_button_value_changed", (GCallback)on_scale_button_value_changed);
gtk_builder_add_callback_symbol (builder, "on_scale_button_query_tooltip", (GCallback)on_scale_button_query_tooltip);
gtk_builder_add_callback_symbol (builder, "on_record_button_toggled", (GCallback)on_record_button_toggled);
gtk_builder_add_callback_symbol (builder, "on_page_combo_changed", (GCallback)on_page_combo_changed);
gtk_builder_add_callback_symbol (builder, "on_range_from_changed", (GCallback)on_range_from_changed);
gtk_builder_add_callback_symbol (builder, "on_range_to_changed", (GCallback)on_range_to_changed);
gtk_builder_add_callback_symbol (builder, "tab_close_cb", (GCallback)tab_close_cb);
gtk_builder_add_callback_symbol (builder, "increase_icon_size", (GCallback)increase_icon_size);
gtk_builder_add_callback_symbol (builder, "decrease_icon_size", (GCallback)decrease_icon_size);
gtk_builder_add_callback_symbol (builder, "reset_icon_size", (GCallback)reset_icon_size);
gtk_builder_add_callback_symbol (builder, "osd_frame_pressed", (GCallback)osd_frame_pressed);
scope = gtk_builder_cscope_new ();
gtk_builder_cscope_add_callback_symbols (GTK_BUILDER_CSCOPE (scope),
"on_entry_icon_release", (GCallback)on_entry_icon_release,
"on_scale_button_value_changed", (GCallback)on_scale_button_value_changed,
"on_scale_button_query_tooltip", (GCallback)on_scale_button_query_tooltip,
"on_record_button_toggled", (GCallback)on_record_button_toggled,
"on_page_combo_changed", (GCallback)on_page_combo_changed,
"on_range_from_changed", (GCallback)on_range_from_changed,
"on_range_to_changed", (GCallback)on_range_to_changed,
"tab_close_cb", (GCallback)tab_close_cb,
"increase_icon_size", (GCallback)increase_icon_size,
"decrease_icon_size", (GCallback)decrease_icon_size,
"reset_icon_size", (GCallback)reset_icon_size,
"osd_frame_pressed", (GCallback)osd_frame_pressed,
NULL);
gtk_builder_set_scope (builder, scope);
g_object_unref (scope);
gtk_builder_add_from_resource (builder, "/org/gtk/WidgetFactory4/widget-factory.ui", NULL);
window = (GtkWindow *)gtk_builder_get_object (builder, "window");

View File

@@ -44,15 +44,30 @@
<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" />
<xi:include href="xml/gtktreelistmodel.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/gtktreelistmodel.xml" />
<xi:include href="xml/gtkdirectorylist.xml" />
</chapter>
<chapter id="Application">
@@ -115,6 +130,20 @@
<xi:include href="xml/gtkconstraintguide.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" />
</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/gtktreeexpander.xml" />
</chapter>
<chapter id="DisplayWidgets">
<title>Display Widgets</title>
<xi:include href="xml/gtklabel.xml" />

View File

@@ -493,6 +493,147 @@ 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>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
<SUBSECTION Standard>
GTK_LIST_VIEW
GTK_LIST_VIEW_CLASS
GTK_LIST_VIEW_GET_CLASS
GTK_IS_LIST_VIEW
GTK_IS_LIST_VIEW_CLASS
GTK_TYPE_LIST_VIEW
<SUBSECTION Private>
gtk_list_view_get_type
</SECTION>
<SECTION>
<FILE>gtkcolumnview</FILE>
<TITLE>GtkColumnView</TITLE>
GtkColumnView
gtk_column_view_new
gtk_column_view_get_columns
gtk_column_view_get_model
gtk_column_view_set_model
gtk_column_view_get_show_separators
gtk_column_view_set_show_separators
<SUBSECTION Standard>
GTK_COLUMN_VIEW
GTK_COLUMN_VIEW_CLASS
GTK_COLUMN_VIEW_GET_CLASS
GTK_IS_COLUMN_VIEW
GTK_IS_COLUMN_VIEW_CLASS
GTK_TYPE_COLUMN_VIEW
<SUBSECTION Private>
gtk_column_view_get_type
</SECTION>
<SECTION>
<FILE>gtkcolumnviewcolumn</FILE>
<TITLE>GtkColumnViewColumn</TITLE>
GtkColumnViewColumn
gtk_column_view_column_new
gtk_column_view_column_new_with_factory
gtk_column_view_column_get_column_view
gtk_column_view_column_set_factory
gtk_column_view_column_get_factory
gtk_column_view_column_set_title
gtk_column_view_column_get_title
<SUBSECTION Standard>
GTK_COLUMN_VIEW_COLUMN
GTK_COLUMN_VIEW_COLUMN_CLASS
GTK_COLUMN_VIEW_COLUMN_GET_CLASS
GTK_IS_COLUMN_VIEW_COLUMN
GTK_IS_COLUMN_VIEW_COLUMN_CLASS
GTK_TYPE_COLUMN_VIEW_COLUMN
<SUBSECTION Private>
gtk_column_view_column_get_type
</SECTION>
<SECTION>
<FILE>gtkgridview</FILE>
<TITLE>GtkGridView</TITLE>
GtkGridView
gtk_grid_view_new
gtk_grid_view_set_model
gtk_grid_view_get_model
gtk_grid_view_set_max_columns
gtk_grid_view_get_max_columns
gtk_grid_view_set_min_columns
gtk_grid_view_get_min_columns
<SUBSECTION Standard>
GTK_GRID_VIEW
GTK_GRID_VIEW_CLASS
GTK_GRID_VIEW_GET_CLASS
GTK_IS_GRID_VIEW
GTK_IS_GRID_VIEW_CLASS
GTK_TYPE_GRID_VIEW
<SUBSECTION Private>
gtk_grid_view_get_type
</SECTION>
<SECTION>
<FILE>gtkbuildable</FILE>
GtkBuildable
@@ -516,21 +657,41 @@ GTK_BUILDABLE_CLASS
GTK_BUILDABLE_GET_IFACE
</SECTION>
<SECTION>
<FILE>gtkbuilderscope</FILE>
<TITLE>GtkBuilderScope</TITLE>
gtk_builder_cscope_new
gtk_builder_cscope_add_callback_symbol
gtk_builder_cscope_add_callback_symbols
gtk_builder_cscope_lookup_callback_symbol
<SUBSECTION Standard>
GTK_BUILDER_SCOPE
GTK_IS_BUILDER_SCOPE
GTK_TYPE_BUILDER_SCOPE
GTK_BUILDER_SCOPE_INTERFACE
GTK_IS_BUILDER_SCOPE_INTERFACE
GTK_BUILDER_SCOPE_GET_INTERFACE
GTK_BUILDER_CSCOPE
GTK_IS_BUILDER_CSCOPE
GTK_TYPE_BUILDER_CSCOPE
GTK_BUILDER_CSCOPE_CLASS
GTK_IS_BUILDER_CSCOPE_CLASS
GTK_BUILDER_CSCOPE_GET_CLASS
<SUBSECTION Private>
gtk_builder_scope_get_type
gtk_builder_cscope_get_type
</SECTION>
<SECTION>
<FILE>gtkbuilder</FILE>
<TITLE>GtkBuilder</TITLE>
GtkBuilder
GtkBuilderClosureFunc
GtkBuilderError
gtk_builder_new
gtk_builder_new_from_file
gtk_builder_new_from_resource
gtk_builder_new_from_string
gtk_builder_add_callback_symbol
gtk_builder_add_callback_symbols
gtk_builder_lookup_callback_symbol
gtk_builder_create_closure
gtk_builder_create_cclosure
gtk_builder_add_from_file
gtk_builder_add_from_resource
gtk_builder_add_from_string
@@ -541,10 +702,13 @@ gtk_builder_extend_with_template
gtk_builder_get_object
gtk_builder_get_objects
gtk_builder_expose_object
gtk_builder_set_current_object
gtk_builder_get_current_object
gtk_builder_set_scope
gtk_builder_get_scope
gtk_builder_set_translation_domain
gtk_builder_get_translation_domain
gtk_builder_get_type_from_name
gtk_builder_set_closure_func
gtk_builder_value_from_string
gtk_builder_value_from_string_type
GTK_BUILDER_WARN_INVALID_CHILD_TYPE
@@ -560,7 +724,6 @@ GTK_BUILDER_GET_CLASS
<SUBSECTION Private>
gtk_builder_get_type
gtk_builder_error_quark
GtkBuilderPrivate
</SECTION>
<SECTION>
@@ -1323,6 +1486,91 @@ 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>
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>
@@ -1331,9 +1579,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
@@ -2663,17 +2910,106 @@ GTK_SLICE_LIST_MODEL_GET_CLASS
gtk_slice_list_model_get_type
</SECTION>
<SECTION>
<FILE>gtksorter</FILE>
<TITLE>GtkSorter</TITLE>
GtkSorter
GtkSorderOrder
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>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
@@ -3472,6 +3808,26 @@ gtk_tree_list_model_get_type
gtk_tree_list_row_get_type
</SECTION>
<SECTION>
<FILE>gtktreeexpander</FILE>
<TITLE>GtkTreeExpander</TITLE>
gtk_tree_expander_new
gtk_tree_expander_get_child
gtk_tree_expander_set_child
gtk_tree_expander_get_item
gtk_tree_expander_get_list_row
gtk_tree_expander_set_list_row
<SUBSECTION Standard>
GTK_TREE_EXPANDER
GTK_IS_TREE_EXPANDER
GTK_TYPE_TREE_EXPANDER
GTK_TREE_EXPANDER_CLASS
GTK_IS_TREE_EXPANDER_CLASS
GTK_TREE_EXPANDER_GET_CLASS
<SUBSECTION Private>
gtk_tree_expander_get_type
</SECTION>
<SECTION>
<FILE>gtktreemodel</FILE>
<TITLE>GtkTreeModel</TITLE>
@@ -4559,6 +4915,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
@@ -4666,7 +5023,7 @@ gtk_widget_class_bind_template_child_internal_private
gtk_widget_class_bind_template_child_full
gtk_widget_class_bind_template_callback
gtk_widget_class_bind_template_callback_full
gtk_widget_class_set_closure_func
gtk_widget_class_set_template_scope
<SUBSECTION>
gtk_widget_observe_children
@@ -5273,6 +5630,8 @@ GtkDeleteType
GtkDirectionType
GtkJustification
GtkMovementStep
GtkOrdering
gtk_ordering_from_cmpfunc
GtkOrientation
GtkPackType
GtkPositionType
@@ -7405,3 +7764,53 @@ 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_unwatch
<SUBSECTION>
gtk_property_expression_new
gtk_constant_expression_new
gtk_constant_expression_new_for_value
gtk_object_expression
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>

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
@@ -47,6 +48,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
@@ -54,8 +57,11 @@ 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_editable_get_type
gtk_entry_buffer_get_type
@@ -66,12 +72,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
@@ -97,6 +105,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
@@ -109,9 +118,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
@@ -125,10 +137,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
@@ -173,11 +188,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
@@ -185,6 +202,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

View File

@@ -131,6 +131,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)

View File

@@ -55,6 +55,8 @@
#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>
#include <gtk/gtkcellarea.h>
@@ -81,15 +83,20 @@
#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>
@@ -105,6 +112,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>
@@ -113,7 +121,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>
@@ -121,6 +131,7 @@
#include <gtk/gtkfontchooserdialog.h>
#include <gtk/gtkfontchooserwidget.h>
#include <gtk/gtkframe.h>
#include <gtk/gtkfunctionslistitemfactory.h>
#include <gtk/gtkgesture.h>
#include <gtk/gtkgestureclick.h>
#include <gtk/gtkgesturedrag.h>
@@ -134,6 +145,7 @@
#include <gtk/gtkglarea.h>
#include <gtk/gtkgrid.h>
#include <gtk/gtkgridlayout.h>
#include <gtk/gtkgridview.h>
#include <gtk/gtkheaderbar.h>
#include <gtk/gtkicontheme.h>
#include <gtk/gtkiconview.h>
@@ -146,9 +158,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>
@@ -163,10 +179,13 @@
#include <gtk/gtkmenutoolbutton.h>
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkmountoperation.h>
#include <gtk/gtkmultifilter.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>
@@ -210,9 +229,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>
@@ -222,6 +243,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>
@@ -242,6 +265,7 @@
#include <gtk/gtktooltip.h>
#include <gtk/gtktestutils.h>
#include <gtk/gtktreednd.h>
#include <gtk/gtktreeexpander.h>
#include <gtk/gtktreelistmodel.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtktreemodelfilter.h>

View File

@@ -162,23 +162,6 @@
* “last_modification_time” attribute is also allowed, but it does not
* have a meaning to the builder.
*
* By default, GTK+ tries to find functions (like the handlers for
* signals) by using g_module_symbol(), but this can be changed by
* passing a custom #GtkBuilderClosureFunc to gtk_builder_set_closure_func().
* Bindings in particular will want to make use of this functionality to
* allow language-specific name mangling and namespacing.
*
* The default closure function uses symbols explicitly added to @builder
* with prior calls to gtk_builder_add_callback_symbol(). In the case that
* symbols are not explicitly added; it uses #GModules introspective
* features (by opening the module %NULL) to look at the applications symbol
* table. From here it tries to match the signal function names given in the
* interface description with symbols in the application.
*
* Note that unless gtk_builder_add_callback_symbol() is called for
* all signal callbacks which are referenced by the loaded XML, this
* functionality will require that #GModule be supported on the platform.
*
* If you rely on #GModule support to lookup callbacks in the symbol table,
* the following details should be noted:
*
@@ -230,16 +213,18 @@
#include <stdlib.h>
#include <string.h> /* strlen */
#include "gtkbuilder.h"
#include "gtkbuildable.h"
#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"
#include "gtktypebuiltins.h"
#include "gtkicontheme.h"
#include "gtktestutils.h"
static void gtk_builder_finalize (GObject *object);
static void gtk_builder_set_property (GObject *object,
@@ -253,6 +238,8 @@ static void gtk_builder_get_property (GObject *object,
enum {
PROP_0,
PROP_CURRENT_OBJECT,
PROP_SCOPE,
PROP_TRANSLATION_DOMAIN,
LAST_PROP
};
@@ -273,27 +260,35 @@ typedef struct
{
gchar *domain;
GHashTable *objects;
GHashTable *callbacks;
GSList *delayed_properties;
GSList *signals;
GSList *bindings;
GModule *module;
gchar *filename;
gchar *resource_prefix;
GType template_type;
GtkBuilderClosureFunc closure_func;
gpointer closure_data;
GDestroyNotify closure_destroy;
GObject *current_object;
GtkBuilderScope *scope;
} GtkBuilderPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (GtkBuilder, gtk_builder, G_TYPE_OBJECT)
static void
gtk_builder_dispose (GObject *object)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (GTK_BUILDER (object));
g_clear_object (&priv->current_object);
g_clear_object (&priv->scope);
G_OBJECT_CLASS (gtk_builder_parent_class)->dispose (object);
}
static void
gtk_builder_class_init (GtkBuilderClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = gtk_builder_dispose;
gobject_class->finalize = gtk_builder_finalize;
gobject_class->set_property = gtk_builder_set_property;
gobject_class->get_property = gtk_builder_get_property;
@@ -313,6 +308,30 @@ gtk_builder_class_init (GtkBuilderClass *klass)
NULL,
GTK_PARAM_READWRITE);
/**
* GtkBuilder:current-object:
*
* The object the builder is evaluating for.
*/
builder_props[PROP_CURRENT_OBJECT] =
g_param_spec_object ("current-object",
P_("Current object"),
P_("The object the builder is evaluating for"),
G_TYPE_OBJECT,
GTK_PARAM_READWRITE);
/**
* GtkBuilder:scope:
*
* The scope the builder is operating in
*/
builder_props[PROP_SCOPE] =
g_param_spec_object ("scope",
P_("Scope"),
P_("The scope the builder is operating in"),
GTK_TYPE_BUILDER_SCOPE,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT);
g_object_class_install_properties (gobject_class, LAST_PROP, builder_props);
}
@@ -336,18 +355,11 @@ gtk_builder_finalize (GObject *object)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (GTK_BUILDER (object));
if (priv->closure_destroy)
priv->closure_destroy (priv->closure_data);
g_clear_pointer (&priv->module, g_module_close);
g_free (priv->domain);
g_free (priv->filename);
g_free (priv->resource_prefix);
g_hash_table_destroy (priv->objects);
if (priv->callbacks)
g_hash_table_destroy (priv->callbacks);
g_slist_free_full (priv->signals, (GDestroyNotify)_free_signal_info);
@@ -364,9 +376,18 @@ gtk_builder_set_property (GObject *object,
switch (prop_id)
{
case PROP_CURRENT_OBJECT:
gtk_builder_set_current_object (builder, g_value_get_object (value));
break;
case PROP_SCOPE:
gtk_builder_set_scope (builder, g_value_get_object (value));
break;
case PROP_TRANSLATION_DOMAIN:
gtk_builder_set_translation_domain (builder, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -384,88 +405,24 @@ gtk_builder_get_property (GObject *object,
switch (prop_id)
{
case PROP_CURRENT_OBJECT:
g_value_set_object (value, priv->current_object);
break;
case PROP_SCOPE:
g_value_set_object (value, priv->scope);
break;
case PROP_TRANSLATION_DOMAIN:
g_value_set_string (value, priv->domain);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/*
* Try to map a type name to a _get_type function
* and call it, eg:
*
* GtkWindow -> gtk_window_get_type
* GtkHBox -> gtk_hbox_get_type
* GtkUIManager -> gtk_ui_manager_get_type
* GWeatherLocation -> gweather_location_get_type
*
* Keep in sync with testsuite/gtk/typename.c !
*/
static gchar *
type_name_mangle (const gchar *name)
{
GString *symbol_name = g_string_new ("");
gint i;
for (i = 0; name[i] != '\0'; i++)
{
/* skip if uppercase, first or previous is uppercase */
if ((name[i] == g_ascii_toupper (name[i]) &&
i > 0 && name[i-1] != g_ascii_toupper (name[i-1])) ||
(i > 2 && name[i] == g_ascii_toupper (name[i]) &&
name[i-1] == g_ascii_toupper (name[i-1]) &&
name[i-2] == g_ascii_toupper (name[i-2])))
g_string_append_c (symbol_name, '_');
g_string_append_c (symbol_name, g_ascii_tolower (name[i]));
}
g_string_append (symbol_name, "_get_type");
return g_string_free (symbol_name, FALSE);
}
GModule *
gtk_builder_get_module (GtkBuilder *builder)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
if (priv->module == NULL)
{
if (!g_module_supported ())
return NULL;
priv->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
}
return priv->module;
}
static GType
gtk_builder_resolve_type_lazily (GtkBuilder *builder,
const gchar *name)
{
GModule *module;
GTypeGetFunc func;
gchar *symbol;
GType gtype = G_TYPE_INVALID;
module = gtk_builder_get_module (builder);
if (!module)
return G_TYPE_INVALID;
symbol = type_name_mangle (name);
if (g_module_symbol (module, symbol, (gpointer)&func))
gtype = func ();
g_free (symbol);
return gtype;
}
/*
* GtkBuilder virtual methods
*/
@@ -565,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) &&
@@ -710,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,
@@ -814,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,
@@ -996,11 +996,13 @@ _gtk_builder_add_signals (GtkBuilder *builder,
g_slist_copy (signals));
}
static void
gtk_builder_apply_delayed_properties (GtkBuilder *builder)
static gboolean
gtk_builder_apply_delayed_properties (GtkBuilder *builder,
GError **error)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
GSList *l, *props;
gboolean result = TRUE;
/* take the list over from the builder->priv.
*
@@ -1015,53 +1017,100 @@ gtk_builder_apply_delayed_properties (GtkBuilder *builder)
DelayedProperty *property = l->data;
GObject *object, *obj;
object = g_hash_table_lookup (priv->objects, property->object);
g_assert (object != NULL);
if (result)
{
object = g_hash_table_lookup (priv->objects, property->object);
g_assert (object != NULL);
obj = _gtk_builder_lookup_object (builder, property->value, property->line, property->col);
if (obj)
g_object_set (object, property->pspec->name, obj, NULL);
obj = gtk_builder_lookup_object (builder, property->value, property->line, property->col, error);
if (obj)
g_object_set (object, property->pspec->name, obj, NULL);
else
result = FALSE;
}
g_free (property->value);
g_free (property->object);
g_slice_free (DelayedProperty, property);
}
g_slist_free (props);
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 void
gtk_builder_create_bindings (GtkBuilder *builder)
static inline gboolean
gtk_builder_create_bindings (GtkBuilder *builder,
GError **error)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
GSList *l;
gboolean result = TRUE;
for (l = priv->bindings; l; l = l->next)
{
BindingInfo *info = l->data;
GObject *source;
CommonInfo *common_info = l->data;
source = _gtk_builder_lookup_object (builder, info->source, info->line, info->col);
if (source)
g_object_bind_property (source, info->source_property,
info->target, info->target_pspec->name,
info->flags);
if (common_info->tag_type == TAG_BINDING)
{
BindingInfo *info = l->data;
GObject *source;
free_binding_info (info, NULL);
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
error = 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, object, info->target_pspec->name);
}
}
free_binding_expression_info (info);
}
}
g_slist_free (priv->bindings);
priv->bindings = NULL;
return result;
}
/**
@@ -1222,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)
@@ -1238,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,
@@ -1248,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;
@@ -1261,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,
@@ -1660,30 +1709,106 @@ gtk_builder_expose_object (GtkBuilder *builder,
}
/**
* GtkBuilderClosureFunc:
* @builder: a #GtkBuilder
* @function_name: name of the function to create a closure for
* @swapped: if the closure should swap user data and instance
* @object: (nullable): object to use as user data for the closure
* @user_data: user data passed when setting the function
* @error: location for error when creating the closure fails
* gtk_builder_get_current_object:
* @self: a #GtkBuilder
*
* Prototype of function used to create closures by @builder. It is meant
* for influencing how @function_name is resolved.
* Gets the current object set via gtk_builder_set_current_object().
*
* This function is most useful for bindings and can be used with
* gtk_builder_set_closure_func() or gtk_widget_class_set_closure_func()
* to allow creating closures for functions defined in the binding's
* language.
* Returns: (nullable) (transfer none): the current object
**/
GObject *
gtk_builder_get_current_object (GtkBuilder *self)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self);
g_return_val_if_fail (GTK_IS_BUILDER (self), NULL);
return priv->current_object;
}
/**
* gtk_builder_set_current_object:
* @self: a #GtkBuilder
* @current_object: (nullable) (transfer none): the new current object or
* %NULL for none
*
* If the given @function_name does not match a function name or when the
* arguments cannot be supported by the bindings, bindings should return
* %NULL and set @error. Usually %GTK_BUILDER_ERROR_INVALID_FUNCTION will
* be the right error code to use.
* Sets the current object for the @self. The current object can be
* tought of as the `this` object that the builder is working for and
* will often be used as the default object when an object is optional.
*
* Returns: (nullable): a new #GClosure or %NULL when no closure could
* be created and @error was set.
*/
* gtk_widget_init_template() for example will set the current object to
* the widget the template is inited for.
* For functions like gtk_builder_new_from_resource(), the current object
* will be %NULL.
**/
void
gtk_builder_set_current_object (GtkBuilder *self,
GObject *current_object)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self);
g_return_if_fail (GTK_IS_BUILDER (self));
g_return_if_fail (current_object || G_IS_OBJECT (current_object));
if (!g_set_object (&priv->current_object, current_object))
return;
g_object_notify_by_pspec (G_OBJECT (self), builder_props[PROP_CURRENT_OBJECT]);
}
/**
* gtk_builder_get_scope:
* @self: a #GtkBuilder
*
* Gets the scope in use that was set via gtk_builder_set_scope().
*
* See the #GtkBuilderScope documentation for details.
*
* Returns: (transfer none): the current scope
**/
GtkBuilderScope *
gtk_builder_get_scope (GtkBuilder *self)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self);
g_return_val_if_fail (GTK_IS_BUILDER (self), NULL);
return priv->scope;
}
/**
* gtk_builder_set_current_object:
* @self: a #GtkBuilder
* @scope: (nullable) (transfer none): the scope to use or
* %NULL for the default
*
* Sets the scope the builder should operate in.
*
* If @scope is %NULL a new #GtkBuilderCScope will be created.
*
* See the #GtkBuilderScope documentation for details.
**/
void
gtk_builder_set_scope (GtkBuilder *self,
GtkBuilderScope *scope)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self);
g_return_if_fail (GTK_IS_BUILDER (self));
g_return_if_fail (scope == NULL || GTK_IS_BUILDER_SCOPE (scope));
if (scope && priv->scope == scope)
return;
g_clear_object (&priv->scope);
if (scope)
priv->scope = g_object_ref (scope);
else
priv->scope = gtk_builder_cscope_new ();
g_object_notify_by_pspec (G_OBJECT (self), builder_props[PROP_SCOPE]);
}
static gboolean
gtk_builder_connect_signals (GtkBuilder *builder,
@@ -1755,9 +1880,9 @@ gboolean
_gtk_builder_finish (GtkBuilder *builder,
GError **error)
{
gtk_builder_apply_delayed_properties (builder);
gtk_builder_create_bindings (builder);
return gtk_builder_connect_signals (builder, error);
return gtk_builder_apply_delayed_properties (builder, error)
&& gtk_builder_create_bindings (builder, error)
&& gtk_builder_connect_signals (builder, error);
}
/**
@@ -2460,21 +2585,15 @@ GType
gtk_builder_get_type_from_name (GtkBuilder *builder,
const gchar *type_name)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
GType type;
g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID);
g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID);
type = g_type_from_name (type_name);
type = gtk_builder_scope_get_type_from_name (priv->scope, builder, type_name);
if (type == G_TYPE_INVALID)
{
type = gtk_builder_resolve_type_lazily (builder, type_name);
if (type == G_TYPE_INVALID)
{
gtk_test_register_all_types ();
type = g_type_from_name (type_name);
}
}
return G_TYPE_INVALID;
if (G_TYPE_IS_CLASSED (type))
g_type_class_unref (g_type_class_ref (type));
@@ -2542,172 +2661,11 @@ _gtk_builder_get_template_type (GtkBuilder *builder)
return priv->template_type;
}
/**
* gtk_builder_add_callback_symbol:
* @builder: a #GtkBuilder
* @callback_name: The name of the callback, as expected in the XML
* @callback_symbol: (scope async): The callback pointer
*
* Adds the @callback_symbol to the scope of @builder under the given @callback_name.
*
* Using this function overrides the behavior of gtk_builder_create_closure()
* for any callback symbols that are added. Using this method allows for better
* encapsulation as it does not require that callback symbols be declared in
* the global namespace.
*/
void
gtk_builder_add_callback_symbol (GtkBuilder *builder,
const gchar *callback_name,
GCallback callback_symbol)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
g_return_if_fail (GTK_IS_BUILDER (builder));
g_return_if_fail (callback_name && callback_name[0]);
g_return_if_fail (callback_symbol != NULL);
if (!priv->callbacks)
priv->callbacks = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
g_hash_table_insert (priv->callbacks, g_strdup (callback_name), callback_symbol);
}
/**
* gtk_builder_add_callback_symbols:
* @builder: a #GtkBuilder
* @first_callback_name: The name of the callback, as expected in the XML
* @first_callback_symbol: (scope async): The callback pointer
* @...: A list of callback name and callback symbol pairs terminated with %NULL
*
* A convenience function to add many callbacks instead of calling
* gtk_builder_add_callback_symbol() for each symbol.
*/
void
gtk_builder_add_callback_symbols (GtkBuilder *builder,
const gchar *first_callback_name,
GCallback first_callback_symbol,
...)
{
va_list var_args;
const gchar *callback_name;
GCallback callback_symbol;
g_return_if_fail (GTK_IS_BUILDER (builder));
g_return_if_fail (first_callback_name && first_callback_name[0]);
g_return_if_fail (first_callback_symbol != NULL);
callback_name = first_callback_name;
callback_symbol = first_callback_symbol;
va_start (var_args, first_callback_symbol);
do {
gtk_builder_add_callback_symbol (builder, callback_name, callback_symbol);
callback_name = va_arg (var_args, const gchar*);
if (callback_name)
callback_symbol = va_arg (var_args, GCallback);
} while (callback_name != NULL);
va_end (var_args);
}
/**
* gtk_builder_lookup_callback_symbol: (skip)
* @builder: a #GtkBuilder
* @callback_name: The name of the callback
*
* Fetches a symbol previously added to @builder
* with gtk_builder_add_callback_symbols()
*
* This function is intended for possible use in language bindings
* or for any case that one might be customizing signal connections
* using gtk_builder_set_closure_func().
*
* Returns: (nullable): The callback symbol in @builder for @callback_name, or %NULL
*/
GCallback
gtk_builder_lookup_callback_symbol (GtkBuilder *builder,
const gchar *callback_name)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
g_return_val_if_fail (callback_name && callback_name[0], NULL);
if (!priv->callbacks)
return NULL;
return g_hash_table_lookup (priv->callbacks, callback_name);
}
/**
* gtk_builder_set_closure_func: (skip)
* @builder: a #GtkBuilder
* @closure_func: (allow-none): function to call when creating
* closures or %NULL to use the default
* @user_data: (nullable): user data to pass to @closure_func
* @user_destroy: destroy function for user data
*
* Sets the function to call for creating closures.
* gtk_builder_create_closure() will use this function instead
* of gtk_builder_create_cclosure().
*
* This is useful for bindings.
**/
void
gtk_builder_set_closure_func (GtkBuilder *builder,
GtkBuilderClosureFunc closure_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
g_return_if_fail (GTK_IS_BUILDER (builder));
if (priv->closure_destroy)
priv->closure_destroy (priv->closure_data);
priv->closure_func = closure_func;
priv->closure_data = user_data;
priv->closure_destroy = user_destroy;
}
static GClosure *
gtk_builder_create_closure_for_funcptr (GtkBuilder *builder,
GCallback callback,
gboolean swapped,
GObject *object)
{
GClosure *closure;
if (object)
{
if (swapped)
closure = g_cclosure_new_object_swap (callback, object);
else
closure = g_cclosure_new_object (callback, object);
}
else
{
if (swapped)
closure = g_cclosure_new_swap (callback, NULL, NULL);
else
closure = g_cclosure_new (callback, NULL, NULL);
}
return closure;
}
/**
* gtk_builder_create_closure:
* @builder: a #GtkBuilder
* @function_name: name of the function to look up
* @swapped: %TRUE to create a swapped closure
* @flags: closure creation flags
* @object: (nullable): Object to create the closure with
* @error: (allow-none): return location for an error, or %NULL
*
@@ -2723,11 +2681,11 @@ gtk_builder_create_closure_for_funcptr (GtkBuilder *builder,
* Returns: (nullable): A new closure for invoking @function_name
**/
GClosure *
gtk_builder_create_closure (GtkBuilder *builder,
const char *function_name,
gboolean swapped,
GObject *object,
GError **error)
gtk_builder_create_closure (GtkBuilder *builder,
const char *function_name,
GtkBuilderClosureFlags flags,
GObject *object,
GError **error)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
@@ -2736,66 +2694,7 @@ gtk_builder_create_closure (GtkBuilder *builder,
g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (priv->closure_func)
return priv->closure_func (builder, function_name, swapped, object, priv->closure_data, error);
else
return gtk_builder_create_cclosure (builder, function_name, swapped, object, error);
}
/**
* gtk_builder_create_cclosure: (skip)
* @builder: a #GtkBuilder
* @function_name: name of the function to look up
* @swapped: %TRUE to create a swapped closure
* @object: (nullable): Object to create the closure with
* @error: (allow-none): return location for an error, or %NULL
*
* This is the default function used by gtk_builder_set_closure_func(). Some bindings
* with C support may want to call this function as a fallback from their closure
* function.
*
* This function has no purpose otherwise.
*
* This function will prefer callbacks added via gtk_builder_add_callback_symbol()
* to looking up public symbols.
*
* Returns: (nullable): A new closure for invoking @function_name
**/
GClosure *
gtk_builder_create_cclosure (GtkBuilder *builder,
const char *function_name,
gboolean swapped,
GObject *object,
GError **error)
{
GModule *module = gtk_builder_get_module (builder);
GCallback func;
func = gtk_builder_lookup_callback_symbol (builder, function_name);
if (func)
return gtk_builder_create_closure_for_funcptr (builder, func, swapped, object);
if (module == NULL)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_FUNCTION,
"Could not look up function `%s`: GModule is not supported.",
function_name);
return NULL;
}
if (!g_module_symbol (module, function_name, (gpointer)&func))
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_FUNCTION,
"No function named `%s`.",
function_name);
return NULL;
}
return gtk_builder_create_closure_for_funcptr (builder, func, swapped, object);
return gtk_builder_scope_create_closure (priv->scope, builder, function_name, flags, object, error);
}
/**
@@ -2983,6 +2882,28 @@ _gtk_builder_check_parent (GtkBuilder *builder,
return FALSE;
}
GObject *
gtk_builder_lookup_object (GtkBuilder *builder,
const gchar *name,
gint line,
gint col,
GError **error)
{
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
GObject *obj;
obj = g_hash_table_lookup (priv->objects, name);
if (obj == NULL)
{
g_set_error (error,
GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_ID,
"%s:%d:%d Object with ID %s not found",
priv->filename, line, col, name);
}
return obj;
}
/*< private >
* @builder: a #GtkBuilder
* @name: object name to look up

View File

@@ -23,7 +23,7 @@
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkapplication.h>
#include <gtk/gtkbuilderscope.h>
#include <gtk/gtkwidget.h>
G_BEGIN_DECLS
@@ -98,11 +98,6 @@ GType gtk_builder_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkBuilder* gtk_builder_new (void);
GDK_AVAILABLE_IN_ALL
void gtk_builder_set_closure_func (GtkBuilder *builder,
GtkBuilderClosureFunc closure_func,
gpointer user_data,
GDestroyNotify user_destroy);
GDK_AVAILABLE_IN_ALL
gboolean gtk_builder_add_from_file (GtkBuilder *builder,
const gchar *filename,
@@ -142,11 +137,21 @@ void gtk_builder_expose_object (GtkBuilder *builder,
const gchar *name,
GObject *object);
GDK_AVAILABLE_IN_ALL
GObject * gtk_builder_get_current_object (GtkBuilder *self);
GDK_AVAILABLE_IN_ALL
void gtk_builder_set_current_object (GtkBuilder *self,
GObject *current_object);
GDK_AVAILABLE_IN_ALL
void gtk_builder_set_translation_domain (GtkBuilder *builder,
const gchar *domain);
GDK_AVAILABLE_IN_ALL
const gchar* gtk_builder_get_translation_domain (GtkBuilder *builder);
GDK_AVAILABLE_IN_ALL
GtkBuilderScope *gtk_builder_get_scope (GtkBuilder *builder);
GDK_AVAILABLE_IN_ALL
void gtk_builder_set_scope (GtkBuilder *builder,
GtkBuilderScope *scope);
GDK_AVAILABLE_IN_ALL
GType gtk_builder_get_type_from_name (GtkBuilder *builder,
const char *type_name);
@@ -171,27 +176,9 @@ GtkBuilder * gtk_builder_new_from_string (const gchar *string,
gssize length);
GDK_AVAILABLE_IN_ALL
void gtk_builder_add_callback_symbol (GtkBuilder *builder,
const gchar *callback_name,
GCallback callback_symbol);
GDK_AVAILABLE_IN_ALL
void gtk_builder_add_callback_symbols (GtkBuilder *builder,
const gchar *first_callback_name,
GCallback first_callback_symbol,
...) G_GNUC_NULL_TERMINATED;
GDK_AVAILABLE_IN_ALL
GCallback gtk_builder_lookup_callback_symbol (GtkBuilder *builder,
const gchar *callback_name);
GDK_AVAILABLE_IN_ALL
GClosure * gtk_builder_create_closure (GtkBuilder *builder,
const char *function_name,
gboolean swapped,
GObject *object,
GError **error);
GDK_AVAILABLE_IN_ALL
GClosure * gtk_builder_create_cclosure (GtkBuilder *builder,
const char *function_name,
gboolean swapped,
GtkBuilderClosureFlags flags,
GObject *object,
GError **error);
@@ -210,7 +197,7 @@ GClosure * gtk_builder_create_cclosure (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,328 @@
/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkbuilderlistitemfactory.h"
#include "gtkbuilder.h"
#include "gtkintl.h"
#include "gtklistitemfactoryprivate.h"
#include "gtklistitemprivate.h"
struct _GtkBuilderListItemFactory
{
GtkListItemFactory parent_instance;
GtkBuilderScope *scope;
GBytes *bytes;
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->bytes, NULL),
g_bytes_get_size (self->bytes),
&error))
{
g_critical ("Error building template for list item: %s", error->message);
g_error_free (error);
/* This should never happen, if the template XML cannot be built
* then it is a critical programming error.
*/
g_object_unref (builder);
return;
}
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);
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_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)
{
}
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);
}
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

@@ -18,17 +18,17 @@
#include "config.h"
#include <string.h>
#include <gmodule.h>
#include "gtkbuilderprivate.h"
#include "gtkbuildable.h"
#include "gtkbuilderscopeprivate.h"
#include "gtkdebug.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
#include "gtkversion.h"
#include <gio/gio.h>
#include "gtkbuilderprivate.h"
#include "gtkbuilder.h"
#include "gtkbuildable.h"
#include "gtkdebug.h"
#include "gtkversion.h"
#include "gtktypebuiltins.h"
#include "gtkintl.h"
#include <string.h>
typedef struct {
@@ -485,21 +485,6 @@ builder_construct (ParserData *data,
return object;
}
static GType
_get_type_by_symbol (const gchar *symbol)
{
static GModule *module = NULL;
GTypeGetFunc func;
if (!module)
module = g_module_open (NULL, 0);
if (!g_module_symbol (module, symbol, (gpointer)&func))
return G_TYPE_INVALID;
return func ();
}
static void
parse_requires (ParserData *data,
const gchar *element_name,
@@ -612,7 +597,7 @@ parse_object (GtkBuildableParseContext *context,
/* Call the GType function, and return the GType, it's guaranteed afterwards
* that g_type_from_name on the name will return our GType
*/
object_type = _get_type_by_symbol (type_func);
object_type = gtk_builder_scope_get_type_from_function (gtk_builder_get_scope (data->builder), data->builder, type_func);
if (object_type == G_TYPE_INVALID)
{
g_set_error (error,
@@ -928,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);
@@ -947,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 ("");
@@ -960,14 +946,471 @@ 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:
gtk_expression_unref (info->expression);
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;
if (info->property.expression)
{
expression = expression_info_construct (builder, info->property.expression, error);
if (expression == NULL)
return NULL;
free_expression_info (info->property.expression);
}
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,
"%s:%d:%d Lookups require a type attribute if they don't have an expression.",
"???", 0, 0);
return NULL;
}
expression = gtk_property_expression_new (type,
expression,
info->property.property_name);
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,
@@ -1052,6 +1495,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)
@@ -1292,6 +1753,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)
@@ -1302,6 +1765,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)
@@ -1377,6 +1846,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)
{
@@ -1437,6 +1930,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);
@@ -1517,6 +2053,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
@@ -1531,6 +2094,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;
@@ -1540,6 +2109,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 ();
}
@@ -1594,10 +2166,10 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
if (!gtk_buildable_parse_context_parse (&data.ctx, buffer, length, error))
goto out;
if (!_gtk_builder_finish (builder, error))
if (_gtk_builder_lookup_failed (builder, error))
goto out;
if (_gtk_builder_lookup_failed (builder, error))
if (!_gtk_builder_finish (builder, error))
goto out;
/* Custom parser_finished */

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,
@@ -222,6 +274,11 @@ gboolean _gtk_builder_check_parent (GtkBuilder *builder,
GtkBuildableParseContext *context,
const gchar *parent_name,
GError **error);
GObject *gtk_builder_lookup_object (GtkBuilder *builder,
const gchar *name,
gint line,
gint col,
GError **error);
GObject *_gtk_builder_lookup_object (GtkBuilder *builder,
const gchar *name,
gint line,

521
gtk/gtkbuilderscope.c Normal file
View File

@@ -0,0 +1,521 @@
/*
* 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 "gtkbuilderscopeprivate.h"
#include "gtkbuilder.h"
#include "gtktestutils.h"
/**
* SECTION:gtkbuilderscope
* @Title: GtkBuilderScope
* @Short_description: Bindings for GtkBuilder
* @See_also: #GtkBuilder, #GClosure
*
* #GtkBuilderScope is an interface to provide support to #GtkBuilder, primarily
* for looking up programming-language-specific values for strings that are
* given in a #GtkBuilder UI file.
*
* The primary intended audience is bindings that want to provide deeper integration
* of #GtkBuilder into the language.
*
* A #GtkBuilderScope instance may be used with multiple #GtkBuilder objects, even
* at once.
*
* By default, GTK will use its own implementation of #GtkBuilderScope for the C
* language which can be created via gtk_builder_cscope_new().
*
* #GtkBuilderCScope instances use symbols explicitly added to @builder
* with prior calls to gtk_builder_scope_add_callback_symbol(). If developers want
* to do that, they are encouraged to create their own scopes for that purpose.
*
* In the case that symbols are not explicitly added; GTK will uses #GModules
* introspective features (by opening the module %NULL) to look at the applications
* symbol table. From here it tries to match the signal function names given in the
* interface description with symbols in the application.
*
* Note that unless gtk_builder_scope_add_callback_symbol() is called for
* all signal callbacks which are referenced by the loaded XML, this
* functionality will require that #GModule be supported on the platform.
*/
G_DEFINE_INTERFACE (GtkBuilderScope, gtk_builder_scope, G_TYPE_OBJECT)
static GType
gtk_builder_scope_default_get_type_from_name (GtkBuilderScope *self,
GtkBuilder *builder,
const char *type_name)
{
GType type;
type = g_type_from_name (type_name);
if (type != G_TYPE_INVALID)
return type;
gtk_test_register_all_types ();
return g_type_from_name (type_name);
}
static GType
gtk_builder_scope_default_get_type_from_function (GtkBuilderScope *self,
GtkBuilder *builder,
const char *type_name)
{
return G_TYPE_INVALID;
}
static GClosure *
gtk_builder_scope_default_create_closure (GtkBuilderScope *self,
GtkBuilder *builder,
const char *function_name,
GtkBuilderClosureFlags flags,
GObject *object,
GError **error)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_FUNCTION,
"Creating closures is not supported by %s",
G_OBJECT_TYPE_NAME (self));
return NULL;
}
static void
gtk_builder_scope_default_init (GtkBuilderScopeInterface *iface)
{
iface->get_type_from_name = gtk_builder_scope_default_get_type_from_name;
iface->get_type_from_function = gtk_builder_scope_default_get_type_from_function;
iface->create_closure = gtk_builder_scope_default_create_closure;
}
GType
gtk_builder_scope_get_type_from_name (GtkBuilderScope *self,
GtkBuilder *builder,
const char *type_name)
{
g_return_val_if_fail (GTK_IS_BUILDER_SCOPE (self), G_TYPE_INVALID);
g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID);
g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID);
return GTK_BUILDER_SCOPE_GET_IFACE (self)->get_type_from_name (self, builder, type_name);
}
GType
gtk_builder_scope_get_type_from_function (GtkBuilderScope *self,
GtkBuilder *builder,
const char *function_name)
{
g_return_val_if_fail (GTK_IS_BUILDER_SCOPE (self), G_TYPE_INVALID);
g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID);
g_return_val_if_fail (function_name != NULL, G_TYPE_INVALID);
return GTK_BUILDER_SCOPE_GET_IFACE (self)->get_type_from_function (self, builder, function_name);
}
GClosure *
gtk_builder_scope_create_closure (GtkBuilderScope *self,
GtkBuilder *builder,
const char *function_name,
GtkBuilderClosureFlags flags,
GObject *object,
GError **error)
{
g_return_val_if_fail (GTK_IS_BUILDER_SCOPE (self), NULL);
g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
g_return_val_if_fail (function_name != NULL, NULL);
g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return GTK_BUILDER_SCOPE_GET_IFACE (self)->create_closure (self, builder, function_name, flags, object, error);
}
/*** GTK_BUILDER_CSCOPE ***/
typedef struct _GtkBuilderCScopePrivate GtkBuilderCScopePrivate;
struct _GtkBuilderCScopePrivate
{
GModule *module;
GHashTable *callbacks;
};
static void gtk_builder_cscope_scope_init (GtkBuilderScopeInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GtkBuilderCScope, gtk_builder_cscope, G_TYPE_OBJECT,
G_ADD_PRIVATE(GtkBuilderCScope)
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDER_SCOPE,
gtk_builder_cscope_scope_init))
static GModule *
gtk_builder_cscope_get_module (GtkBuilderCScope *self)
{
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
if (priv->module == NULL)
{
if (!g_module_supported ())
return NULL;
priv->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
}
return priv->module;
}
/*
* Try to map a type name to a _get_type function
* and call it, eg:
*
* GtkWindow -> gtk_window_get_type
* GtkHBox -> gtk_hbox_get_type
* GtkUIManager -> gtk_ui_manager_get_type
* GWeatherLocation -> gweather_location_get_type (split_first_cap == FALSE)
* GThemedIcon -> g_themed_icon_get_type (slit_first_cap == TRUE)
*
* Keep in sync with testsuite/gtk/typename.c !
*/
static gchar *
type_name_mangle (const gchar *name,
gboolean split_first_cap)
{
GString *symbol_name = g_string_new ("");
gint i;
for (i = 0; name[i] != '\0'; i++)
{
/* skip if uppercase, first or previous is uppercase */
if ((name[i] == g_ascii_toupper (name[i]) &&
((i > 0 && name[i-1] != g_ascii_toupper (name[i-1])) ||
(i == 1 && name[0] == g_ascii_toupper (name[0]) && split_first_cap))) ||
(i > 2 && name[i] == g_ascii_toupper (name[i]) &&
name[i-1] == g_ascii_toupper (name[i-1]) &&
name[i-2] == g_ascii_toupper (name[i-2])))
g_string_append_c (symbol_name, '_');
g_string_append_c (symbol_name, g_ascii_tolower (name[i]));
}
g_string_append (symbol_name, "_get_type");
return g_string_free (symbol_name, FALSE);
}
static GType
gtk_builder_cscope_resolve_type_lazily (GtkBuilderCScope *self,
const gchar *name)
{
GModule *module;
GType (*func) (void);
char *symbol;
GType gtype = G_TYPE_INVALID;
module = gtk_builder_cscope_get_module (self);
if (!module)
return G_TYPE_INVALID;
symbol = type_name_mangle (name, TRUE);
if (g_module_symbol (module, symbol, (gpointer)&func))
gtype = func ();
g_free (symbol);
symbol = type_name_mangle (name, FALSE);
if (g_module_symbol (module, symbol, (gpointer)&func))
gtype = func ();
g_free (symbol);
return gtype;
}
static GType
gtk_builder_cscope_get_type_from_name (GtkBuilderScope *scope,
GtkBuilder *builder,
const char *type_name)
{
GtkBuilderCScope *self = GTK_BUILDER_CSCOPE (scope);
GType type;
type = g_type_from_name (type_name);
if (type != G_TYPE_INVALID)
return type;
type = gtk_builder_cscope_resolve_type_lazily (self, type_name);
if (type != G_TYPE_INVALID)
return type;
gtk_test_register_all_types ();
type = g_type_from_name (type_name);
return type;
}
static GCallback
gtk_builder_cscope_get_callback (GtkBuilderCScope *self,
const char *function_name,
GError **error)
{
GModule *module;
GCallback func;
func = gtk_builder_cscope_lookup_callback_symbol (self, function_name);
if (func)
return func;
module = gtk_builder_cscope_get_module (self);
if (module == NULL)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_FUNCTION,
"Could not look up function `%s`: GModule is not supported.",
function_name);
return NULL;
}
if (!g_module_symbol (module, function_name, (gpointer)&func))
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_FUNCTION,
"No function named `%s`.",
function_name);
return NULL;
}
return func;
}
static GType
gtk_builder_cscope_get_type_from_function (GtkBuilderScope *scope,
GtkBuilder *builder,
const char *function_name)
{
GtkBuilderCScope *self = GTK_BUILDER_CSCOPE (scope);
GType (* type_func) (void);
type_func = (GType (*) (void)) gtk_builder_cscope_get_callback (self, function_name, NULL);
if (!type_func)
return G_TYPE_INVALID;
return type_func();
}
static GClosure *
gtk_builder_cscope_create_closure_for_funcptr (GtkBuilderCScope *self,
GtkBuilder *builder,
GCallback callback,
gboolean swapped,
GObject *object)
{
GClosure *closure;
if (object == NULL)
object = gtk_builder_get_current_object (builder);
if (object)
{
if (swapped)
closure = g_cclosure_new_object_swap (callback, object);
else
closure = g_cclosure_new_object (callback, object);
}
else
{
if (swapped)
closure = g_cclosure_new_swap (callback, NULL, NULL);
else
closure = g_cclosure_new (callback, NULL, NULL);
}
return closure;
}
static GClosure *
gtk_builder_cscope_create_closure (GtkBuilderScope *scope,
GtkBuilder *builder,
const char *function_name,
GtkBuilderClosureFlags flags,
GObject *object,
GError **error)
{
GtkBuilderCScope *self = GTK_BUILDER_CSCOPE (scope);
GCallback func;
gboolean swapped;
swapped = flags & GTK_BUILDER_CLOSURE_SWAPPED;
func = gtk_builder_cscope_get_callback (self, function_name, error);
if (!func)
return NULL;
return gtk_builder_cscope_create_closure_for_funcptr (self, builder, func, swapped, object);
}
static void
gtk_builder_cscope_scope_init (GtkBuilderScopeInterface *iface)
{
iface->get_type_from_name = gtk_builder_cscope_get_type_from_name;
iface->get_type_from_function = gtk_builder_cscope_get_type_from_function;
iface->create_closure = gtk_builder_cscope_create_closure;
}
static void
gtk_builder_cscope_finalize (GObject *object)
{
GtkBuilderCScope *self = GTK_BUILDER_CSCOPE (object);
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
g_clear_pointer (&priv->callbacks, g_hash_table_destroy);
g_clear_pointer (&priv->module, g_module_close);
G_OBJECT_CLASS (gtk_builder_cscope_parent_class)->dispose (object);
}
static void
gtk_builder_cscope_class_init (GtkBuilderCScopeClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gtk_builder_cscope_finalize;
}
static void
gtk_builder_cscope_init (GtkBuilderCScope *self)
{
}
/**
* gtk_builder_cscope_new:
*
* Creates a new #GtkbuilderCScope object to use with future #GtkBuilder
* instances.
*
* Calling this function is only necessary if you want to add custom
* callbacks via gtk_builder_cscope_add_callback_symbol().
*
* Returns: a new #GtkBuilderCScope
**/
GtkBuilderScope *
gtk_builder_cscope_new (void)
{
return g_object_new (GTK_TYPE_BUILDER_CSCOPE, NULL);
}
/**
* gtk_builder_cscope_add_callback_symbol:
* @self: a #GtkBuilderCScope
* @callback_name: The name of the callback, as expected in the XML
* @callback_symbol: (scope async): The callback pointer
*
* Adds the @callback_symbol to the scope of @builder under the given @callback_name.
*
* Using this function overrides the behavior of gtk_builder_create_closure()
* for any callback symbols that are added. Using this method allows for better
* encapsulation as it does not require that callback symbols be declared in
* the global namespace.
*/
void
gtk_builder_cscope_add_callback_symbol (GtkBuilderCScope *self,
const gchar *callback_name,
GCallback callback_symbol)
{
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
g_return_if_fail (GTK_IS_BUILDER_CSCOPE (self));
g_return_if_fail (callback_name && callback_name[0]);
g_return_if_fail (callback_symbol != NULL);
if (!priv->callbacks)
priv->callbacks = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
g_hash_table_insert (priv->callbacks, g_strdup (callback_name), callback_symbol);
}
/**
* gtk_builder_cscope_add_callback_symbols: (skip)
* @self: a #GtkBuilderCScope
* @first_callback_name: The name of the callback, as expected in the XML
* @first_callback_symbol: (scope async): The callback pointer
* @...: A list of callback name and callback symbol pairs terminated with %NULL
*
* A convenience function to add many callbacks instead of calling
* gtk_builder_add_callback_symbol() for each symbol.
*/
void
gtk_builder_cscope_add_callback_symbols (GtkBuilderCScope *self,
const gchar *first_callback_name,
GCallback first_callback_symbol,
...)
{
va_list var_args;
const gchar *callback_name;
GCallback callback_symbol;
g_return_if_fail (GTK_IS_BUILDER_CSCOPE (self));
g_return_if_fail (first_callback_name && first_callback_name[0]);
g_return_if_fail (first_callback_symbol != NULL);
callback_name = first_callback_name;
callback_symbol = first_callback_symbol;
va_start (var_args, first_callback_symbol);
do {
gtk_builder_cscope_add_callback_symbol (self, callback_name, callback_symbol);
callback_name = va_arg (var_args, const gchar*);
if (callback_name)
callback_symbol = va_arg (var_args, GCallback);
} while (callback_name != NULL);
va_end (var_args);
}
/**
* gtk_builder_lookup_callback_symbol: (skip)
* @self: a #GtkBuilderCScope
* @callback_name: The name of the callback
*
* Fetches a symbol previously added to @self
* with gtk_builder_add_callback_symbols().
*
* Returns: (nullable): The callback symbol in @builder for @callback_name, or %NULL
*/
GCallback
gtk_builder_cscope_lookup_callback_symbol (GtkBuilderCScope *self,
const gchar *callback_name)
{
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
g_return_val_if_fail (GTK_IS_BUILDER_CSCOPE (self), NULL);
g_return_val_if_fail (callback_name && callback_name[0], NULL);
if (priv->callbacks == NULL)
return NULL;
return g_hash_table_lookup (priv->callbacks, callback_name);
}

124
gtk/gtkbuilderscope.h Normal file
View File

@@ -0,0 +1,124 @@
/*
* 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_SCOPE_H__
#define __GTK_BUILDER_SCOPE_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtktypes.h>
G_BEGIN_DECLS
#define GTK_TYPE_BUILDER_SCOPE (gtk_builder_scope_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (GtkBuilderScope, gtk_builder_scope, GTK, BUILDER_SCOPE, GObject)
/**
* GtkBuilderClosureFlags:
* @GTK_BUILDER_CLOSURE_SWAPPED: The closure should be created swapped. See
* g_cclosure_new_swap() for details.
*
* The list of flags that can be passed to gtk_builder_scope_create_closure().
* New values may be added in the future for new features, so external
* implementations of GtkBuilderScopeInterface should test the flags for unknown
* values and raise a %@GTK_BUILDER_ERROR_INVALID_ATTRIBUTE error when they
* encounter one.
*/
typedef enum {
GTK_BUILDER_CLOSURE_SWAPPED = (1 << 0)
} GtkBuilderClosureFlags;
/**
* GtkBuilderScopeInterface:
* @get_type_from_name: Try to lookup a #GType via the its name. See
* gtk_builder_get_type_from_name() for more details.
* The C implementation will use g_type_from_name() and if that fails try to guess the
* correct function name for registering the type and then use dlsym() to load it.
* The default implementation just tries g_type_from_name() and otherwise fails.
* @get_type_from_function: Try to lookup a #GType via the given function name, specified
* explicitly in a GtkBuilder file, like via the "type-func" attribute in the "<object>" tag.
* This function is very rarely used.
* The C implementation will use dlsym() and call the resulting function as a #GTypeFunc.
* The default implementation will fail and just return %G_TYPE_INVALID.
* @create_closure: Create a closure with the given arguments. See gtk_builder_create_closure()
* for more details on those.
* The C implementation will try to use dlsym() to locate the function name and then
* g_cclosure_new() to create a closure for the symbol.
* The default implementation just fails and returns %NULL.
*
* The virtual function table to implement for #GtkBuilderScope implementations.
* Default implementations for each function do exist, but they usually just fail,
* so it is suggested that implementations implement all of them.
*/
struct _GtkBuilderScopeInterface
{
/*< private >*/
GTypeInterface g_iface;
/*< public >*/
GType (* get_type_from_name) (GtkBuilderScope *self,
GtkBuilder *builder,
const char *type_name);
GType (* get_type_from_function) (GtkBuilderScope *self,
GtkBuilder *builder,
const char *function_name);
GClosure * (* create_closure) (GtkBuilderScope *self,
GtkBuilder *builder,
const char *function_name,
GtkBuilderClosureFlags flags,
GObject *object,
GError **error);
};
struct _GtkBuilderCScopeClass
{
GObjectClass parent_class;
};
#define GTK_TYPE_BUILDER_CSCOPE (gtk_builder_cscope_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (GtkBuilderCScope, gtk_builder_cscope, GTK, BUILDER_CSCOPE, GObject)
GDK_AVAILABLE_IN_ALL
GtkBuilderScope * gtk_builder_cscope_new (void);
GDK_AVAILABLE_IN_ALL
void gtk_builder_cscope_add_callback_symbol (GtkBuilderCScope *self,
const gchar *callback_name,
GCallback callback_symbol);
GDK_AVAILABLE_IN_ALL
void gtk_builder_cscope_add_callback_symbols (GtkBuilderCScope *self,
const gchar *first_callback_name,
GCallback first_callback_symbol,
...) G_GNUC_NULL_TERMINATED;
GDK_AVAILABLE_IN_ALL
GCallback gtk_builder_cscope_lookup_callback_symbol(GtkBuilderCScope *self,
const gchar *callback_name);
G_END_DECLS
#endif /* __GTK_BUILDER_SCOPE_H__ */

View File

@@ -0,0 +1,43 @@
/*
* 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_SCOPE_PRIVATE_H__
#define __GTK_BUILDER_SCOPE_PRIVATE_H__
#include <gtk/gtkbuilderscope.h>
G_BEGIN_DECLS
GType gtk_builder_scope_get_type_from_name (GtkBuilderScope *self,
GtkBuilder *builder,
const char *type_name);
GType gtk_builder_scope_get_type_from_function(GtkBuilderScope *self,
GtkBuilder *builder,
const char *function_name);
GClosure * gtk_builder_scope_create_closure (GtkBuilderScope *self,
GtkBuilder *builder,
const char *function_name,
GtkBuilderClosureFlags flags,
GObject *object,
GError **error);
G_END_DECLS
#endif /* __GTK_BUILDER_SCOPE_PRIVATE_H__ */

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__ */

683
gtk/gtkcolumnview.c Normal file
View File

@@ -0,0 +1,683 @@
/*
* 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 "gtkcolumnviewprivate.h"
#include "gtkboxlayout.h"
#include "gtkbuildable.h"
#include "gtkcolumnlistitemfactoryprivate.h"
#include "gtkcolumnviewcolumnprivate.h"
#include "gtkcolumnviewlayoutprivate.h"
#include "gtkcssnodeprivate.h"
#include "gtkintl.h"
#include "gtklistview.h"
#include "gtkmain.h"
#include "gtkprivate.h"
#include "gtkscrollable.h"
#include "gtkwidgetprivate.h"
/**
* SECTION:gtkcolumnview
* @title: GtkColumnView
* @short_description: A widget for displaying lists in multiple columns
* @see_also: #GtkColumnViewColumn, #GtkTreeView
*
* GtkColumnView is a widget to present a view into a large dynamic list of items
* using multiple columns.
*/
struct _GtkColumnView
{
GtkWidget parent_instance;
GListStore *columns;
GtkWidget *header;
GtkListView *listview;
GtkColumnListItemFactory *factory;
};
struct _GtkColumnViewClass
{
GtkWidgetClass parent_class;
};
enum
{
PROP_0,
PROP_COLUMNS,
PROP_HADJUSTMENT,
PROP_HSCROLL_POLICY,
PROP_MODEL,
PROP_SHOW_SEPARATORS,
PROP_VADJUSTMENT,
PROP_VSCROLL_POLICY,
N_PROPS
};
enum {
ACTIVATE,
LAST_SIGNAL
};
static GtkBuildableIface *parent_buildable_iface;
static void
gtk_column_view_buildable_add_child (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const gchar *type)
{
if (GTK_IS_COLUMN_VIEW_COLUMN (child))
{
if (type != NULL)
{
GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
}
else
{
gtk_column_view_append_column (GTK_COLUMN_VIEW (buildable),
GTK_COLUMN_VIEW_COLUMN (child));
}
}
else
{
parent_buildable_iface->add_child (buildable, builder, child, type);
}
}
static void
gtk_column_view_buildable_interface_init (GtkBuildableIface *iface)
{
parent_buildable_iface = g_type_interface_peek_parent (iface);
iface->add_child = gtk_column_view_buildable_add_child;
}
G_DEFINE_TYPE_WITH_CODE (GtkColumnView, gtk_column_view, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_column_view_buildable_interface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
static GParamSpec *properties[N_PROPS] = { NULL, };
static guint signals[LAST_SIGNAL] = { 0 };
static void
gtk_column_view_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
gtk_column_view_measure_across (self, minimum, natural);
}
else
{
int header_min, header_nat, list_min, list_nat;
gtk_widget_measure (GTK_WIDGET (self->listview),
orientation, for_size,
&header_min, &header_nat,
NULL, NULL);
gtk_widget_measure (GTK_WIDGET (self->listview),
orientation, for_size,
&list_min, &list_nat,
NULL, NULL);
*minimum = header_min + list_min;
*natural = header_nat + list_nat;
}
}
static int
gtk_column_view_allocate_columns (GtkColumnView *self,
int width)
{
GtkScrollablePolicy scroll_policy;
int col_min, col_nat, widget_min, widget_nat, extra, col_size, x;
guint i;
gtk_column_view_measure_across (self, &col_min, &col_nat);
gtk_widget_measure (GTK_WIDGET (self),
GTK_ORIENTATION_HORIZONTAL, -1,
&widget_min, &widget_nat,
NULL, NULL);
scroll_policy = gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview));
if (scroll_policy == GTK_SCROLL_MINIMUM)
{
extra = widget_min - col_min;
col_size = col_min;
}
else
{
extra = widget_nat - col_nat;
col_size = col_nat;
}
width -= extra;
width = MAX (width, col_size);
x = 0;
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
{
GtkColumnViewColumn *column;
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
gtk_column_view_column_measure (column, &col_min, &col_nat);
if (scroll_policy == GTK_SCROLL_MINIMUM)
col_size = col_min;
else
col_size = col_nat;
gtk_column_view_column_allocate (column, x, col_size);
x += col_size;
g_object_unref (column);
}
return width + extra;
}
static void
gtk_column_view_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
int full_width, header_height, min, nat;
full_width = gtk_column_view_allocate_columns (self, width);
gtk_widget_measure (self->header, GTK_ORIENTATION_VERTICAL, full_width, &min, &nat, NULL, NULL);
if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (self->listview)) == GTK_SCROLL_MINIMUM)
header_height = min;
else
header_height = nat;
gtk_widget_allocate (self->header, full_width, header_height, -1, NULL);
gtk_widget_allocate (GTK_WIDGET (self->listview),
full_width, height - header_height, -1,
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (0, header_height)));
}
static void
gtk_column_view_activate_cb (GtkListView *listview,
guint pos,
GtkColumnView *self)
{
g_signal_emit (self, signals[ACTIVATE], 0, pos);
}
static void
gtk_column_view_dispose (GObject *object)
{
GtkColumnView *self = GTK_COLUMN_VIEW (object);
while (g_list_model_get_n_items (G_LIST_MODEL (self->columns)) > 0)
{
GtkColumnViewColumn *column = g_list_model_get_item (G_LIST_MODEL (self->columns), 0);
gtk_column_view_remove_column (self, column);
g_object_unref (column);
}
g_clear_pointer (&self->header, gtk_widget_unparent);
g_clear_pointer ((GtkWidget **) &self->listview, gtk_widget_unparent);
g_clear_object (&self->factory);
G_OBJECT_CLASS (gtk_column_view_parent_class)->dispose (object);
}
static void
gtk_column_view_finalize (GObject *object)
{
GtkColumnView *self = GTK_COLUMN_VIEW (object);
g_object_unref (self->columns);
G_OBJECT_CLASS (gtk_column_view_parent_class)->finalize (object);
}
static void
gtk_column_view_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkColumnView *self = GTK_COLUMN_VIEW (object);
switch (property_id)
{
case PROP_COLUMNS:
g_value_set_object (value, self->columns);
break;
case PROP_HADJUSTMENT:
g_value_set_object (value, gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)));
break;
case PROP_HSCROLL_POLICY:
g_value_set_enum (value, gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview)));
break;
case PROP_MODEL:
g_value_set_object (value, gtk_list_view_get_model (self->listview));
break;
case PROP_SHOW_SEPARATORS:
g_value_set_boolean (value, gtk_list_view_get_show_separators (self->listview));
break;
case PROP_VADJUSTMENT:
g_value_set_object (value, gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->listview)));
break;
case PROP_VSCROLL_POLICY:
g_value_set_enum (value, gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (self->listview)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_column_view_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkColumnView *self = GTK_COLUMN_VIEW (object);
switch (property_id)
{
case PROP_HADJUSTMENT:
if (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)) != g_value_get_object (value))
{
gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (self->listview), g_value_get_object (value));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HADJUSTMENT]);
}
break;
case PROP_HSCROLL_POLICY:
if (gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview)) != g_value_get_enum (value))
{
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (self->listview), g_value_get_enum (value));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HSCROLL_POLICY]);
}
break;
case PROP_MODEL:
gtk_column_view_set_model (self, g_value_get_object (value));
break;
case PROP_SHOW_SEPARATORS:
gtk_column_view_set_show_separators (self, g_value_get_boolean (value));
break;
case PROP_VADJUSTMENT:
if (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->listview)) != g_value_get_object (value))
{
gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (self->listview), g_value_get_object (value));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VADJUSTMENT]);
}
break;
case PROP_VSCROLL_POLICY:
if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (self->listview)) != g_value_get_enum (value))
{
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (self->listview), g_value_get_enum (value));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VSCROLL_POLICY]);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_column_view_class_init (GtkColumnViewClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gpointer iface;
widget_class->measure = gtk_column_view_measure;
widget_class->size_allocate = gtk_column_view_allocate;
gobject_class->dispose = gtk_column_view_dispose;
gobject_class->finalize = gtk_column_view_finalize;
gobject_class->get_property = gtk_column_view_get_property;
gobject_class->set_property = gtk_column_view_set_property;
/* GtkScrollable implementation */
iface = g_type_default_interface_peek (GTK_TYPE_SCROLLABLE);
properties[PROP_HADJUSTMENT] =
g_param_spec_override ("hadjustment",
g_object_interface_find_property (iface, "hadjustment"));
properties[PROP_HSCROLL_POLICY] =
g_param_spec_override ("hscroll-policy",
g_object_interface_find_property (iface, "hscroll-policy"));
properties[PROP_VADJUSTMENT] =
g_param_spec_override ("vadjustment",
g_object_interface_find_property (iface, "vadjustment"));
properties[PROP_VSCROLL_POLICY] =
g_param_spec_override ("vscroll-policy",
g_object_interface_find_property (iface, "vscroll-policy"));
/**
* GtkColumnView:columns:
*
* The list of columns
*/
properties[PROP_COLUMNS] =
g_param_spec_object ("columns",
P_("Columns"),
P_("List of columns"),
G_TYPE_LIST_MODEL,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnView:model:
*
* Model for the items displayed
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("Model for the items displayed"),
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnView:show-separators:
*
* Show separators between rows
*/
properties[PROP_SHOW_SEPARATORS] =
g_param_spec_boolean ("show-separators",
P_("Show separators"),
P_("Show separators between rows"),
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
/**
* GtkColumnView::activate:
* @self: The #GtkColumnView
* @position: position of item to activate
*
* The ::activate signal is emitted when a row has been activated by the user,
* usually via activating the GtkListBase|list.activate-item action.
*
* This allows for a convenient way to handle activation in a columnview.
* See gtk_list_item_set_activatable() for details on how to use this signal.
*/
signals[ACTIVATE] =
g_signal_new (I_("activate"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
g_signal_set_va_marshaller (signals[ACTIVATE],
G_TYPE_FROM_CLASS (gobject_class),
g_cclosure_marshal_VOID__UINTv);
gtk_widget_class_set_css_name (widget_class, I_("treeview"));
}
static void
gtk_column_view_init (GtkColumnView *self)
{
self->columns = g_list_store_new (GTK_TYPE_COLUMN_VIEW_COLUMN);
self->header = gtk_list_item_widget_new (NULL, "header");
gtk_widget_set_can_focus (self->header, FALSE);
gtk_widget_set_layout_manager (self->header, gtk_column_view_layout_new (self));
gtk_widget_set_parent (self->header, GTK_WIDGET (self));
self->factory = gtk_column_list_item_factory_new (self);
self->listview = GTK_LIST_VIEW (gtk_list_view_new_with_factory (
GTK_LIST_ITEM_FACTORY (g_object_ref (self->factory))));
gtk_widget_set_hexpand (GTK_WIDGET (self->listview), TRUE);
gtk_widget_set_vexpand (GTK_WIDGET (self->listview), TRUE);
g_signal_connect (self->listview, "activate", G_CALLBACK (gtk_column_view_activate_cb), self);
gtk_widget_set_parent (GTK_WIDGET (self->listview), GTK_WIDGET (self));
gtk_css_node_add_class (gtk_widget_get_css_node (GTK_WIDGET (self)),
g_quark_from_static_string (I_("view")));
gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
}
/**
* gtk_column_view_new:
*
* Creates a new empty #GtkColumnView.
*
* You most likely want to call gtk_column_view_set_factory() to
* set up a way to map its items to widgets and gtk_column_view_set_model()
* to set a model to provide items next.
*
* Returns: a new #GtkColumnView
**/
GtkWidget *
gtk_column_view_new (void)
{
return g_object_new (GTK_TYPE_COLUMN_VIEW, NULL);
}
/**
* gtk_column_view_get_model:
* @self: a #GtkColumnView
*
* Gets the model that's currently used to read the items displayed.
*
* Returns: (nullable) (transfer none): The model in use
**/
GListModel *
gtk_column_view_get_model (GtkColumnView *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
return gtk_list_view_get_model (self->listview);
}
/**
* gtk_column_view_set_model:
* @self: a #GtkColumnView
* @model: (allow-none) (transfer none): the model to use or %NULL for none
*
* Sets the #GListModel to use.
*
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
* Otherwise, @self creates a #GtkSingleSelection for the selection.
**/
void
gtk_column_view_set_model (GtkColumnView *self,
GListModel *model)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (gtk_list_view_get_model (self->listview) == model)
return;
gtk_list_view_set_model (self->listview, model);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_column_view_get_columns:
* @self: a #GtkColumnView
*
* Gets the list of columns in this column view. This list is constant over
* the lifetime of @self and can be used to monitor changes to the columns
* of @self by connecting to the GListModel:items-changed signal.
*
* Returns: (transfer none): The list managing the columns
**/
GListModel *
gtk_column_view_get_columns (GtkColumnView *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
return G_LIST_MODEL (self->columns);
}
/**
* gtk_column_view_set_show_separators:
* @self: a #GtkColumnView
* @show_separators: %TRUE to show separators
*
* Sets whether the list should show separators
* between rows.
*/
void
gtk_column_view_set_show_separators (GtkColumnView *self,
gboolean show_separators)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
if (gtk_list_view_get_show_separators (self->listview) == show_separators)
return;
gtk_list_view_set_show_separators (self->listview, show_separators);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SEPARATORS]);
}
/**
* gtk_column_view_get_show_separators:
* @self: a #GtkColumnView
*
* Returns whether the list box should show separators
* between rows.
*
* Returns: %TRUE if the list box shows separators
*/
gboolean
gtk_column_view_get_show_separators (GtkColumnView *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), FALSE);
return gtk_list_view_get_show_separators (self->listview);
}
/**
* gtk_column_view_append_column:
* @self: a #GtkColumnView
* @column: a #GtkColumnViewColumn that hasn't been added to a
* #GtkColumnView yet
*
* Appends the @column to the end of the columns in @self.
**/
void
gtk_column_view_append_column (GtkColumnView *self,
GtkColumnViewColumn *column)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column));
g_return_if_fail (gtk_column_view_column_get_column_view (column) == NULL);
gtk_column_view_column_set_column_view (column, self);
g_list_store_append (self->columns, column);
}
/**
* gtk_column_view_remove_column:
* @self: a #GtkColumnView
* @column: a #GtkColumnViewColumn that's part of @self
*
* Removes the @column from the list of columns of @self.
**/
void
gtk_column_view_remove_column (GtkColumnView *self,
GtkColumnViewColumn *column)
{
guint i;
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column));
g_return_if_fail (gtk_column_view_column_get_column_view (column) == self);
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
{
GtkColumnViewColumn *item = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
g_object_unref (item);
if (item == column)
break;
}
gtk_column_view_column_set_column_view (column, NULL);
g_list_store_remove (self->columns, i);
}
void
gtk_column_view_measure_across (GtkColumnView *self,
int *minimum,
int *natural)
{
guint i;
int min, nat;
min = 0;
nat = 0;
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
{
GtkColumnViewColumn *column;
int col_min, col_nat;
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
gtk_column_view_column_measure (column, &col_min, &col_nat);
min += col_min;
nat += col_nat;
g_object_unref (column);
}
*minimum = min;
*natural = nat;
}
GtkListItemWidget *
gtk_column_view_get_header_widget (GtkColumnView *self)
{
return GTK_LIST_ITEM_WIDGET (self->header);
}

76
gtk/gtkcolumnview.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* 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>
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
GListModel * gtk_column_view_get_model (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_set_model (GtkColumnView *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
gboolean gtk_column_view_get_show_separators (GtkColumnView *self);
GDK_AVAILABLE_IN_ALL
void gtk_column_view_set_show_separators (GtkColumnView *self,
gboolean show_separators);
G_END_DECLS
#endif /* __GTK_COLUMN_VIEW_H__ */

190
gtk/gtkcolumnviewcell.c Normal file
View File

@@ -0,0 +1,190 @@
/*
* 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)
{
GtkWidget *child = gtk_widget_get_first_child (widget);
if (child)
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
}
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),
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__ */

554
gtk/gtkcolumnviewcolumn.c Normal file
View File

@@ -0,0 +1,554 @@
/*
* 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 "gtkcolumnviewcolumnprivate.h"
#include "gtkcolumnviewprivate.h"
#include "gtkcolumnviewtitleprivate.h"
#include "gtkintl.h"
#include "gtklistbaseprivate.h"
#include "gtklistitemwidgetprivate.h"
#include "gtkmain.h"
#include "gtkprivate.h"
#include "gtkrbtreeprivate.h"
#include "gtksizegroup.h"
#include "gtkstylecontext.h"
#include "gtkwidgetprivate.h"
/**
* SECTION:gtkcolumnviewcolumn
* @title: GtkColumnViewColumn
* @short_description: The column added to GtkColumnView
* @see_also: #GtkColumnView
*
* GtkColumnViewColumn represents the columns being added to #GtkColumnView.
*/
struct _GtkColumnViewColumn
{
GObject parent_instance;
GtkListItemFactory *factory;
char *title;
/* data for the view */
GtkColumnView *view;
GtkWidget *header;
int minimum_size_request;
int natural_size_request;
int allocation_offset;
int allocation_size;
/* This list isn't sorted - this is just caching for performance */
GtkColumnViewCell *first_cell; /* no reference, just caching */
};
struct _GtkColumnViewColumnClass
{
GObjectClass parent_class;
};
enum
{
PROP_0,
PROP_COLUMN_VIEW,
PROP_FACTORY,
PROP_TITLE,
N_PROPS
};
G_DEFINE_TYPE (GtkColumnViewColumn, gtk_column_view_column, G_TYPE_OBJECT)
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_column_view_column_dispose (GObject *object)
{
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
g_assert (self->view == NULL); /* would hold a ref otherwise */
g_assert (self->first_cell == NULL); /* no view = no children */
g_clear_object (&self->factory);
g_clear_pointer (&self->title, g_free);
G_OBJECT_CLASS (gtk_column_view_column_parent_class)->dispose (object);
}
static void
gtk_column_view_column_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
switch (property_id)
{
case PROP_COLUMN_VIEW:
g_value_set_object (value, self->view);
break;
case PROP_FACTORY:
g_value_set_object (value, self->factory);
break;
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_column_view_column_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
switch (property_id)
{
case PROP_FACTORY:
gtk_column_view_column_set_factory (self, g_value_get_object (value));
break;
case PROP_TITLE:
gtk_column_view_column_set_title (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = gtk_column_view_column_dispose;
gobject_class->get_property = gtk_column_view_column_get_property;
gobject_class->set_property = gtk_column_view_column_set_property;
/**
* GtkColumnViewColumn:column-view:
*
* #GtkColumnView this column is a part of
*/
properties[PROP_COLUMN_VIEW] =
g_param_spec_object ("column-view",
P_("Column view"),
P_("Column view this column is a part of"),
G_TYPE_LIST_MODEL,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnViewColumn:factory:
*
* Factory for populating list items
*/
properties[PROP_FACTORY] =
g_param_spec_object ("factory",
P_("Factory"),
P_("Factory for populating list items"),
GTK_TYPE_LIST_ITEM_FACTORY,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GtkColumnViewColumn:title:
*
* Title displayed in the header
*/
properties[PROP_TITLE] =
g_param_spec_string ("title",
P_("Title"),
P_("Title displayed in the header"),
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_column_view_column_init (GtkColumnViewColumn *self)
{
self->minimum_size_request = -1;
self->natural_size_request = -1;
}
/**
* gtk_column_view_column_new:
* @title: (nullable): Title to use for this column
*
* Creates a new #GtkColumnViewColumn.
*
* You most likely want to call gtk_column_add_column() next.
*
* Returns: a new #GtkColumnViewColumn
**/
GtkColumnViewColumn *
gtk_column_view_column_new (const char *title)
{
return g_object_new (GTK_TYPE_COLUMN_VIEW_COLUMN,
"title", title,
NULL);
}
/**
* gtk_column_view_column_new_with_factory:
* @title: (nullable): Title to use for this column
* @factory: (transfer full): The factory to populate items with
*
* Creates a new #GtkColumnViewColumn that uses the given @factory for
* mapping items to widgets.
*
* You most likely want to call gtk_column_add_column() next.
*
* The function takes ownership of the
* argument, so you can write code like
* ```
* column = gtk_column_view_column_new_with_factory (_("Name"),
* gtk_builder_list_item_factory_new_from_resource ("/name.ui"));
* ```
*
* Returns: a new #GtkColumnViewColumn using the given @factory
**/
GtkColumnViewColumn *
gtk_column_view_column_new_with_factory (const char *title,
GtkListItemFactory *factory)
{
GtkColumnViewColumn *result;
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
result = g_object_new (GTK_TYPE_COLUMN_VIEW_COLUMN,
"factory", factory,
"title", title,
NULL);
g_object_unref (factory);
return result;
}
GtkColumnViewCell *
gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self)
{
return self->first_cell;
}
void
gtk_column_view_column_add_cell (GtkColumnViewColumn *self,
GtkColumnViewCell *cell)
{
self->first_cell = cell;
gtk_column_view_column_queue_resize (self);
}
void
gtk_column_view_column_remove_cell (GtkColumnViewColumn *self,
GtkColumnViewCell *cell)
{
if (cell == self->first_cell)
self->first_cell = gtk_column_view_cell_get_next (cell);
gtk_column_view_column_queue_resize (self);
gtk_widget_queue_resize (GTK_WIDGET (cell));
}
void
gtk_column_view_column_queue_resize (GtkColumnViewColumn *self)
{
GtkColumnViewCell *cell;
if (self->minimum_size_request < 0)
return;
self->minimum_size_request = -1;
self->natural_size_request = -1;
if (self->header)
gtk_widget_queue_resize (self->header);
for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell))
{
gtk_widget_queue_resize (GTK_WIDGET (cell));
}
}
void
gtk_column_view_column_measure (GtkColumnViewColumn *self,
int *minimum,
int *natural)
{
if (self->minimum_size_request < 0)
{
GtkColumnViewCell *cell;
int min, nat, cell_min, cell_nat;
if (self->header)
{
gtk_widget_measure (self->header, GTK_ORIENTATION_HORIZONTAL, -1, &min, &nat, NULL, NULL);
}
else
{
min = 0;
nat = 0;
}
for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell))
{
gtk_widget_measure (GTK_WIDGET (cell),
GTK_ORIENTATION_HORIZONTAL,
-1,
&cell_min, &cell_nat,
NULL, NULL);
min = MAX (min, cell_min);
nat = MAX (nat, cell_nat);
}
self->minimum_size_request = min;
self->natural_size_request = nat;
}
*minimum = self->minimum_size_request;
*natural = self->natural_size_request;
}
void
gtk_column_view_column_allocate (GtkColumnViewColumn *self,
int offset,
int size)
{
self->allocation_offset = offset;
self->allocation_size = size;
}
void
gtk_column_view_column_get_allocation (GtkColumnViewColumn *self,
int *offset,
int *size)
{
if (offset)
*offset = self->allocation_offset;
if (size)
*size = self->allocation_size;
}
static void
gtk_column_view_column_create_cells (GtkColumnViewColumn *self)
{
GtkWidget *row;
if (self->first_cell)
return;
for (row = gtk_widget_get_first_child (GTK_WIDGET (self->view));
row != NULL;
row = gtk_widget_get_next_sibling (row))
{
GtkListItemWidget *list_item;
GtkWidget *cell;
if (!gtk_widget_get_root (row))
continue;
list_item = GTK_LIST_ITEM_WIDGET (row);
cell = gtk_column_view_cell_new (self);
gtk_list_item_widget_add_child (list_item, 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));
}
}
static void
gtk_column_view_column_remove_cells (GtkColumnViewColumn *self)
{
while (self->first_cell)
gtk_column_view_cell_remove (self->first_cell);
}
static void
gtk_column_view_column_create_header (GtkColumnViewColumn *self)
{
if (self->header != NULL)
return;
self->header = gtk_column_view_title_new (self);
gtk_list_item_widget_add_child (gtk_column_view_get_header_widget (self->view),
self->header);
gtk_column_view_column_queue_resize (self);
}
static void
gtk_column_view_column_remove_header (GtkColumnViewColumn *self)
{
if (self->header == NULL)
return;
gtk_list_item_widget_remove_child (gtk_column_view_get_header_widget (self->view),
self->header);
self->header = NULL;
gtk_column_view_column_queue_resize (self);
}
static void
gtk_column_view_column_ensure_cells (GtkColumnViewColumn *self)
{
if (self->view && gtk_widget_get_root (GTK_WIDGET (self->view)))
gtk_column_view_column_create_cells (self);
else
gtk_column_view_column_remove_cells (self);
if (self->view)
gtk_column_view_column_create_header (self);
else
gtk_column_view_column_remove_header (self);
}
/**
* gtk_column_view_column_get_column_view:
* @self: a #GtkColumnViewColumn
*
* Gets the column view that's currently displaying this column.
*
* If @self has not been added to a column view yet, %NULL is returned.
*
* Returns: (nullable) (transfer none): The column view displaying @self.
**/
GtkColumnView *
gtk_column_view_column_get_column_view (GtkColumnViewColumn *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
return self->view;
}
void
gtk_column_view_column_set_column_view (GtkColumnViewColumn *self,
GtkColumnView *view)
{
if (self->view == view)
return;
gtk_column_view_column_remove_cells (self);
gtk_column_view_column_remove_header (self);
self->view = view;
gtk_column_view_column_ensure_cells (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLUMN_VIEW]);
}
/**
* gtk_column_view_column_get_factory:
* @self: a #GtkColumnViewColumn
*
* Gets the factory that's currently used to populate list items for
* this column.
*
* Returns: (nullable) (transfer none): The factory in use
**/
GtkListItemFactory *
gtk_column_view_column_get_factory (GtkColumnViewColumn *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
return self->factory;
}
/**
* gtk_column_view_column_set_factory:
* @self: a #GtkColumnViewColumn
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
*
* Sets the #GtkListItemFactory to use for populating list items for this
* column.
**/
void
gtk_column_view_column_set_factory (GtkColumnViewColumn *self,
GtkListItemFactory *factory)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
if (!g_set_object (&self->factory, factory))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
}
/**
* gtk_column_view_column_set_title:
* @self: a #GtkColumnViewColumn
* @title: (nullable): Title to use for this column
*
* Sets the title of this column. The title is displayed in the header of a
* #GtkColumnView for this column and is therefor user-facing text that should
* be translated.
*/
void
gtk_column_view_column_set_title (GtkColumnViewColumn *self,
const char *title)
{
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
if (g_strcmp0 (self->title, title) == 0)
return;
g_free (self->title);
self->title = g_strdup (title);
if (self->header)
gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
}
/**
* gtk_column_view_column_get_title:
* @self: a #GtkColumnViewColumn
*
* Returns the title set with gtk_column_view_column_set_title().
*
* Returns: (nullable) The column's title
*/
const char *
gtk_column_view_column_get_title (GtkColumnViewColumn *self)
{
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), FALSE);
return self->title;
}

70
gtk/gtkcolumnviewcolumn.h Normal file
View File

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

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_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);
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);
#endif /* __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__ */

154
gtk/gtkcolumnviewlayout.c Normal file
View File

@@ -0,0 +1,154 @@
/*
* 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));
else
column = gtk_column_view_title_get_column (GTK_COLUMN_VIEW_TITLE (child));
gtk_column_view_column_get_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,33 @@
/*
* 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/gtklistitemwidgetprivate.h"
GtkListItemWidget * gtk_column_view_get_header_widget (GtkColumnView *self);
void gtk_column_view_measure_across (GtkColumnView *self,
int *minimum,
int *natural);
#endif /* __GTK_COLUMN_VIEW_PRIVATE_H__ */

142
gtk/gtkcolumnviewtitle.c Normal file
View File

@@ -0,0 +1,142 @@
/*
* 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 "gtkcolumnviewcolumnprivate.h"
#include "gtkintl.h"
#include "gtklabel.h"
#include "gtkwidgetprivate.h"
struct _GtkColumnViewTitle
{
GtkWidget parent_instance;
GtkColumnViewColumn *column;
GtkWidget *title;
};
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)
{
GtkWidget *child = gtk_widget_get_first_child (widget);
if (child)
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
}
static void
gtk_column_view_title_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_title_dispose (GObject *object)
{
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (object);
g_clear_pointer(&self->title, 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
gtk_column_view_title_init (GtkColumnViewTitle *self)
{
GtkWidget *widget = GTK_WIDGET (self);
widget->priv->resize_func = gtk_column_view_title_resize_func;
self->title = gtk_label_new (NULL);
gtk_widget_set_parent (self->title, widget);
}
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)
{
gtk_label_set_label (GTK_LABEL (self->title), gtk_column_view_column_get_title (self->column));
}
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__ */

View File

@@ -607,6 +607,39 @@ typedef enum
GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_RIGHT_TO_LEFT /*< nick=btrl >*/
} GtkNumberUpLayout;
/**
* GtkOrdering:
* @GTK_ORDERING_SMALLER: the first value is smaller than the second
* @GTK_ORDERING_EQUAL: the two values are equal
* @GTK_ORDERING_LARGER: the first value is larger than the second
*
* Describes the way two values can be compared.
*
* These values can be used with a #GCompareFunc. However, a
* #GCompareFunc is allowed to return any integer values.
* For converting such a value to a #GtkOrdering, use
* gtk_ordering_from_cmpfunc().
*/
typedef enum {
GTK_ORDERING_SMALLER = -1,
GTK_ORDERING_EQUAL = 0,
GTK_ORDERING_LARGER = 1
} GtkOrdering;
/**
* gtk_ordering_from_cmpfunc:
* @cmpfunc_result: Result of a comparison function
*
* Converts the result of a #GCompareFunc like strcmp() to a #GtkOrdering.
*
* Returns: the corresponding #GtkOrdering
**/
static inline GtkOrdering
gtk_ordering_from_cmpfunc (int cmpfunc_result)
{
return (GtkOrdering) ((cmpfunc_result > 0) - (cmpfunc_result < 0));
}
/**
* GtkPageOrientation:
* @GTK_PAGE_ORIENTATION_PORTRAIT: Portrait mode.

View File

@@ -24,8 +24,6 @@
#error "Only <gtk/gtk.h> can be included directly."
#endif
typedef struct _GtkEventControllerClass GtkEventControllerClass;
#include <gdk/gdk.h>
#include <gtk/gtktypes.h>
#include <gtk/gtkenums.h>
@@ -33,15 +31,8 @@ typedef struct _GtkEventControllerClass GtkEventControllerClass;
G_BEGIN_DECLS
#define GTK_TYPE_EVENT_CONTROLLER (gtk_event_controller_get_type ())
#define GTK_EVENT_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_EVENT_CONTROLLER, GtkEventController))
#define GTK_EVENT_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_EVENT_CONTROLLER, GtkEventControllerClass))
#define GTK_IS_EVENT_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_EVENT_CONTROLLER))
#define GTK_IS_EVENT_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_EVENT_CONTROLLER))
#define GTK_EVENT_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER, GtkEventControllerClass))
GDK_AVAILABLE_IN_ALL
GType gtk_event_controller_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GtkEventController, gtk_event_controller, GTK, EVENT_CONTROLLER)
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_event_controller_get_widget (GtkEventController *controller);

View File

@@ -31,17 +31,8 @@
G_BEGIN_DECLS
#define GTK_TYPE_EVENT_CONTROLLER_KEY (gtk_event_controller_key_get_type ())
#define GTK_EVENT_CONTROLLER_KEY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKey))
#define GTK_EVENT_CONTROLLER_KEY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKeyClass))
#define GTK_IS_EVENT_CONTROLLER_KEY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_EVENT_CONTROLLER_KEY))
#define GTK_IS_EVENT_CONTROLLER_KEY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_EVENT_CONTROLLER_KEY))
#define GTK_EVENT_CONTROLLER_KEY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKeyClass))
typedef struct _GtkEventControllerKey GtkEventControllerKey;
typedef struct _GtkEventControllerKeyClass GtkEventControllerKeyClass;
GDK_AVAILABLE_IN_ALL
GType gtk_event_controller_key_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GtkEventControllerKey, gtk_event_controller_key, GTK, EVENT_CONTROLLER_KEY)
GDK_AVAILABLE_IN_ALL
GtkEventController *gtk_event_controller_key_new (void);

View File

@@ -29,17 +29,8 @@
G_BEGIN_DECLS
#define GTK_TYPE_EVENT_CONTROLLER_LEGACY (gtk_event_controller_legacy_get_type ())
#define GTK_EVENT_CONTROLLER_LEGACY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_EVENT_CONTROLLER_LEGACY, GtkEventControllerLegacy))
#define GTK_EVENT_CONTROLLER_LEGACY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_EVENT_CONTROLLER_LEGACY, GtkEventControllerLegacyClass))
#define GTK_IS_EVENT_CONTROLLER_LEGACY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_EVENT_CONTROLLER_LEGACY))
#define GTK_IS_EVENT_CONTROLLER_LEGACY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_EVENT_CONTROLLER_LEGACY))
#define GTK_EVENT_CONTROLLER_LEGACY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER_LEGACY, GtkEventControllerLegacyClass))
typedef struct _GtkEventControllerLegacy GtkEventControllerLegacy;
typedef struct _GtkEventControllerLegacyClass GtkEventControllerLegacyClass;
GDK_AVAILABLE_IN_ALL
GType gtk_event_controller_legacy_get_type (void) G_GNUC_CONST;
GDK_DECLARE_EXPORTED_TYPE (GtkEventControllerLegacy, gtk_event_controller_legacy, GTK, EVENT_CONTROLLER_LEGACY)
GDK_AVAILABLE_IN_ALL
GtkEventController *gtk_event_controller_legacy_new (void);

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