Compare commits

...

178 Commits

Author SHA1 Message Date
Matthias Clasen
12858114a2 Merge branch 'more-size-allocation-fixes' into 'main'
widget: Skip popovers in allocation

See merge request GNOME/gtk!5615
2023-03-08 22:26:51 +00:00
Matthias Clasen
4f6133e00a Merge branch 'wip/chergert/fix-const-preserving-text-iter-order' into 'main'
textlinedisplaycache: fix const-preserving gtk_text_iter_order()

See merge request GNOME/gtk!5620
2023-03-08 22:06:04 +00:00
Matthias Clasen
9ca3a52bf0 Merge branch 'file_chooser_stuff' into 'main'
filechooser: two small changes

See merge request GNOME/gtk!5619
2023-03-08 21:57:13 +00:00
Christian Hergert
40d99839d6 textlinedisplaycache: fix const-preserving gtk_text_iter_order() 2023-03-08 13:00:08 -08:00
Barnabás Pőcze
111e8d2808 filechooser: Do not look up parents of directories
If the `GtkRecentInfo` represents a directory, simply use it, and
do not try to find its parent in `_gtk_file_chooser_extract_recent_folders()`.

For example, there is an entry in my recently-used database
from the Amberol music player about the folder I have opened
with it, but the folder is not listed on the "Recent" tab of
the file chooser widget, only its parent. After this change,
the directory itself is shown.
2023-03-08 20:46:17 +01:00
Barnabás Pőcze
9b0b3029a6 filechooser: Use gtk_recent_info_is_local() for filtering
There is already a function for determining if URI
has the "file" scheme, so use that.
2023-03-08 20:04:41 +01:00
Matthias Clasen
f743b95161 Merge branch 'ebassi/issue-5650' into 'main'
Filter recently used files in the file chooser

Closes #5650

See merge request GNOME/gtk!5618
2023-03-08 18:45:10 +00:00
Matthias Clasen
bdd35c1695 Merge branch 'fix-msvc-older-glib' into 'main'
modules/media: Fix Visual Studio builds with older GLib (<= 2.74.x)

See merge request GNOME/gtk!5605
2023-03-08 18:44:46 +00:00
Matthias Clasen
2f273ba76a widget: Skip popovers in allocation
Native widgets get allocated via their surface,
so can skip them here. This avoids criticals when
re-mapping a popover for the second time, as can
be seen e.g. in the 'Selections' demo in gtk4-demo.
2023-03-08 13:40:51 -05:00
Matthias Clasen
a2d14632de widget: Stop propagating alloc_needed beyond popovers
This should not be necessary, since popovers get
their new size from present_popup via the compositor.
2023-03-08 13:40:48 -05:00
Benjamin Otte
7a3e130efd widget: Remove goto usage in widget_allocate() 2023-03-08 13:40:37 -05:00
Benjamin Otte
c8d356a250 widget: Split out gtk_widget_ensure_allocate_on_children()
allocate() should not be calling into ensure_allocate(), they do a similar job.
In the end, the code does the same work, but it should be easier to follow now.
2023-03-08 13:40:31 -05:00
Matthias Clasen
ffb7177c2e widget: Simplify size allocation
Don't call ensure_allocate if we've just
done size_allocate. This makes criticals
from reshowing popovers go away.
2023-03-08 13:39:54 -05:00
Emmanuele Bassi
8b2202cd56 Filter recently used files in the file chooser
Since it's a file selection widget, we should only list recently used
files that point to a file.

Fixes: #5650
2023-03-08 18:08:24 +00:00
Matthias Clasen
14a5c78d80 Merge branch 'wip/carlosg/reuse-search-engine' into 'main'
gtkfilechooser: Keep GtkSearchEngine around for longer

See merge request GNOME/gtk!5617
2023-03-08 12:58:43 +00:00
Carlos Garnacho
5fd2a6c8f3 gtkfilechooser: Keep GtkSearchEngine around for longer
Currently the GtkSearchEngine is torn down every time the search
is stopped, which also means between typed characters. This
prevents any of the optimizations that the GtkSearchEngine can
do in the long run.

Let the GtkSearchEngine stay around for longer, and only be
disposed after search is cancelled, the filechooser moves
onto a different mode than search, or is otherwise unmapped/disposed.

While at it, remove an unused struct field.
2023-03-08 13:23:56 +01:00
Matthias Clasen
6cb6f4a50f Merge branch 'wip/carlosg/recursive-file-search' into 'main'
searchengine: Improve performance for recursive search

Closes #4133

See merge request GNOME/gtk!5611
2023-03-08 11:56:41 +00:00
Carlos Garnacho
7b909fb310 searchengine: Do not fight filechooser search mode sorting
Provide the hits in an order that is more easily consumed by the
filechooser, and less jarring when populating.
2023-03-08 11:33:10 +01:00
Carlos Garnacho
d12bea03d2 searchengine: Bail out on the first character
Again on massive filesystems, the very first character
is likely to bring a likewise massive amount of search
results that we need to maybe query info for, then create
icons and widgets for. While it's impressive we can do
that, it's also expensive and likely pointless, for the
first character.

Typing a second character is however very likely to
considerably reduce the amount of items to categorize and
show. So start actually searching from there.

Testing on a filesystem with 1434099 files indexed, trying 5
semi-random 1 character searches (n, h, t, i, o) returns on
average 168K items (min. 78771, max. 331471), trying 5
semi-random 2 character searches (no, he, th, in, on)
returns on average 34K items (min. 11133, max. 94961),
which is a more approachable set.

Doing this is enough that typing on a filechooser search
entry feels completely fluid.
2023-03-08 11:33:10 +01:00
Carlos Garnacho
deacc63d54 searchengine: Ensure some GFileInfo fields
The search provider should make it sure there are some
specific GFileInfo fields set. Fix the mimetype extraction
from the query, and use that to fill in the missing gaps
the best we can.
2023-03-08 11:33:10 +01:00
Carlos Garnacho
ead121a3c1 searchengine: Drop non-recursive search
Recursive search seems to be the hardcoded default, go with it.
2023-03-08 11:33:10 +01:00
Carlos Garnacho
de4725dbd5 searchengine: Populate filesystem model in an idle
When starting a search over a very populated filesystem, it
is possible that typing the first chars will return a too
high number of results. Even though iterating through the
cursor is in itself very fast, extracting the GIO information
from those many files at once is not going to be as fast.

In order to increase interactivity (i.e. not make things
possibly sluggish) iterate the cursor in an idle function
and add search results to the filechooser model little by little.

If the user keeps typing (as it is likely will happen), there
will be better chances to cancel and proceed to the next
query timely. If not, the results will be there soon enough.
2023-03-08 11:32:44 +01:00
Matthias Clasen
7f5504523f Merge branch 'check_null_gfile_location' into 'main'
filechooser: Fix memory leaks

See merge request GNOME/gtk!5610
2023-03-08 03:19:00 +00:00
Matthias Clasen
40b154bf28 Merge branch 'matthiasc/for-main' into 'main'
build: Handle introspection a bit better

See merge request GNOME/gtk!5614
2023-03-08 03:02:16 +00:00
Barnabás Pőcze
f0e4293b83 filechooser: Fix memory leaks
The returned strings from `file_chooser_get_location()` were never freed.
2023-03-08 03:59:10 +01:00
Matthias Clasen
87f820287a build: Handle introspection a bit better
Error out if introspection is requested,
but g-ir-scanner isn't found.

And if introspection isn't explicitly disabled
but is required for building the docs, build it.
2023-03-07 21:49:24 -05:00
Matthias Clasen
30586d75ac Merge branch 'matthiasc/for-main' into 'main'
filechooser: Handle pathless files

See merge request GNOME/gtk!5613
2023-03-08 02:31:19 +00:00
Matthias Clasen
7a1573a381 Merge branch 'expose_checkable_state' into 'main'
a11y: When an accessible has GTK_STATE_CHECKED, set its ATSPI_STATE_CHECKABLE as well

See merge request GNOME/gtk!5602
2023-03-08 02:07:39 +00:00
Matthias Clasen
bc42955a13 filechooser: Handle pathless files
g_file_get_path() can return NULL. Make our
getter for file locations handle that case.

Related: !5610
2023-03-07 20:54:31 -05:00
Matthias Clasen
cdc008763b Merge branch 'matthiasc/for-main' into 'main'
wayland: Don't crash during DND with Cairo renderer

See merge request GNOME/gtk!5612
2023-03-08 01:46:43 +00:00
Benjamin Otte
7fff188557 wayland: Don't crash during DND with Cairo renderer
Attaching buffers with offsets is forbidden now and nobody
checked the Cairo renderer.
2023-03-07 20:16:59 -05:00
Benjamin Otte
69238c1197 text: Don't crash when somebody drops NULL 2023-03-07 20:16:59 -05:00
Emmanuele Bassi
574380c744 Merge branch 'wroy-main-patch-03255' into 'main'
MSBuild: Fix gnome.compile_resources current source directory

See merge request GNOME/gtk!5609
2023-03-07 23:48:25 +00:00
Emmanuele Bassi
d1fc1adae7 Apply suggestion to use '/' over join_paths 2023-03-07 22:40:17 +00:00
Carlos Garnacho
64697ea95b searchengine: Improve performance for recursive search
As fancy as property paths are, recursive resolution of files
to a location increases the big O complexity enough that it's
not a great option on large homedirs with many indexed files.

Ensure the files are from the right location through a URI
prefix match, which does hits an index. This may dramatically
improve performance on large indexed trees.

Testing this query in an isolated testcase with a total
1434099 indexed files shows that it can run more than 1500 times
per second in this computer (an average of 15200 queries in
several 10 second runs), which presumably is a tad faster than
anyone can type.

Closes: https://gitlab.gnome.org/GNOME/gtk/-/issues/4133
2023-03-07 23:18:46 +01:00
William Roy
aaeec84d75 Fix compile_resources present source directory
In certain scenarios, address the issue where gnome.compile_resources 
fails to transmit the present source directory. This is most notably 
visible with MSBuild.
2023-03-07 21:59:50 +00:00
Matthias Clasen
5822ba76d0 Merge branch 'unbreak-combobox' into 'main'
combobox: Avoid extra queue_resize()

Closes #5644

See merge request GNOME/gtk!5608
2023-03-07 19:10:51 +00:00
Ask Hjorth Larsen
a6ad8ebe0c Updated Danish translation 2023-03-07 20:02:40 +01:00
Ivan Molodetskikh
55faea1046 combobox: Avoid extra queue_resize()
width-request already ensures it's above the minimum width, so avoid an
extra queue_resize() when setting size request to (-1, -1).

This is the same way as GtkDropDown works. This also unbreaks
GtkComboBox after the recent allocation fix in
75a417e337.

Incidentally, this also makes GtkComboBox actually resize its popup as
intended (that was broken before).

I don't think this is ultimately the final fix, sometimes I still get
allocation warnings. But the proper fix will probably involve changing
some more allocation machinery around popovers. This is good enough for
now.
2023-03-07 09:55:30 -08:00
Matthias Clasen
21ee81823e Merge branch 'window-signals' into 'main'
window: Disconnect the ::compute-size handler

See merge request GNOME/gtk!5606
2023-03-07 13:15:21 +00:00
Matthias Clasen
9efd8ef201 window: Disconnect the ::compute-size handler
All the other signal handlers are connected in
realize and disconnected in unrealize, but the
::compute-size handler was forgotten.

This was notices in !5597.
2023-03-07 06:40:25 -05:00
Chun-wei Fan
432e8664e1 modules/media: Fix Visual Studio builds with older GLib
The current definitions of the g_io_module_*() symbols do not build on
Visual Studio when building against GLib earlier than 2.75.0 due to the
way how these symbols are decorated in the GLib headers, as Visual Studio
does not allow symbols that were previously marked with 'extern' (or so)
to be marked with anything that is symantically different later.

As a result, if we are using Visual Studio and glib-2.74.x or earlier,
override _GLIB_EXTERN as appropriate in the modules/media sources before
including the GIO headers.  This sadly, means that we need a
configure-time check as it would have been too late if we checked the
GLib version using G_VERSION_CHECK macro, as the GIO headers would have
been included already.

There are similar items in the print backends, but we will not attempt
to update these files as they are not meant to be built for Windows.
2023-03-07 15:36:42 +08:00
Matthias Clasen
6d2c5e51e0 Merge branch 'expose_landmark_role' into 'main'
a11y: Expose GTK_ACCESSIBLE_ROLE_LANDMARK to AtSPI2

See merge request GNOME/gtk!5601
2023-03-06 17:49:05 +00:00
Lukáš Tyrychtr
b669295fd8 a11y: When an accessible has GTK_STATE_CHECKED, set its ATSPI_STATE_CHECKABLE as well
This allows, for example, checkable list itemss.
2023-03-06 17:15:54 +01:00
Lukáš Tyrychtr
96e6332d28 a11y: Expose GTK_ACCESSIBLE_ROLE_LANDMARK to AtSPI2 2023-03-06 16:45:19 +01:00
Marek Černocký
59549d0665 Update Czech translation 2023-03-06 11:57:22 +00:00
Emmanuele Bassi
3e3158ce12 Merge branch 'wip/exalm/template' into 'main'
widget: Use the correct template in dispose_template()

See merge request GNOME/gtk!5600
2023-03-06 11:00:04 +00:00
Matthias Clasen
cfc2de4e3d Merge branch 'main' into 'main'
removed duplicate function call

See merge request GNOME/gtk!5545
2023-03-06 05:52:00 +00:00
Matthias Clasen
343852af7b Merge branch 'matthiasc/for-main' into 'main'
macos: Fix type func generation

See merge request GNOME/gtk!5596
2023-03-06 05:50:36 +00:00
Matthias Clasen
51902bbce8 Merge branch 'wip/fix-wl-destruct-order' into 'main'
wayland: Destroy xdg_surface after role

See merge request GNOME/gtk!5598
2023-03-06 05:08:44 +00:00
Alexander Mikhaylenko
9b3fb66bd4 widget: Use the correct template in dispose_template()
In derivable classes, the widget's class can be different from the one
dispose_template() was called for, which can lead to failing the
template != NULL check at best, undefined behavior at worst.

Since we already pass the correct GType into the function, just use that
instead.
2023-03-06 05:37:53 +04:00
Benjamin Otte
3ccf63b3c0 Merge branch 'wip/otte/for-main' into 'main'
inspector: Make really sure we don't inspect ourselves

See merge request GNOME/gtk!5599
2023-03-05 23:49:15 +00:00
Benjamin Otte
4655226907 inspector: Make really sure we don't inspect ourselves
The problem here is that new windows appear in the list before the
window's dispay gets set and we don't update the filter when the
display changes (would need watches support for the filtermodel).

So add this somewhat hacky method.
2023-03-06 00:13:53 +01:00
Jonas Ådahl
4500fa633b wayland: Destroy xdg_surface after role
The split-up of gdksurface-wayland.c introduced a protocol violation
when it didn't make sure xdg_surface was destroyed after the role
objects (xdg_popup / xdg_toplevel). Fix that.

Fixes: 2a463baed0 ("wayland: Rearrange the surface code")
2023-03-05 20:55:41 +01:00
Matthias Clasen
be11202538 macos: Fix type func generation
We were checking for gdk_quartz even though
the sysbols are now all called gdk_macos. Oops.
2023-03-05 10:17:52 -08:00
Benjamin Otte
0fa1e71ef0 Merge branch 'wip/otte/for-main' into 'main'
cssnode: Don't crash the inspector

See merge request GNOME/gtk!5595
2023-03-05 17:53:18 +00:00
Benjamin Otte
987b9cec3f testsuite: Add tests for the unknown enums
See commit 40e7a265a7
2023-03-05 18:35:02 +01:00
Benjamin Otte
a77beb39a1 testsuite: Add empty test for new node 2023-03-05 18:33:37 +01:00
Benjamin Otte
ac8e053ab6 cssnode: Don't crash the inspector
Don't misinform the observing listmodel that CSS nodes were removed that
weren't actually removed, but just moved. Otherwise the observer would
think it has run out of items when it really hasn't.
2023-03-05 18:33:37 +01:00
Matthias Clasen
986275239f Merge branch 'matthiasc/for-main' into 'main'
meson: Mention testsuite in summary

See merge request GNOME/gtk!5594
2023-03-05 16:51:23 +00:00
Matthias Clasen
2ee1273244 meson: Mention testsuite in summary
This was broken out from the 'tests' toggle,
so list it separately.
2023-03-05 08:10:12 -08:00
Matthias Clasen
248027a366 Merge branch 'expose_error_message_relation' into 'main'
a11y: Expose GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE to AtSPI2

See merge request GNOME/gtk!5588
2023-03-05 16:08:38 +00:00
Benjamin Otte
c823432eef Merge branch 'wip/otte/listview2' into 'main'
listview: GtkListTile

See merge request GNOME/gtk!5584
2023-03-05 15:53:43 +00:00
Matthias Clasen
98ac2de649 Merge branch 'matthiasc/for-main' into 'main'
ci: Disable the testsuite in flatpak builds

See merge request GNOME/gtk!5593
2023-03-05 15:46:13 +00:00
Matthias Clasen
1cee54009a ci: Disable the testsuite in flatpak builds
No need to build the testsuite when we are
just producing flatpaks.
2023-03-05 07:26:17 -08:00
Benjamin Otte
80b7c60150 listbase: Remove nonexisting function from header 2023-03-05 15:23:20 +00:00
Benjamin Otte
e8c5a771e5 gridview: Add a filler tile for empty space
That stupid space in the bottom right when n_items isn't a multiple of
n_columns needs its own tile, or we'll get errors about not finding a
tile.

So make one.
2023-03-05 15:23:20 +00:00
Benjamin Otte
882dcda53b columnview: Clear the sorter first thing in dispose
Otherwise, when removing the columns, each column will trigger a
sorter::changed signal emission.

And because sorters are often still connected to a sortlistmodel, we
can't skip that emission and need to do it.
But we only need to do it once.
2023-03-05 15:23:20 +00:00
Benjamin Otte
b488fae893 listview: Handle emptying of views
The previous check does not longer work.

When a model gets all items deleted, there will still be existing tiles
until the next time garbage collection is run.
So do that before checking if the list is empty.
2023-03-05 15:23:20 +00:00
Benjamin Otte
b962defc11 listview: Remove an unused member variable
It's only used in the allocate function, so make it a local varaible
there.
2023-03-05 15:23:20 +00:00
Benjamin Otte
9ee0696923 listview: Simplify a vfunc
Instead of making it 2 vfuncs for getting horizontal and vertical area,
make it one vfunc to get the area.

Also rewrite the implementations to use the tile's area instead of
trying to deduce things with fancy math.
2023-03-05 15:23:20 +00:00
Benjamin Otte
f3c53ae69d listview: Simplify allocation
With the Tile changes, a lot of stuff does no longer need to be
duplicated between listview and gridview. Move it to ListBase instead.
2023-03-05 15:23:20 +00:00
Benjamin Otte
8aea6fc1b5 gridview: Remove an unused member variable
It's only used during size_allocate(), so make it a local variable
there.
2023-03-05 15:23:20 +00:00
Benjamin Otte
d949afb80e listitemmanager: Add a split vfunc and use it
Instead of randomly changing tiles, the listitemmanager gains a split
vfunc that listview and gridview implement so they can keep their tile
areas intact. The listitemmanager will now conform to these rules:

1. Never delete a tile.
   This ensures that all areas stay intact.

2. Never change the n_items of a tile other than setting them to 0.
   This causes "empty" areas to appear, but listview/gridview can
   easily check for them by checking for tile->n_items == 0.
   gtk_list_tile_gc() will get rid of them.

3. Adding items always creates new tiles that are added with empty area.
   That way they don't interrupt any existing machinery until the next
   allocation.

4. Adding/removing widgets has no effect on areas
   This is useful in particular when scrolling where new widgets are
   moving between tiles. When the manager moves the widgets, it may
   split some areas, but will not remove any existing tiles, so the
   whole area stays intact and the list can deal with further scroll
   events before an allocation.

This improve the situation for #3334
2023-03-05 15:23:20 +00:00
Benjamin Otte
08c583b1b3 listview: Add gc'ing
This is in preparation for the following reorg of the listitemmanager,
it should not have any effect now.
2023-03-05 15:23:20 +00:00
Benjamin Otte
1c663cb340 listitemmanager: Remove unused functionality
Both ListView and GridView use GtkListTile now, so no need anymore for
custom machinery.
2023-03-05 15:23:20 +00:00
Benjamin Otte
776910d03d gridview: Redo tile management
Instead of the custom size property, use the new tile size.

Also introduce the ability to split tiles, so that gridview can split a
layout that would look like (question mark denoting cells without a
widget, which in this case would be a single tile)

█ █ █ ? ?
? ? ? ? ?
? ? ? ? ?
? ? ?

into 3 rectangular tiles like so:

█ █ █ A A
B B B B B
B B B B B
C C C

This of course also means we need to be able to merge those tiles again
when cells got added/deleted or the gridview was resized. For that job,
gtk_list_tile_gc() exists now, which removes tiles without items and
merges adjacent tiles without widgets.
2023-03-05 15:23:20 +00:00
Benjamin Otte
c82b2d86c0 listview: Get rid of ListRow and its height
Use the tiles' area instead.
2023-03-05 15:23:20 +00:00
Benjamin Otte
3ca4acdc1a list: Allow storing size in the ListTile
... and use it to handle ListView allocations.

Nothing spectacular, just proof of concept.

The code introduces the idea that every tile stores its area (others
would call it "allocation", but I avoided that because tiles aren't
widgets). This should allow moving lots of code into gtklistbase.c and
not require special handling inside ListView and GridView.

And that in turn hopefully makes it easier to add more features (like
sections and so on.)
2023-03-05 15:23:20 +00:00
Benjamin Otte
c705dba2ee list: Make GtkListTile more prominent
* Instead of using a gpointer to refer to it, use the GtkListTile type.

* Use gtk_list_tile_get_foo() instead of
  gtk_list_item_manager_get_tile_foo() naming.
2023-03-05 15:23:20 +00:00
Benjamin Otte
55ad241f43 list: Rename GtkListItemManagerItem => GtkListTile
That's a good description for what the job of those elements is:
They're a tile in the lists scrollable area.
2023-03-05 15:23:20 +00:00
Benjamin Otte
d3efd80b90 rendernodepaintable: Allow the node to be NULL
This can happen when creating paintables from GtkSnapshot when nothing
was drawn.
2023-03-05 15:23:20 +00:00
Jordi Mas
a899c0af6e Update Catalan translation 2023-03-05 09:44:49 +01:00
Matthias Clasen
f8fb30b555 Post-release version bump 2023-03-04 13:47:37 -08:00
Matthias Clasen
47aa0dcc7f 4.10.0 2023-03-04 08:28:12 -08:00
Emmanuele Bassi
32f0723bf0 Merge branch 'ebassi/file-info-attributes' into 'main'
Check for attributes being available before querying them

See merge request GNOME/gtk!5592
2023-03-04 16:04:04 +00:00
Matthias Clasen
90a3584c1d Merge branch 'wip/otte/dont-scale-or-it-breaks' into 'main'
Add a test for lots of texture scaling and fix the fallout

See merge request GNOME/gtk!5509
2023-03-04 15:57:49 +00:00
Emmanuele Bassi
891b6dc4a9 Check for attributes being available before querying them
GLib 2.75 started checking if a GFileInfo was created with the attribute
we're querying, instead of failing silently and leaving us in an
inconsistent state.

Turns out that GtkFileChooserWidget, GtkFileSystemModel, and GtkPathBar
trip the newly introduced check.
2023-03-04 15:28:53 +00:00
Emmanuele Bassi
c6ff7400a8 Merge branch 'wip/chergert/fix-action-activation-crash' into 'main'
inspector: be defensive against out parameters

See merge request GNOME/gtk!5591
2023-03-04 15:16:59 +00:00
Emmanuele Bassi
6620b7de92 Merge branch 'ebassi/stack-page-fix' into 'main'
a11y: Fix GtkStackPage first accessible child

See merge request GNOME/gtk!5590
2023-03-04 15:16:34 +00:00
Aleksandr Melman
d58071b4c1 Update Russian translation 2023-03-04 15:05:45 +00:00
Piotr Drąg
f38df62e2b Update Polish translation 2023-03-04 15:59:29 +01:00
Benjamin Otte
0581e38b09 testsuite: Add a test for stripes
The GL renderer was creating sripes for nodes that were scaled in
particular ways, probably due to rounding errors.

This testsuite focuses on one of those stripes to make sure they are
gone.
2023-03-04 02:50:38 +01:00
Emmanuele Bassi
74a00319dd a11y: Use weak references for GtkATContext cached accessibles
The accessible objects already own the GtkATContext, let's avoid a
reference cycle.
2023-03-03 22:07:09 +00:00
Matthias Clasen
611788fb53 stack: Don't recreate at contexts in dispose
This is the same protection we have in
GtkWidgetAccessible.
2023-03-03 22:07:09 +00:00
Emmanuele Bassi
500128d186 a11y: Plug reference leaks
The gtk_accessible_get_at_context() getter is now transfer full, which
means we need to drop the reference when getting the GtkATContext.
2023-03-03 22:07:09 +00:00
Christian Hergert
32cf104167 inspector: be defensive against out parameters
Set initial state to NULL so that we don't risk accessing an unset out
parameter.

Fixes a crash when activating certain actions.
2023-03-03 12:17:32 -08:00
Emmanuele Bassi
9b98426e71 a11y: Simplify bookkeeping while iterating accessible children 2023-03-03 18:02:22 +00:00
Emmanuele Bassi
9820d25cf9 a11y: Fix GtkStackPage first accessible child
The first accessible child is the child widget of the GtkStackPage, not
the first child of the widget.
2023-03-03 18:02:22 +00:00
Benjamin Otte
8bbf220fdf testsuite: Add a test for large scale nodes
This test fails if we naively create fullscale
intermediate offscreens. This was fixed in the
previous commits.

This tests the fixes in 22ba6b1f33 (for
cairo) and 3a0152b65f (for GL).
2023-03-03 11:33:57 -06:00
Matthias Clasen
3a0152b65f gl: Respect clip wehn drawing scale nodes
Use the same approach and only create an offscreen
that is big enough for the clipped part of the scaled
texture.

If the clipped part is still too large for a single
texture, we give up and just render the texture without
filters (using the regular texture rendering code path
which supports slicing).

The following commit will add the texture-scale-magnify-10000x
test which fails without this fix.
2023-03-03 11:32:08 -06:00
Benjamin Otte
22ba6b1f33 rendernode: Respect clip when drawing scale nodes
Scale nodes can use large scale factors and we don't want to create
insanely huge Cairo surfaces.

A subsequent commit will add the texture-scale-magnify-10000x
test which fails without this fix.
2023-03-03 11:31:31 -06:00
Benjamin Otte
718b5d5fe7 rendernode: Clarify some Cairo stuff
Split out a function to make it more obvious what's going on.
2023-03-03 11:24:46 -06:00
Benjamin Otte
88dd64551c rendernode: Don't do unnecessary stuff
Cairo surfaces are created transparent.

And even if they weren't, overdrawing with transparency wouldn't erase
what's in the surface because it's a no-op.

It would require CAIRO_OPERATOR_CLEAR or CAIRO_OPERATOR_SOURCE.
2023-03-03 11:24:46 -06:00
Benjamin Otte
98eac8ac83 Fixes for gdk_memory_texture_new_subtexture checks
There were several mistakes here.
2023-03-03 11:24:46 -06:00
Changwoo Ryu
dbb2cb22ca Update Korean translation 2023-03-03 15:36:03 +00:00
Lukáš Tyrychtr
71dbb5090e a11y: Expose GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE to AtSPI2
Also, update the AtSPI2 relations to match the 1.26 version of AtSPI2.
2023-03-03 15:15:59 +01:00
Matthias Clasen
4b46097748 Merge branch 'ebassi/accessible-transfer-full' into 'main'
Mark Accessible getters as transfer full

Closes #5615

See merge request GNOME/gtk!5586
2023-03-03 09:46:51 +00:00
Emmanuele Bassi
eb0f33d76b Mark Accessible getters as transfer full
GtkAccessible implementations in C can get away returning objects just
by shuffling pointers around, but higher level languages prefer using
full ownership transfer in virtual functions.

Fixes: #5615
2023-03-03 02:13:26 +00:00
Emmanuele Bassi
828f686fc9 Merge branch 'expose_details_relation' into 'main'
a11y: Expose GTK_ACCESSIBLE_RELATION_DETAILS to at-spi2

See merge request GNOME/gtk!5580
2023-03-03 01:55:48 +00:00
Emmanuele Bassi
681f8bed19 Merge branch 'fix_section_role' into 'main'
a11y: Fix mapping of GTK_ACCESSIBLE_ROLE_PRESENTATION

See merge request GNOME/gtk!5576
2023-03-03 01:54:51 +00:00
Matthias Clasen
5fe32a46a0 Merge branch 'ebassi/atcontext-dispose' into 'main'
a11y: Clear the accessible tree in GtkATContext

See merge request GNOME/gtk!5587
2023-03-03 01:54:38 +00:00
Emmanuele Bassi
e3548bb9ad Revert "stack: clear accessible parent when removing child"
This reverts commit 40d4441fd8.

The accessible parent of the child widget in a GtkStackPage is cleared
when the GtkATContext gets disposed, so we don't need to unset it
ourselves. This also avoids a temporary vivification of the GtkATContext
during dispose.
2023-03-03 01:39:24 +00:00
Emmanuele Bassi
61506648bf a11y: Clear the accessible tree in GtkATContext
We acquire a reference on the accessible objects, so we need to release
it when disposing the GtkATContext.
2023-03-03 01:38:27 +00:00
Balázs Úr
621b3b51ea Update Hungarian translation 2023-03-03 00:35:25 +00:00
Matthias Clasen
784ba8bf12 Merge branch 'matthiasc/for-main' into 'main'
impcontextsimple: Return from GTask

See merge request GNOME/gtk!5585
2023-03-03 00:19:38 +00:00
Matthias Clasen
d968659b43 impcontextsimple: Return from GTask
GLib complains about this now, so do it.
2023-03-02 16:18:45 -05:00
Emmanuele Bassi
48d3db8daa Merge branch 'leak-fix' into 'main'
gtkatspicontext: Fix a leak of a floating GVariant

See merge request GNOME/gtk!5583
2023-03-01 20:41:53 +00:00
Philip Withnall
ca702b4596 gtkatspicontext: Fix a leak of a floating GVariant
If the early return path in `emit_property_changed()` is taken, and
`value` is floating, it will be leaked. Fix that by sinking `value` on
entry to the function.

Spotted by asan:
```
Direct leak of 128 byte(s) in 2 object(s) allocated from:
    #0 0x7f44774ba6af in __interceptor_malloc (/lib64/libasan.so.8+0xba6af)
    #1 0x7f44764c941a in g_malloc ../../source/glib/glib/gmem.c:130
    #2 0x7f44764f6d8a in g_slice_alloc ../../source/glib/glib/gslice.c:252
    #3 0x7f447654655d in g_variant_alloc ../../source/glib/glib/gvariant-core.c:565
    #4 0x7f447654664c in g_variant_new_from_bytes ../../source/glib/glib/gvariant-core.c:608
    #5 0x7f4476536ed5 in g_variant_new_take_string ../../source/glib/glib/gvariant.c:1307
    #6 0x7f4475c75ada in gtk_at_spi_context_state_change ../../source/gtk4/gtk/a11y/gtkatspicontext.c:1112
    #7 0x7f44758ee194 in gtk_at_context_update ../../source/gtk4/gtk/gtkatcontext.c:694
    #8 0x7f44758dbfcf in gtk_accessible_update_property ../../source/gtk4/gtk/gtkaccessible.c:326
    #9 0x7f4475b5abe3 in gtk_widget_set_tooltip_text ../../source/gtk4/gtk/gtkwidget.c:9740
    #10 0x58439d in gs_updates_page_update_ui_state ../../source/gnome-software/src/gs-updates-page.c:302
    #11 0x5857dc in gs_updates_page_set_state ../../source/gnome-software/src/gs-updates-page.c:403
    #12 0x5879f1 in gs_updates_page_load ../../source/gnome-software/src/gs-updates-page.c:636
    #13 0x58822d in gs_updates_page_reload ../../source/gnome-software/src/gs-updates-page.c:678
    #14 0x50ff48 in gs_page_reload ../../source/gnome-software/src/gs-page.c:731
    #15 0x5491ce in gs_shell_reload_cb ../../source/gnome-software/src/gs-shell.c:830
    #16 0x7f4477363f54 in g_cclosure_marshal_VOID__VOID ../../source/glib/gobject/gmarshal.c:117
    #17 0x7f447735e0ad in g_closure_invoke ../../source/glib/gobject/gclosure.c:832
    #18 0x7f4477391f3f in signal_emit_unlocked_R ../../source/glib/gobject/gsignal.c:3802
    #19 0x7f4477390c13 in g_signal_emit_valist ../../source/glib/gobject/gsignal.c:3555
    #20 0x7f4477391324 in g_signal_emit ../../source/glib/gobject/gsignal.c:3612
    #21 0x7f447705b3c3 in gs_plugin_loader_reload_delay_cb ../../source/gnome-software/lib/gs-plugin-loader.c:1538
    #22 0x7f44764bd140 in g_timeout_dispatch ../../source/glib/glib/gmain.c:5054
    #23 0x7f44764b9eb1 in g_main_dispatch ../../source/glib/glib/gmain.c:3460
    #24 0x7f44764bb72c in g_main_context_dispatch ../../source/glib/glib/gmain.c:4200
    #25 0x7f44764bba15 in g_main_context_iterate ../../source/glib/glib/gmain.c:4276
    #26 0x7f44764bbbfa in g_main_context_iteration ../../source/glib/glib/gmain.c:4343
    #27 0x7f44769ef655 in g_application_run ../../source/glib/gio/gapplication.c:2589
    #28 0x4f2da5 in main ../../source/gnome-software/src/gs-main.c:49
    #29 0x7f4474e4a50f in __libc_start_call_main (/lib64/libc.so.6+0x2750f)
```

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2023-03-01 19:42:33 +00:00
Lukáš Tyrychtr
c91ba630a7 a11y: Expose GTK_ACCESSIBLE_RELATION_DETAILS to at-spi2
This particular relation was not exposed to at-spi2.
Exposing it required adding a missing at-spi2 relation variant, but it was introduced
in at-spi2-core 2.26, so that's likely safe as well.
2023-03-01 13:53:39 +01:00
Matthias Clasen
bc99ab38ce Merge branch 'matthiasc/for-main' into 'main'
inspector: Remove debug spew

See merge request GNOME/gtk!5579
2023-03-01 12:36:49 +00:00
Matthias Clasen
fe8c262351 inspector: Remove debug spew 2023-03-01 06:32:52 -05:00
Lukáš Tyrychtr
1b000b5586 a11y: Fix mapping of GTK_ACCESSIBLE_ROLE_PRESENTATION
It was, for some reason, mapped to ATSPI_ROLE_SECTION, and GTK_ACCESSIBLE_ROLE_SECTION was mapped to
ATSPI_ROLE_FILLER, so the mapping is reversed. So, reverse it and make it correct.
2023-03-01 11:07:09 +01:00
Benjamin Otte
1b4f240883 Merge branch 'wip/otte/default-size-docs' into 'main'
window: Clarify docs for default-size

See merge request GNOME/gtk!5569
2023-03-01 10:06:34 +00:00
Benjamin Otte
496f68376b window: Clarify docs for default-size 2023-03-01 10:06:32 +00:00
Matthias Clasen
f019ff5287 Merge branch 'textbuffer-foreground-property' into 'main'
textbuffer: Use correct foreground color property

See merge request GNOME/gtk!5574
2023-03-01 02:46:05 +00:00
Mat
c08e739996 textbuffer: Use correct foreground color property
The 'foreground-rgba' property should be used instead of 'foreground',
since the latter is not readable.
2023-03-01 04:14:19 +02:00
Emmanuele Bassi
8eb3c55709 Merge branch 'wip/chergert/fix-stack-page-leak' into 'main'
stack: clear accessible parent when removing child

Closes #5626

See merge request GNOME/gtk!5571
2023-02-28 23:24:57 +00:00
Christian Hergert
40d4441fd8 stack: clear accessible parent when removing child
Without this, the GtkStackPage may not reach a reference count of zero
and therefore will be leaked.

Fixes #5626
2023-02-28 15:08:20 -08:00
Benjamin Otte
471ebabd77 Merge branch 'gbsneto/filechooser-fixes' into 'main'
Fix GtkFileThumbnail thumbnail query

See merge request GNOME/gtk!5570
2023-02-28 18:04:40 +00:00
Georges Basile Stavracas Neto
d8b7c909ea filethumbnail: Set filechooser::queried after querying
Setting this attribute after querying, but before receiving the
results, can lead to inappropriate behaviour. This can be reproduced
by dragging the scrollbar very quickly in a large directory; after
going up and down a few times, some thumbnails will be wrong.

Without this branch, "wrong" means they'll show the completely wrong
icon or thumbnail, e.g. a folder icon in a video file. With previous
commit, "wrong" means they'll be empty even when there is a thumbnail
available.

The sequence of events that triggers this is as follows:

 1. GtkListItem receives a GFileInfo object and passes it to
    GtkFileThumbnail via expressions

 2. `get_thumbnail()` is called, doesn't find a thumbnail

 3. `filechooser::queried` is not set yet, so it is set to TRUE
      and we call `g_file_query_info_async()`

 4. **Before `thumbnail_queried_cb` is called**, a new GFileInfo
    is set, and we cancel the query initiated in the previous
    step

 5. We now have a GFileInfo with `filechooser::queried` set to
    TRUE, and no thumbnail!

This commit fixes that by only setting the `filechooser::queried`
attribute after the icon is queried. We need to set it in two
situations: when the query is successful, or when the error is
not G_IO_ERROR_CANCELLED. That's because the query was cancelled,
we didn't really perform it!
2023-02-28 14:18:30 -03:00
Georges Basile Stavracas Neto
158165f769 filethumbnail: Clear image on failure
Unset the image if we fail to find the appropriate icon, regardless
of the reason of the failure. Prevents the thumbnail to misrepresent
the GFileInfo it's supposed to represent.
2023-02-28 13:52:12 -03:00
Georges Basile Stavracas Neto
2d90031dab filethumbnail: Cosmetics
Remove an empty newline
2023-02-28 13:50:08 -03:00
Matthias Clasen
2a862c05a1 Merge branch 'matthiasc/for-main' into 'main'
inspector: Look for icon themes in system data dirs

See merge request GNOME/gtk!5567
2023-02-28 16:35:48 +00:00
Matthias Clasen
93b6c7863b Merge branch 'main' into 'main'
Update docs/reference/gtk/getting_started.md

See merge request GNOME/gtk!5558
2023-02-28 16:23:26 +00:00
Matthias Clasen
090f45a589 Merge branch 'wip/exalm/buildable' into 'main'
builderparser: Don't exit too early on nested custom tags

See merge request GNOME/gtk!5566
2023-02-28 16:21:19 +00:00
Alexander Mikhaylenko
ca737b1411 testsuite: Add a GtkBuildable custom tag test
Just test that it doesn't error out during parsing.
2023-02-28 17:51:38 +04:00
Matthias Clasen
b2af4006ba inspector: Look for icon themes in system data dirs
For some reason, these were not included, and they should.
2023-02-28 07:18:42 -05:00
Matthias Clasen
5d3942e5fa NEWS: Updates 2023-02-28 06:53:26 -05:00
Alexander Mikhaylenko
2bcc3cfb33 builderparser: Don't exit too early on nested custom tags
Currently nested custom tags work only as long as the element names differ
from the root one. If it's same, for example:

<condition type="any">
  <condition type="max-width">600</condition>
  <condition type="max-height">600</condition>
</condition>

then it will fail. Meanwhile the same tags wrapped into <conditions> would
work.

The problem is that custom tag parsing is considered finished as soon as we
encounter a closing tag with the same element name. So instead, track the
nesting level.
2023-02-28 14:46:14 +04:00
Matthias Clasen
a7a498e803 Merge branch 'alloc-needed-on-child-fix' into 'main'
widget: Reset alloc_needed_on_child before allocating children

See merge request GNOME/gtk!5564
2023-02-28 10:40:02 +00:00
Ivan Molodetskikh
75a417e337 widget: Reset alloc_needed_on_child before allocating children
Reset alloc_needed_on_child *before* allocating the children. This is
because some child's size_allocate() may call queue_allocate(), which
will bubble up alloc_needed_on_child. An example of this happening is
with GtkScrollable implementations, which are supposed to configure
their adjustments in size_allocate(), which will cause GtkScrollbar's
GtkRange to notice and queue_allocate() on itself.

If we reset alloc_needed_on_child after this happens, then our children
will have a lingering alloc_needed_on_child and will not receive an
allocation.

This commit fixes widgets occasionally losing an allocation when this
scenario happens.
2023-02-27 21:38:42 -08:00
Matthias Clasen
dbaaa59758 Merge branch 'fix-text-undo-disabling' into 'main'
text: Fix disabling of history

Closes #5622

See merge request GNOME/gtk!5565
2023-02-28 02:39:30 +00:00
Matthias Clasen
73ba043b02 text: Make editable API irreversible
Programmatic changes to the entry contents should
not become part of the undo history.

Sadly, the editable implementations are also used
in the code paths that we use for user-initiated changes,
so we have to be careful to only set them as
irreversible if we are not already in a user action.

Fixes: #5622
2023-02-27 19:43:11 -05:00
Matthias Clasen
61e4eadbce text: Fix disabling of history
Keep a separate boolean for enable-undo, and
disable the history if it is false, or the entry
is not using visible text, or isn't editable.

Related to: #5622
2023-02-27 19:17:08 -05:00
Matthias Clasen
0db2d0bc07 Merge branch 'expose_autocomplete' into 'main'
a11y: Expose GTK_ACCESSIBLE_PROPERTY_AUTOCOMPLETE

See merge request GNOME/gtk!5563
2023-02-27 16:49:00 +00:00
Emmanuele Bassi
cd10e5dd1a Verbiage Change 2023-02-27 16:32:35 +00:00
Lukáš Tyrychtr
c57d6c5575 a11y: Expose GTK_ACCESSIBLE_PROPERTY_AUTOCOMPLETE
Previously, it was not exposed at all, now it at least somewhat is.
2023-02-27 15:27:07 +01:00
Emmanuele Bassi
73057b267e Merge branch 'expose_has_popup' into 'main'
a11y: Expose GTK_ACCESSIBLE_PROPERTY_HAS_POPUP

See merge request GNOME/gtk!5562
2023-02-27 14:20:44 +00:00
Lukáš Tyrychtr
9d4bb77263 a11y: Expose GTK_ACCESSIBLE_PROPERTY_HAS_POPUP
This property was not exposed before, not it is.
2023-02-27 14:10:11 +01:00
Emmanuele Bassi
0d6cee9763 Merge branch 'expose_multi_selectable' into 'main'
a11y: Expose GTK_ACCESSIBLE_PROPERTY_MULTI_SELECTABLE to at-spi2

See merge request GNOME/gtk!5561
2023-02-27 12:41:11 +00:00
Lukáš Tyrychtr
b33bfe26fe a11y: Expose GTK_ACCESSIBLE_PROPERTY_MULTI_SELECTABLE to at-spi2
Previously, this property was not exposed to the a11y backend, now, it is.
2023-02-27 13:24:56 +01:00
Emmanuele Bassi
0cacaa08f5 Merge branch 'expose_required' into 'main'
a11y: Expose GTK_ACCESSIBLE_PROPERTY_REQUIRED to at-spi2

See merge request GNOME/gtk!5560
2023-02-27 11:30:59 +00:00
Lukáš Tyrychtr
cbf4a546b4 a11y: Expose GTK_ACCESSIBLE_PROPERTY_REQUIRED to at-spi2
Up until now, this property was silently not exposed to assistive technologies.
2023-02-27 12:17:58 +01:00
Emmanuele Bassi
03732c1d48 Merge branch 'fix_invalid_role_mapping' into 'main'
a11y: Map GTK_ACCESSIBLE_STATE_INVALID to ATSPI_STATE_INVALID_ENTRY

See merge request GNOME/gtk!5559
2023-02-27 11:02:40 +00:00
Lukáš Tyrychtr
568cf21486 a11y: Map GTK_ACCESSIBLE_STATE_INVALID to ATSPI_STATE_INVALID_ENTRY
Previously, it was mapped to ATSPI_STATE_INVALID. However, that state
is used for some internal errors, and not user errors, so use the correct
one for that purpose.
2023-02-27 11:40:46 +01:00
Bobby Neal
f11d647aa4 Update docs/reference/gtk/getting_started.md 2023-02-26 17:04:17 +00:00
Hugo Carvalho
49feaf465d Update Portuguese translation 2023-02-26 16:20:29 +00:00
Matthias Clasen
9b0afd8bff Merge branch 'pickers-demo-dnd' into 'main'
gtk4-demo: Accept file dnd in the pickers demo

See merge request GNOME/gtk!5556
2023-02-26 13:44:30 +00:00
Matthias Clasen
495ca72232 gtk4-demo: Accept file dnd in the pickers demo
Easy to add and expected of a file picker.
2023-02-26 08:28:38 -05:00
Alexander Shopov
e6f4464a3d Update Bulgarian translation 2023-02-26 08:48:06 +00:00
Anders Jonsson
cb0e4ee2be Update Swedish translation 2023-02-25 21:57:36 +00:00
Matthias Clasen
6f7b6ad61a Merge branch 'ebassi/button-no-check' into 'main'
Remove unused code in GtkButton

See merge request GNOME/gtk!5553
2023-02-25 13:41:52 +00:00
Emmanuele Bassi
1e599c9141 Remove unused code in GtkButton
GtkButton still has some code checking if the instance passed to
gtk_button_set_label() is a GtkCheckButton; GtkCheckButton is not a
GtkButton any more.
2023-02-24 14:38:39 +00:00
Yosef Or Boczko
4cf42eb19c Update Hebrew translation 2023-02-24 09:57:37 +00:00
Matthias Clasen
b0fc2f99ba Merge branch 'mcatanzaro/#5619' into 'main'
filesystemmodel: fix crash when file is removed

Closes #5619

See merge request GNOME/gtk!5550
2023-02-23 16:24:06 +00:00
Emmanuele Bassi
2b4dd182c1 Merge branch 'add_toggle_button_role' into 'main'
a11y: Introduce a dedicated role for toggle buttons

See merge request GNOME/gtk!5549
2023-02-23 15:22:16 +00:00
Michael Catanzaro
c141d0a70a filesystemmodel: fix crash when file is removed
In 32247bc50e node_get_for_file() was
changed to return GTK_INVALID_LIST_POSITION rather than 0 when the file
is untracked. Most call sites were updated accordingly, but this one was
missed.

Fixes #5619
2023-02-23 09:20:03 -06:00
Lukáš Tyrychtr
6fa8033c7c Use the correct role for GtkToggleButton 2023-02-23 15:02:29 +01:00
Lukáš Tyrychtr
de80f503e4 a11y: Introduce a dedicated role for toggle buttons
Up until now, toggle buttons were presented as regular push buttons.
That's the approach used by the ARIA specification, however, our platform
accessibility backend, at-spi2, can not represent accessibe states with values,
so we can not represent the design pattern precisely enough for screen readers.
If, in future, the a11y backends gain this capability, we might consider again
removing this role.
2023-02-23 14:22:20 +01:00
Matthias Clasen
41b67c4722 Merge branch 'matthiasc/for-main' into 'main'
gtk-demo: Fix the shortcuts-window demo

See merge request GNOME/gtk!5548
2023-02-23 01:03:41 +00:00
Matthias Clasen
1eae87621d gtk-demo: Fix the shortcuts-window demo
Presenting the shortcutswindow got lost
in fb8e52f0c9. Oops.
2023-02-22 14:57:47 -05:00
Fran Dieguez
5f502601c8 Update Galician translation 2023-02-22 17:23:21 +00:00
Sabri Ünal
04d23c5cb9 Update Turkish translation 2023-02-22 11:41:36 +00:00
Philipp Kiemle
d9b6f814ad Update German translation 2023-02-22 10:34:50 +00:00
Matthias Clasen
871c46591a Merge branch 'improve_gtknotebook_a11y' into 'main'
GtkNotebook: Improve the labels of notebook pages for a11y

See merge request GNOME/gtk!5546
2023-02-21 16:48:28 +00:00
Lukáš Tyrychtr
d906b456a8 GtkNotebook: Improve the labels of notebook pages for a11y 2023-02-21 15:29:00 +01:00
Michael J. Baars
32bc7ce987 removed duplicate function call 2023-02-21 13:28:15 +01:00
107 changed files with 13713 additions and 12678 deletions

View File

@ -20,6 +20,7 @@ flatpak build ${builddir} meson \
-Dx11-backend=true \
-Dwayland-backend=true \
-Dbuild-tests=false \
-Dbuild-testsuite=false \
-Dbuild-examples=false \
-Dintrospection=disabled \
-Ddemos=true \

61
NEWS
View File

@ -1,5 +1,62 @@
Overview of Changes in 4.9.5, xx-xx-xxxx
========================================
Overview of Changes in 4.10.1, xx-xx-xxxx
=========================================
Overview of Changes in 4.10.0, 04-03-2023
=========================================
* GtkTextView
- Document hanging indentation
* GtkListView
- Fix a size allocation problem
* GtkFileChooser
- Fix paned behavior
- Fix a crash
* GtkText
- Fix various problems with undo
* Accessibility
- Make some getters transfer-full
- Allow setting accessible parents and siblings
- Add a role for toggle buttons
- Miscellaneous property fixes and improvements
* gtk
- Improve the handling resize-during-size-allocate
* gdk
- Introduce GdkTextureDownloader and use it
- Make gdk_texture_get_format public
* gsk
- Make mask nodes more versatile
- Improve the GL implementation for texture scale nodes
* X11
- Fix key handling during DND
* Tools
- gtk-builder-tool: Try harder to handle templates
- gtk-builder-tool: Prefer properties over <child>
* Translation updates
Basque
Belarusian
Bulgarian
Indonesian
Galician
Georgian
German
Hebrew
Lithuanian
Portuguese
Spanish
Swedish
Turkish
Ukrainian
Overview of Changes in 4.9.4, 12-02-2023
========================================

View File

@ -9,7 +9,7 @@ constraint_editor_sources = [
constraint_editor_resources = gnome.compile_resources('constraint_editor_resources',
'constraint-editor.gresource.xml',
source_dir: '.',
source_dir: meson.current_source_dir(),
)
executable('gtk4-constraint-editor',

View File

@ -225,7 +225,7 @@ if not meson.is_cross_build() and build_machine.cpu_family() != 'arm' and build_
else
gtkdemo_resources = gnome.compile_resources('gtkdemo_resources',
'demo.gresource.xml',
source_dir: '.',
source_dir: meson.current_source_dir()
)
endif

View File

@ -13,20 +13,13 @@
static GtkWidget *app_picker;
static void
file_opened (GObject *source,
GAsyncResult *result,
void *data)
set_file (GFile *file,
gpointer data)
{
GFile *file;
GError *error = NULL;
char *name;
file = gtk_file_dialog_open_finish (GTK_FILE_DIALOG (source), result, &error);
if (!file)
{
g_print ("%s\n", error->message);
g_error_free (error);
gtk_widget_set_sensitive (app_picker, FALSE);
g_object_set_data (G_OBJECT (app_picker), "file", NULL);
return;
@ -40,6 +33,25 @@ file_opened (GObject *source,
g_object_set_data_full (G_OBJECT (app_picker), "file", g_object_ref (file), g_object_unref);
}
static void
file_opened (GObject *source,
GAsyncResult *result,
void *data)
{
GFile *file;
GError *error = NULL;
file = gtk_file_dialog_open_finish (GTK_FILE_DIALOG (source), result, &error);
if (!file)
{
g_print ("%s\n", error->message);
g_error_free (error);
}
set_file (file, data);
}
static gboolean
abort_mission (gpointer data)
{
@ -130,11 +142,28 @@ launch_uri (GtkButton *picker)
g_object_unref (launcher);
}
static gboolean
on_drop (GtkDropTarget *target,
const GValue *value,
double x,
double y,
gpointer data)
{
if (G_VALUE_HOLDS (value, G_TYPE_FILE))
{
set_file (g_value_get_object (value), data);
return TRUE;
}
return FALSE;
}
GtkWidget *
do_pickers (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
GtkWidget *table, *label, *picker, *button;
GtkDropTarget *drop_target;
if (!window)
{
@ -179,7 +208,13 @@ do_pickers (GtkWidget *do_widget)
picker = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
button = gtk_button_new_from_icon_name ("document-open-symbolic");
label = gtk_label_new ("None");
drop_target = gtk_drop_target_new (G_TYPE_FILE, GDK_ACTION_COPY);
g_signal_connect (drop_target, "drop", G_CALLBACK (on_drop), label);
gtk_widget_add_controller (button, GTK_EVENT_CONTROLLER (drop_target));
gtk_label_set_xalign (GTK_LABEL (label), 0.);
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_MIDDLE);
gtk_widget_set_hexpand (label, TRUE);

View File

@ -22,6 +22,7 @@ show_shortcuts (GtkWidget *window,
gtk_window_set_transient_for (GTK_WINDOW (overlay), GTK_WINDOW (window));
g_object_set (overlay, "view-name", view, NULL);
g_object_unref (builder);
gtk_window_present (GTK_WINDOW (overlay));
}
G_MODULE_EXPORT void

View File

@ -8,7 +8,7 @@ iconbrowser_sources = [
iconbrowser_resources = gnome.compile_resources('iconbrowser_resources',
'iconbrowser.gresource.xml',
source_dir: '.',
source_dir: meson.current_source_dir(),
)
executable('gtk4-icon-browser',

View File

@ -7,7 +7,7 @@ node_editor_sources = [
node_editor_resources = gnome.compile_resources('node_editor_resources',
'node-editor.gresource.xml',
source_dir: '.',
source_dir: meson.current_source_dir(),
)
executable('gtk4-node-editor',

View File

@ -69,7 +69,7 @@ if not meson.is_cross_build() and build_machine.cpu_family() != 'arm' and build_
else
widgetfactory_resources = gnome.compile_resources('widgetfactory_resources',
'widget-factory.gresource.xml',
source_dir: '.',
source_dir: meson.current_source_dir(),
)
endif

View File

@ -66,6 +66,10 @@ You can compile the program above with GCC using:
gcc $( pkg-config --cflags gtk4 ) -o example-0 example-0.c $( pkg-config --libs gtk4 )
```
**Note**: If the above compilation does not work due to an error regarding `G_APPLICATION_DEFAULT_FLAGS`
this could be due to your OS providing an older version of GLib. For GLib versions older than 2.74 you
will need to replace `G_APPLICATION_DEFAULT_FLAGS` with `G_APPLICATION_FLAGS_NONE` in this example, and
others in this documentation.
For more information on how to compile a GTK application, please
refer to the [Compiling GTK Applications](compiling.html)
section in this reference.

View File

@ -1,6 +1,6 @@
app2_resources = gnome.compile_resources('exampleapp2_resources',
'exampleapp.gresource.xml',
source_dir: '.')
source_dir: meson.current_source_dir())
executable('exampleapp2',
'exampleapp.c', 'exampleappwin.c', 'main.c', app2_resources,

View File

@ -1,6 +1,6 @@
app3_resources = gnome.compile_resources('exampleapp3_resources',
'exampleapp.gresource.xml',
source_dir: '.')
source_dir: meson.current_source_dir())
executable('exampleapp3',
'exampleapp.c', 'exampleappwin.c', 'main.c', app3_resources,

View File

@ -1,6 +1,6 @@
app4_resources = gnome.compile_resources('exampleapp4_resources',
'exampleapp.gresource.xml',
source_dir: '.')
source_dir: meson.current_source_dir())
executable('exampleapp4',
'exampleapp.c', 'exampleappwin.c', 'main.c', app4_resources,

View File

@ -1,6 +1,6 @@
app5_resources = gnome.compile_resources('exampleapp5_resources',
'exampleapp.gresource.xml',
source_dir: '.')
source_dir: meson.current_source_dir())
app5_schemas = gnome.compile_schemas()

View File

@ -1,6 +1,6 @@
app6_resources = gnome.compile_resources('exampleapp6_resources',
'exampleapp.gresource.xml',
source_dir: '.')
source_dir: meson.current_source_dir())
app6_schemas = gnome.compile_schemas()

View File

@ -1,6 +1,6 @@
app7_resources = gnome.compile_resources('exampleapp7_resources',
'exampleapp.gresource.xml',
source_dir: '.')
source_dir: meson.current_source_dir())
app7_schemas = gnome.compile_schemas()

View File

@ -1,6 +1,6 @@
app8_resources = gnome.compile_resources('exampleapp8 resources',
'exampleapp.gresource.xml',
source_dir: '.')
source_dir: meson.current_source_dir())
app8_schemas = gnome.compile_schemas()

View File

@ -1,6 +1,6 @@
app9_resources = gnome.compile_resources('exampleapp9_resources',
'exampleapp.gresource.xml',
source_dir: '.')
source_dir: meson.current_source_dir())
app9_schemas = gnome.compile_schemas()

View File

@ -1,5 +1,5 @@
bp_resources = gnome.compile_resources('bloatpad_resources',
'bloatpad.gresources.xml',
source_dir: '.')
source_dir: meson.current_source_dir())
executable('bloatpad', 'bloatpad.c', bp_resources, dependencies: libgtk_dep, c_args: common_cflags)

View File

@ -178,10 +178,10 @@ gdk_memory_texture_new_subtexture (GdkMemoryTexture *source,
GBytes *bytes;
g_return_val_if_fail (GDK_IS_MEMORY_TEXTURE (source), NULL);
g_return_val_if_fail (x >= 0 || x < GDK_TEXTURE (source)->width, NULL);
g_return_val_if_fail (y >= 0 || y < GDK_TEXTURE (source)->height, NULL);
g_return_val_if_fail (width > 0 || x + width <= GDK_TEXTURE (source)->width, NULL);
g_return_val_if_fail (height > 0 || y + height <= GDK_TEXTURE (source)->height, NULL);
g_return_val_if_fail (x >= 0 && x < GDK_TEXTURE (source)->width, NULL);
g_return_val_if_fail (y >= 0 && y < GDK_TEXTURE (source)->height, NULL);
g_return_val_if_fail (width > 0 && x + width <= GDK_TEXTURE (source)->width, NULL);
g_return_val_if_fail (height > 0 && y + height <= GDK_TEXTURE (source)->height, NULL);
texture = GDK_TEXTURE (source);
bpp = gdk_memory_format_bytes_per_pixel (texture->format);

View File

@ -131,7 +131,7 @@ gdk_gresource_xml = configure_file(output: 'gdk.gresource.xml',
gdkresources = gnome.compile_resources('gdkresources',
gdk_gresource_xml,
source_dir: '.',
source_dir: meson.current_source_dir(),
c_name: '_gdk',
extra_args: '--manual-register',
)

View File

@ -561,8 +561,14 @@ gdk_wayland_surface_attach_image (GdkSurface *surface,
/* Attach this new buffer to the surface */
wl_surface_attach (impl->display_server.wl_surface,
_gdk_wayland_shm_surface_get_wl_buffer (cairo_surface),
impl->pending_buffer_offset_x,
impl->pending_buffer_offset_y);
0, 0);
if ((impl->pending_buffer_offset_x || impl->pending_buffer_offset_y) &&
wl_surface_get_version (impl->display_server.wl_surface) >=
WL_SURFACE_OFFSET_SINCE_VERSION)
wl_surface_offset (impl->display_server.wl_surface,
impl->pending_buffer_offset_x,
impl->pending_buffer_offset_y);
impl->pending_buffer_offset_x = 0;
impl->pending_buffer_offset_y = 0;
@ -971,6 +977,15 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface)
impl->display_server.egl_window = NULL;
}
impl->awaiting_frame = FALSE;
if (impl->awaiting_frame_frozen)
{
impl->awaiting_frame_frozen = FALSE;
gdk_surface_thaw_updates (surface);
}
GDK_WAYLAND_SURFACE_GET_CLASS (impl)->hide_surface (impl);
if (impl->display_server.xdg_surface)
{
xdg_surface_destroy (impl->display_server.xdg_surface);
@ -989,15 +1004,6 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface)
impl->initial_configure_received = FALSE;
}
impl->awaiting_frame = FALSE;
if (impl->awaiting_frame_frozen)
{
impl->awaiting_frame_frozen = FALSE;
gdk_surface_thaw_updates (surface);
}
GDK_WAYLAND_SURFACE_GET_CLASS (impl)->hide_surface (impl);
g_clear_pointer (&impl->display_server.wl_surface, wl_surface_destroy);
g_slist_free (impl->display_server.outputs);

View File

@ -872,6 +872,21 @@ gsk_gl_render_job_transform_bounds (GskGLRenderJob *job,
}
}
static inline void
gsk_gl_render_job_untransform_bounds (GskGLRenderJob *job,
const graphene_rect_t *rect,
graphene_rect_t *out_rect)
{
GskTransform *transform;
transform = gsk_transform_invert (gsk_transform_ref (job->current_modelview->transform));
gsk_transform_transform_bounds (transform, rect, out_rect);
out_rect->origin.x -= job->offset_x;
out_rect->origin.y -= job->offset_y;
}
static inline void
gsk_gl_render_job_transform_rounded_rect (GskGLRenderJob *job,
const GskRoundedRect *rect,
@ -3615,127 +3630,98 @@ gsk_gl_render_job_visit_texture_scale_node (GskGLRenderJob *job,
int min_filter = min_filters[scaling_filter];
int mag_filter = mag_filters[scaling_filter];
int max_texture_size = job->command_queue->max_texture_size;
graphene_rect_t clip_rect;
GskGLRenderTarget *render_target;
GskGLRenderOffscreen offscreen = {0};
graphene_rect_t viewport;
graphene_rect_t prev_viewport;
graphene_matrix_t prev_projection;
float prev_alpha;
guint prev_fbo;
guint texture_id;
float u0, u1, v0, v1;
if (scaling_filter == GSK_SCALING_FILTER_LINEAR)
gsk_gl_render_job_untransform_bounds (job, &job->current_clip->rect.bounds, &clip_rect);
if (!graphene_rect_intersection (bounds, &clip_rect, &clip_rect))
return;
if G_UNLIKELY (clip_rect.size.width > max_texture_size ||
clip_rect.size.height > max_texture_size)
{
gsk_gl_render_job_visit_texture (job, texture, bounds);
return;
}
if G_LIKELY (texture->width <= max_texture_size &&
texture->height <= max_texture_size)
viewport = GRAPHENE_RECT_INIT (0, 0,
clip_rect.size.width,
clip_rect.size.height);
if (!gsk_gl_driver_create_render_target (job->driver,
(int) ceilf (clip_rect.size.width),
(int) ceilf (clip_rect.size.height),
get_target_format (job, node),
GL_LINEAR, GL_LINEAR,
&render_target))
{
GskGLRenderTarget *render_target;
GskGLRenderOffscreen offscreen = {0};
graphene_rect_t viewport;
graphene_rect_t prev_viewport;
graphene_matrix_t prev_projection;
float prev_alpha;
guint prev_fbo;
guint texture_id;
viewport = GRAPHENE_RECT_INIT (0, 0,
bounds->size.width,
bounds->size.height);
if (!gsk_gl_driver_create_render_target (job->driver,
(int) ceilf (viewport.size.width),
(int) ceilf (viewport.size.height),
get_target_format (job, node),
GL_LINEAR, GL_LINEAR,
&render_target))
{
/* viewport is too big, slice the texture and try again */
goto slice;
}
gsk_gl_render_job_upload_texture (job, texture, min_filter, mag_filter, &offscreen);
g_assert (offscreen.texture_id);
g_assert (offscreen.was_offscreen == FALSE);
gsk_gl_render_job_set_viewport (job, &viewport, &prev_viewport);
gsk_gl_render_job_set_projection_from_rect (job, &viewport, &prev_projection);
gsk_gl_render_job_set_modelview (job, NULL);
prev_alpha = gsk_gl_render_job_set_alpha (job, 1.0f);
gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport));
prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
gsk_gl_command_queue_clear (job->command_queue, 0, &viewport);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
gsk_gl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
GL_TEXTURE_2D,
GL_TEXTURE0,
offscreen.texture_id);
gsk_gl_render_job_draw_offscreen (job, &viewport, &offscreen);
gsk_gl_render_job_end_draw (job);
gsk_gl_render_job_pop_clip (job);
gsk_gl_render_job_pop_modelview (job);
gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL);
gsk_gl_render_job_set_projection (job, &prev_projection);
gsk_gl_render_job_set_alpha (job, prev_alpha);
gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo);
texture_id = gsk_gl_driver_release_render_target (job->driver, render_target, FALSE);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
gsk_gl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
GL_TEXTURE_2D,
GL_TEXTURE0,
texture_id);
gsk_gl_render_job_draw_offscreen_rect (job, bounds);
gsk_gl_render_job_end_draw (job);
gsk_gl_render_job_visit_texture (job, texture, bounds);
return;
}
else
slice:
{
float min_x = bounds->origin.x;
float min_y = bounds->origin.y;
float max_x = min_x + bounds->size.width;
float max_y = min_y + bounds->size.height;
float scale_x = (max_x - min_x) / texture->width;
float scale_y = (max_y - min_y) / texture->height;
GskGLTextureSlice *slices = NULL;
guint n_slices = 0;
GdkGLContext *context = gsk_gl_driver_get_context (job->driver);
guint rows, cols;
/* Slice enough that neither the original texture nor the scaled texture
* exceed the texture size limit
*/
cols = (int)(MAX (bounds->size.width, texture->width) / (max_texture_size / 4)) + 1;
rows = (int)(MAX (bounds->size.height, texture->height) / (max_texture_size / 4)) + 1;
gsk_gl_render_job_upload_texture (job, texture, min_filter, mag_filter, &offscreen);
gsk_gl_driver_slice_texture (job->driver, texture, GL_NEAREST, GL_NEAREST, cols, rows, &slices, &n_slices);
g_assert (offscreen.texture_id);
g_assert (offscreen.was_offscreen == FALSE);
g_assert (slices != NULL);
g_assert (n_slices > 0);
u0 = (clip_rect.origin.x - bounds->origin.x) / bounds->size.width;
v0 = (clip_rect.origin.y - bounds->origin.y) / bounds->size.height;
u1 = (clip_rect.origin.x + clip_rect.size.width - bounds->origin.x) / bounds->size.width;
v1 = (clip_rect.origin.y + clip_rect.size.height - bounds->origin.y) / bounds->size.height;
for (guint i = 0; i < n_slices; i ++)
{
const GskGLTextureSlice *slice = &slices[i];
float x1, x2, y1, y2;
GdkTexture *sub_texture;
GskRenderNode *sub_node;
gsk_gl_render_job_set_viewport (job, &viewport, &prev_viewport);
gsk_gl_render_job_set_projection_from_rect (job, &viewport, &prev_projection);
gsk_gl_render_job_set_modelview (job, NULL);
prev_alpha = gsk_gl_render_job_set_alpha (job, 1.0f);
gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport));
x1 = min_x + (scale_x * slice->rect.x);
x2 = x1 + (slice->rect.width * scale_x);
y1 = min_y + (scale_y * slice->rect.y);
y2 = y1 + (slice->rect.height * scale_y);
prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
gsk_gl_command_queue_clear (job->command_queue, 0, &viewport);
sub_texture = gdk_gl_texture_new (context, slice->texture_id, slice->rect.width, slice->rect.height, NULL, NULL);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
gsk_gl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
GL_TEXTURE_2D,
GL_TEXTURE0,
offscreen.texture_id);
gsk_gl_render_job_draw_coords (job,
0, 0, clip_rect.size.width, clip_rect.size.height,
u0, v0, u1, v1,
(guint16[]) { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO });
gsk_gl_render_job_end_draw (job);
sub_node = gsk_texture_scale_node_new (sub_texture, &GRAPHENE_RECT_INIT (x1, y1, x2 - x1, y2 - y1), scaling_filter);
gsk_gl_render_job_pop_clip (job);
gsk_gl_render_job_pop_modelview (job);
gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL);
gsk_gl_render_job_set_projection (job, &prev_projection);
gsk_gl_render_job_set_alpha (job, prev_alpha);
gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo);
gsk_gl_render_job_visit_node (job, sub_node);
gsk_render_node_unref (sub_node);
g_object_unref (sub_texture);
}
}
texture_id = gsk_gl_driver_release_render_target (job->driver, render_target, FALSE);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
gsk_gl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
GL_TEXTURE_2D,
GL_TEXTURE0,
texture_id);
gsk_gl_render_job_draw_coords (job,
job->offset_x + clip_rect.origin.x,
job->offset_y + clip_rect.origin.y,
job->offset_x + clip_rect.origin.x + clip_rect.size.width,
job->offset_y + clip_rect.origin.y + clip_rect.size.height,
0, clip_rect.size.width / ceilf (clip_rect.size.width),
clip_rect.size.height / ceilf (clip_rect.size.height), 0,
(guint16[]){ FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO } );
gsk_gl_render_job_end_draw (job);
}
static inline void

View File

@ -58,6 +58,16 @@ rectangle_init_from_graphene (cairo_rectangle_int_t *cairo,
cairo->height = ceilf (graphene->origin.y + graphene->size.height) - cairo->y;
}
static void
_graphene_rect_init_from_clip_extents (graphene_rect_t *rect,
cairo_t *cr)
{
double x1c, y1c, x2c, y2c;
cairo_clip_extents (cr, &x1c, &y1c, &x2c, &y2c);
graphene_rect_init (rect, x1c, y1c, x2c - x1c, y2c - y1c);
}
/* {{{ GSK_COLOR_NODE */
/**
@ -1625,15 +1635,21 @@ gsk_texture_scale_node_draw (GskRenderNode *node,
};
cairo_t *cr2;
cairo_surface_t *surface2;
graphene_rect_t clip_rect;
/* Make sure we draw the minimum region by using the clip */
gsk_cairo_rectangle (cr, &node->bounds);
cairo_clip (cr);
_graphene_rect_init_from_clip_extents (&clip_rect, cr);
if (clip_rect.size.width <= 0 || clip_rect.size.height <= 0)
return;
surface2 = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
(int) ceilf (node->bounds.size.width),
(int) ceilf (node->bounds.size.height));
(int) ceilf (clip_rect.size.width),
(int) ceilf (clip_rect.size.height));
cairo_surface_set_device_offset (surface2, -clip_rect.origin.x, -clip_rect.origin.y);
cr2 = cairo_create (surface2);
cairo_set_source_rgba (cr2, 0, 0, 0, 0);
cairo_paint (cr2);
surface = gdk_texture_download_surface (self->texture);
pattern = cairo_pattern_create_for_surface (surface);
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
@ -2072,15 +2088,15 @@ gsk_inset_shadow_node_draw (GskRenderNode *node,
GskInsetShadowNode *self = (GskInsetShadowNode *) node;
GskRoundedRect box, clip_box;
int clip_radius;
double x1c, y1c, x2c, y2c;
graphene_rect_t clip_rect;
double blur_radius;
/* We don't need to draw invisible shadows */
if (gdk_rgba_is_clear (&self->color))
return;
cairo_clip_extents (cr, &x1c, &y1c, &x2c, &y2c);
if (!gsk_rounded_rect_intersects_rect (&self->outline, &GRAPHENE_RECT_INIT (x1c, y1c, x2c - x1c, y2c - y1c)))
_graphene_rect_init_from_clip_extents (&clip_rect, cr);
if (!gsk_rounded_rect_intersects_rect (&self->outline, &clip_rect))
return;
blur_radius = self->blur_radius / 2;
@ -2368,7 +2384,7 @@ gsk_outset_shadow_node_draw (GskRenderNode *node,
GskOutsetShadowNode *self = (GskOutsetShadowNode *) node;
GskRoundedRect box, clip_box;
int clip_radius;
double x1c, y1c, x2c, y2c;
graphene_rect_t clip_rect;
float top, right, bottom, left;
double blur_radius;
@ -2376,8 +2392,8 @@ gsk_outset_shadow_node_draw (GskRenderNode *node,
if (gdk_rgba_is_clear (&self->color))
return;
cairo_clip_extents (cr, &x1c, &y1c, &x2c, &y2c);
if (gsk_rounded_rect_contains_rect (&self->outline, &GRAPHENE_RECT_INIT (x1c, y1c, x2c - x1c, y2c - y1c)))
_graphene_rect_init_from_clip_extents (&clip_rect, cr);
if (!gsk_rounded_rect_intersects_rect (&self->outline, &clip_rect))
return;
blur_radius = self->blur_radius / 2;

View File

@ -164,7 +164,7 @@ gskenum_h = gsk_enums[1]
gskresources = gnome.compile_resources('gskresources',
gsk_resources_xml,
dependencies: gsk_private_vulkan_compiled_shaders_deps,
source_dir: '.',
source_dir: meson.current_source_dir(),
c_name: '_gsk',
extra_args: [ '--manual-register', ],
)

View File

@ -158,9 +158,12 @@ component_handle_method (GDBusConnection *connection,
}
else
{
GtkAtSpiContext *ctx = GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (child)));
GtkATContext *context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (child));
GtkAtSpiContext *ctx = GTK_AT_SPI_CONTEXT (context);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@(so))", gtk_at_spi_context_to_ref (ctx)));
g_object_unref (context);
}
}
else if (g_strcmp0 (method_name, "GetExtents") == 0)

View File

@ -203,6 +203,7 @@ collect_states (GtkAtSpiContext *self,
if (gtk_at_context_has_accessible_state (ctx, GTK_ACCESSIBLE_STATE_CHECKED))
{
set_atspi_state (&states, ATSPI_STATE_CHECKABLE);
value = gtk_at_context_get_accessible_state (ctx, GTK_ACCESSIBLE_STATE_CHECKED);
switch (gtk_tristate_accessible_value_get (value))
{
@ -246,7 +247,7 @@ collect_states (GtkAtSpiContext *self,
case GTK_ACCESSIBLE_INVALID_TRUE:
case GTK_ACCESSIBLE_INVALID_GRAMMAR:
case GTK_ACCESSIBLE_INVALID_SPELLING:
set_atspi_state (&states, ATSPI_STATE_INVALID);
set_atspi_state (&states, ATSPI_STATE_INVALID_ENTRY);
break;
case GTK_ACCESSIBLE_INVALID_FALSE:
default:
@ -282,6 +283,34 @@ collect_states (GtkAtSpiContext *self,
}
}
if (gtk_at_context_has_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_REQUIRED))
{
value = gtk_at_context_get_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_REQUIRED);
if (gtk_boolean_accessible_value_get (value))
set_atspi_state (&states, ATSPI_STATE_REQUIRED);
}
if (gtk_at_context_has_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_MULTI_SELECTABLE))
{
value = gtk_at_context_get_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_MULTI_SELECTABLE);
if (gtk_boolean_accessible_value_get (value))
set_atspi_state (&states, ATSPI_STATE_MULTISELECTABLE);
}
if (gtk_at_context_has_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_HAS_POPUP))
{
value = gtk_at_context_get_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_HAS_POPUP);
if (gtk_boolean_accessible_value_get (value))
set_atspi_state (&states, ATSPI_STATE_HAS_POPUP);
}
if (gtk_at_context_has_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_AUTOCOMPLETE))
{
value = gtk_at_context_get_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_AUTOCOMPLETE);
if (gtk_autocomplete_accessible_value_get (value) != GTK_ACCESSIBLE_AUTOCOMPLETE_NONE)
set_atspi_state (&states, ATSPI_STATE_SUPPORTS_AUTOCOMPLETION);
}
g_variant_builder_add (builder, "u", (guint32) (states & 0xffffffff));
g_variant_builder_add (builder, "u", (guint32) (states >> 32));
}
@ -299,11 +328,12 @@ collect_relations (GtkAtSpiContext *self,
{ GTK_ACCESSIBLE_RELATION_LABELLED_BY, ATSPI_RELATION_LABELLED_BY },
{ GTK_ACCESSIBLE_RELATION_CONTROLS, ATSPI_RELATION_CONTROLLER_FOR },
{ GTK_ACCESSIBLE_RELATION_DESCRIBED_BY, ATSPI_RELATION_DESCRIBED_BY },
{ GTK_ACCESSIBLE_RELATION_DETAILS, ATSPI_RELATION_DETAILS },
{ GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE, ATSPI_RELATION_ERROR_MESSAGE},
{ GTK_ACCESSIBLE_RELATION_FLOW_TO, ATSPI_RELATION_FLOWS_TO},
};
GtkAccessibleValue *value;
GList *list, *l;
GtkATContext *target_ctx;
int i;
for (i = 0; i < G_N_ELEMENTS (map); i++)
@ -318,13 +348,16 @@ collect_relations (GtkAtSpiContext *self,
for (l = list; l; l = l->next)
{
target_ctx = gtk_accessible_get_at_context (GTK_ACCESSIBLE (l->data));
GtkATContext *target_ctx =
gtk_accessible_get_at_context (GTK_ACCESSIBLE (l->data));
/* Realize the ATContext of the target, so we can ask for its ref */
gtk_at_context_realize (target_ctx);
g_variant_builder_add (&b, "@(so)",
gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (target_ctx)));
g_object_unref (target_ctx);
}
g_variant_builder_add (builder, "(ua(so))", map[i].s, &b);
@ -336,17 +369,17 @@ static int
get_index_in (GtkAccessible *parent,
GtkAccessible *child)
{
GtkAccessible *candidate;
guint res;
if (parent == NULL)
return -1;
res = 0;
guint res = 0;
GtkAccessible *candidate;
for (candidate = gtk_accessible_get_first_accessible_child (parent);
candidate != NULL;
candidate = gtk_accessible_get_next_accessible_sibling (candidate))
{
g_object_unref (candidate);
if (candidate == child)
return res;
@ -365,7 +398,13 @@ get_index_in_parent (GtkAccessible *accessible)
GtkAccessible *parent = gtk_accessible_get_accessible_parent (accessible);
if (parent != NULL)
return get_index_in (parent, accessible);
{
int res = get_index_in (parent, accessible);
g_object_unref (parent);
return res;
}
return -1;
}
@ -401,7 +440,6 @@ static GVariant *
get_parent_context_ref (GtkAccessible *accessible)
{
GVariant *res = NULL;
GtkAccessible *parent = gtk_accessible_get_accessible_parent (accessible);
if (parent == NULL)
@ -410,13 +448,19 @@ get_parent_context_ref (GtkAccessible *accessible)
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (context);
res = gtk_at_spi_root_to_ref (self->root);
g_object_unref (context);
}
else
{
GtkATContext *parent_context = gtk_accessible_get_at_context (parent);
gtk_at_context_realize (parent_context);
res = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (parent_context));
g_object_unref (parent_context);
g_object_unref (parent);
}
if (res == NULL)
@ -497,31 +541,32 @@ handle_accessible_method (GDBusConnection *connection,
{
GtkATContext *context = NULL;
GtkAccessible *accessible;
GtkAccessible *child = NULL;
int idx, presentable_idx;
g_variant_get (parameters, "(i)", &idx);
accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
GtkAccessible *child;
presentable_idx = 0;
for (child = gtk_accessible_get_first_accessible_child (accessible);
child != NULL;
child = gtk_accessible_get_next_accessible_sibling (child))
{
g_object_unref (child);
if (!gtk_accessible_should_present (child))
continue;
continue;
if (presentable_idx == idx)
break;
presentable_idx++;
presentable_idx += 1;
}
if (child)
{
context = gtk_accessible_get_at_context (child);
}
if (child != NULL)
context = gtk_accessible_get_at_context (child);
if (context == NULL)
{
@ -536,20 +581,23 @@ handle_accessible_method (GDBusConnection *connection,
gtk_at_context_realize (context);
GVariant *ref = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (context));
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@(so))", ref));
g_object_unref (context);
}
else if (g_strcmp0 (method_name, "GetChildren") == 0)
{
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(so)"));
GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
GtkAccessible *child = NULL;
GtkAccessible *child;
for (child = gtk_accessible_get_first_accessible_child (accessible);
child != NULL;
child = gtk_accessible_get_next_accessible_sibling (child))
{
{
g_object_unref (child);
if (!gtk_accessible_should_present (child))
continue;
@ -562,6 +610,8 @@ handle_accessible_method (GDBusConnection *connection,
if (ref != NULL)
g_variant_builder_add (&builder, "@(so)", ref);
g_object_unref (context);
}
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a(so))", &builder));
@ -741,8 +791,13 @@ emit_property_changed (GtkAtSpiContext *self,
const char *name,
GVariant *value)
{
GVariant *value_owned = g_variant_ref_sink (value);
if (self->connection == NULL)
return;
{
g_variant_unref (value_owned);
return;
}
g_dbus_connection_emit_signal (self->connection,
NULL,
@ -750,8 +805,9 @@ emit_property_changed (GtkAtSpiContext *self,
"org.a11y.atspi.Event.Object",
"PropertyChange",
g_variant_new ("(siiva{sv})",
name, 0, 0, value, NULL),
name, 0, 0, value_owned, NULL),
NULL);
g_variant_unref (value_owned);
}
static void
@ -850,8 +906,6 @@ gtk_at_spi_context_state_change (GtkATContext *ctx,
if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_HIDDEN)
{
GtkAccessible *parent;
GtkATContext *context;
GtkAccessibleChildChange change;
value = gtk_accessible_attribute_set_get_value (states, GTK_ACCESSIBLE_STATE_HIDDEN);
@ -867,10 +921,15 @@ gtk_at_spi_context_state_change (GtkATContext *ctx,
}
else
{
parent = gtk_accessible_get_accessible_parent (accessible);
GtkAccessible *parent =
gtk_accessible_get_accessible_parent (accessible);
GtkATContext *context =
gtk_accessible_get_at_context (parent);
context = gtk_accessible_get_at_context (parent);
gtk_at_context_child_changed (context, change, accessible);
g_object_unref (context);
g_object_unref (parent);
}
}
@ -1119,9 +1178,18 @@ gtk_at_spi_context_child_change (GtkATContext *ctx,
int idx = 0;
if (parent == NULL)
idx = -1;
{
idx = -1;
}
else if (parent == accessible)
idx = get_index_in (accessible, child);
{
idx = get_index_in (accessible, child);
g_object_unref (parent);
}
else
{
g_object_unref (parent);
}
if (change & GTK_ACCESSIBLE_CHILD_CHANGE_ADDED)
emit_children_changed (self,
@ -1133,6 +1201,8 @@ gtk_at_spi_context_child_change (GtkATContext *ctx,
GTK_AT_SPI_CONTEXT (child_context),
idx,
GTK_ACCESSIBLE_CHILD_STATE_REMOVED);
g_object_unref (child_context);
}
/* }}} */
/* {{{ D-Bus Registration */
@ -1701,15 +1771,16 @@ gtk_at_spi_context_get_child_count (GtkAtSpiContext *self)
int n_children = 0;
GtkAccessible *child = NULL;
for (child = gtk_accessible_get_first_accessible_child (accessible);
child != NULL;
child = gtk_accessible_get_next_accessible_sibling (child))
{
g_object_unref (child);
if (!gtk_accessible_should_present (child))
continue;
n_children++;
n_children += 1;
}
return n_children;

View File

@ -225,6 +225,10 @@ typedef enum {
ATSPI_RELATION_PARENT_WINDOW_OF,
ATSPI_RELATION_DESCRIPTION_FOR,
ATSPI_RELATION_DESCRIBED_BY,
ATSPI_RELATION_DETAILS,
ATSPI_RELATION_DETAILS_FOR,
ATSPI_RELATION_ERROR_MESSAGE,
ATSPI_RELATION_ERROR_FOR,
ATSPI_RELATION_LAST_DEFINED,
} AtspiRelationType;

View File

@ -314,6 +314,8 @@ handle_accessible_method (GDBusConnection *connection,
const char *path = gtk_at_spi_context_get_context_path (GTK_AT_SPI_CONTEXT (context));
g_dbus_method_invocation_return_value (invocation, g_variant_new ("((so))", name, path));
g_object_unref (context);
}
else if (g_strcmp0 (method_name, "GetChildren") == 0)
{
@ -334,6 +336,8 @@ handle_accessible_method (GDBusConnection *connection,
const char *path = gtk_at_spi_context_get_context_path (GTK_AT_SPI_CONTEXT (context));
g_variant_builder_add (&builder, "(so)", name, path);
g_object_unref (context);
}
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a(so))", &builder));
@ -453,6 +457,8 @@ gtk_at_spi_root_child_changed (GtkAtSpiRoot *self,
GtkATContext *context = gtk_accessible_get_at_context (child);
window_ref = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (context));
g_object_unref (context);
}
switch (change)

View File

@ -94,7 +94,9 @@ listbox_handle_method (GDBusConnection *connection,
else
{
GtkATContext *ctx = gtk_accessible_get_at_context (GTK_ACCESSIBLE (counter.child));
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@(so))", gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (ctx))));
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(@(so))", gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (ctx))));
g_object_unref (ctx);
}
}
else if (g_strcmp0 (method_name, "SelectChild") == 0)
@ -271,7 +273,8 @@ listview_handle_method (GDBusConnection *connection,
{
GtkATContext *ctx = gtk_accessible_get_at_context (GTK_ACCESSIBLE (child));
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(@(so))", gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (ctx))));
g_variant_new ("(@(so))", gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (ctx))));
g_object_unref (ctx);
}
}
else if (g_strcmp0 (method_name, "SelectChild") == 0)
@ -495,7 +498,9 @@ flowbox_handle_method (GDBusConnection *connection,
else
{
GtkATContext *ctx = gtk_accessible_get_at_context (GTK_ACCESSIBLE (counter.child));
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@(so))", gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (ctx))));
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(@(so))", gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (ctx))));
g_object_unref (ctx);
}
}
else if (g_strcmp0 (method_name, "SelectChild") == 0)
@ -761,7 +766,8 @@ stackswitcher_handle_method (GDBusConnection *connection,
{
GtkATContext *ctx = gtk_accessible_get_at_context (GTK_ACCESSIBLE (child));
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(@(so))", gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (ctx))));
g_variant_new ("(@(so))", gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (ctx))));
g_object_unref (ctx);
}
}
else if (g_strcmp0 (method_name, "SelectChild") == 0)
@ -891,7 +897,8 @@ notebook_handle_method (GDBusConnection *connection,
{
GtkATContext *ctx = gtk_accessible_get_at_context (GTK_ACCESSIBLE (child));
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(@(so))", gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (ctx))));
g_variant_new ("(@(so))", gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (ctx))));
g_object_unref (ctx);
}
}
else if (g_strcmp0 (method_name, "SelectChild") == 0)

View File

@ -109,7 +109,7 @@ gtk_accessible_role_to_atspi_role (GtkAccessibleRole role)
return ATSPI_ROLE_LABEL;
case GTK_ACCESSIBLE_ROLE_LANDMARK:
break;
return ATSPI_ROLE_LANDMARK;
case GTK_ACCESSIBLE_ROLE_LEGEND:
return ATSPI_ROLE_LABEL;
@ -169,7 +169,7 @@ gtk_accessible_role_to_atspi_role (GtkAccessibleRole role)
return ATSPI_ROLE_OPTION_PANE;
case GTK_ACCESSIBLE_ROLE_PRESENTATION:
return ATSPI_ROLE_SECTION;
return ATSPI_ROLE_FILLER;
case GTK_ACCESSIBLE_ROLE_PROGRESS_BAR:
return ATSPI_ROLE_PROGRESS_BAR;
@ -205,7 +205,7 @@ gtk_accessible_role_to_atspi_role (GtkAccessibleRole role)
return ATSPI_ROLE_ENTRY;
case GTK_ACCESSIBLE_ROLE_SECTION:
return ATSPI_ROLE_FILLER;
return ATSPI_ROLE_SECTION;
case GTK_ACCESSIBLE_ROLE_SECTION_HEAD:
return ATSPI_ROLE_FILLER;
@ -273,6 +273,8 @@ gtk_accessible_role_to_atspi_role (GtkAccessibleRole role)
case GTK_ACCESSIBLE_ROLE_WINDOW:
return ATSPI_ROLE_FRAME;
case GTK_ACCESSIBLE_ROLE_TOGGLE_BUTTON:
return ATSPI_ROLE_TOGGLE_BUTTON;
default:
break;
}

View File

@ -995,10 +995,11 @@ gtk_cell_area_box_focus_changed (GtkCellArea *area,
GParamSpec *pspec,
GtkCellAreaBox *box)
{
GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box);
GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (box);
GtkCellRenderer *focus_cell = gtk_cell_area_get_focus_cell (area);
if (gtk_cell_area_get_focus_cell (area))
priv->last_focus_cell = gtk_cell_area_get_focus_cell (area);
if (focus_cell)
priv->last_focus_cell = focus_cell;
}
/*************************************************************

View File

@ -366,7 +366,6 @@ gtk_combo_box_size_allocate (GtkWidget *widget,
{
GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box);
int menu_width;
gtk_widget_size_allocate (priv->box,
&(GtkAllocation) {
@ -374,17 +373,8 @@ gtk_combo_box_size_allocate (GtkWidget *widget,
width, height
}, baseline);
gtk_widget_set_size_request (priv->popup_widget, -1, -1);
if (priv->popup_fixed_width)
gtk_widget_measure (priv->popup_widget, GTK_ORIENTATION_HORIZONTAL, -1,
&menu_width, NULL, NULL, NULL);
else
gtk_widget_measure (priv->popup_widget, GTK_ORIENTATION_HORIZONTAL, -1,
NULL, &menu_width, NULL, NULL);
gtk_widget_set_size_request (priv->popup_widget,
MAX (width, menu_width), -1);
gtk_widget_set_size_request (priv->popup_widget, width, -1);
gtk_widget_queue_resize (priv->popup_widget);
gtk_popover_present (GTK_POPOVER (priv->popup_widget));
}

View File

@ -71,7 +71,7 @@ for f in funcs:
file_output += ['#ifdef GDK_WINDOWING_WIN32']
file_output += ['*tp++ = {0}();'.format(f)]
file_output += ['#endif']
elif f.startswith('gdk_quartz'):
elif f.startswith('gdk_macos'):
file_output += ['#ifdef GDK_WINDOWING_MACOS']
file_output += ['*tp++ = {0}();'.format(f)]
file_output += ['#endif']

View File

@ -91,9 +91,9 @@ gtk_accessible_default_init (GtkAccessibleInterface *iface)
* gtk_accessible_get_at_context:
* @self: a `GtkAccessible`
*
* Retrieves the `GtkATContext` for the given `GtkAccessible`.
* Retrieves the accessible implementation for the given `GtkAccessible`.
*
* Returns: (transfer none): the `GtkATContext`
* Returns: (transfer full): the accessible implementation object
*
* Since: 4.10
*/
@ -109,11 +109,11 @@ gtk_accessible_get_at_context (GtkAccessible *self)
* gtk_accessible_get_accessible_parent:
* @self: a `GtkAccessible`
*
* Retrieves the accessible accessible for an accessible object
* Retrieves the accessible parent for an accessible object.
*
* This function returns `NULL` for top level widgets
* This function returns `NULL` for top level widgets.
*
* Returns: (transfer none) (nullable): the accessible parent
* Returns: (transfer full) (nullable): the accessible parent
*
* Since: 4.10
*/
@ -127,10 +127,13 @@ gtk_accessible_get_accessible_parent (GtkAccessible *self)
context = gtk_accessible_get_at_context (self);
if (context != NULL)
parent = gtk_at_context_get_accessible_parent (context);
{
parent = gtk_at_context_get_accessible_parent (context);
g_object_unref (context);
}
if (parent != NULL)
return parent;
return g_object_ref (parent);
else
return GTK_ACCESSIBLE_GET_IFACE (self)->get_accessible_parent (self);
}
@ -161,6 +164,7 @@ gtk_accessible_set_accessible_parent (GtkAccessible *self,
g_return_if_fail (GTK_IS_ACCESSIBLE (self));
g_return_if_fail (parent == NULL || GTK_IS_ACCESSIBLE (parent));
g_return_if_fail (next_sibling == NULL || GTK_IS_ACCESSIBLE (parent));
GtkATContext *context;
context = gtk_accessible_get_at_context (self);
@ -168,6 +172,7 @@ gtk_accessible_set_accessible_parent (GtkAccessible *self,
{
gtk_at_context_set_accessible_parent (context, parent);
gtk_at_context_set_next_accessible_sibling (context, next_sibling);
g_object_unref (context);
}
}
@ -177,6 +182,7 @@ gtk_accessible_set_accessible_parent (GtkAccessible *self,
* @new_sibling: (nullable): the new next accessible sibling to set
*
* Updates the next accessible sibling of @self.
*
* That might be useful when a new child of a custom `GtkAccessible`
* is created, and it needs to be linked to a previous child.
*
@ -187,20 +193,26 @@ gtk_accessible_update_next_accessible_sibling (GtkAccessible *self,
GtkAccessible *new_sibling)
{
GtkATContext *context;
GtkAccessible *parent;
g_return_if_fail (GTK_IS_ACCESSIBLE (self));
context = gtk_accessible_get_at_context (self);
if (!context)
if (context == NULL)
return;
if (gtk_at_context_get_accessible_parent (context) == NULL)
{
g_critical ("Failed to update next accessible sibling: no parent accessible set for this accessible");
return;
}
parent = gtk_at_context_get_accessible_parent (context);
if (parent == NULL)
{
g_object_unref (context);
g_critical ("Failed to update next accessible sibling: no parent accessible set for this accessible");
return;
}
gtk_at_context_set_next_accessible_sibling (context, new_sibling);
g_object_unref (parent);
g_object_unref (context);
}
/**
@ -209,7 +221,7 @@ gtk_accessible_update_next_accessible_sibling (GtkAccessible *self,
*
* Retrieves the first accessible child of an accessible object.
*
* Returns: (transfer none) (nullable): the first accessible child
* Returns: (transfer full) (nullable): the first accessible child
*
* since: 4.10
*/
@ -227,7 +239,7 @@ gtk_accessible_get_first_accessible_child (GtkAccessible *self)
*
* Retrieves the next accessible sibling of an accessible object
*
* Returns: (transfer none) (nullable): the next accessible sibling
* Returns: (transfer full) (nullable): the next accessible sibling
*
* since: 4.10
*/
@ -239,8 +251,21 @@ gtk_accessible_get_next_accessible_sibling (GtkAccessible *self)
GtkATContext *context;
context = gtk_accessible_get_at_context (self);
if (context != NULL && gtk_at_context_get_accessible_parent (context) != NULL)
return gtk_at_context_get_next_accessible_sibling (context);
if (context != NULL)
{
GtkAccessible *sibling = NULL;
if (gtk_at_context_get_accessible_parent (context) != NULL)
{
sibling = gtk_at_context_get_next_accessible_sibling (context);
if (sibling != NULL)
sibling = g_object_ref (sibling);
}
g_object_unref (context);
return sibling;
}
else
return GTK_ACCESSIBLE_GET_IFACE (self)->get_next_accessible_sibling (self);
}
@ -256,13 +281,21 @@ gtk_accessible_get_next_accessible_sibling (GtkAccessible *self)
GtkAccessibleRole
gtk_accessible_get_accessible_role (GtkAccessible *self)
{
GtkAccessibleRole role;
GtkAccessibleRole role = GTK_ACCESSIBLE_ROLE_NONE;
g_return_val_if_fail (GTK_IS_ACCESSIBLE (self), GTK_ACCESSIBLE_ROLE_NONE);
GtkATContext *context = gtk_accessible_get_at_context (self);
if (context != NULL && gtk_at_context_is_realized (context))
return gtk_at_context_get_accessible_role (context);
if (context != NULL)
{
if (gtk_at_context_is_realized (context))
role = gtk_at_context_get_accessible_role (context);
g_object_unref (context);
if (role != GTK_ACCESSIBLE_ROLE_NONE)
return role;
}
g_object_get (G_OBJECT (self), "accessible-role", &role, NULL);
@ -336,6 +369,8 @@ gtk_accessible_update_state (GtkAccessible *self,
out:
va_end (args);
g_object_unref (context);
}
/**
@ -389,6 +424,7 @@ gtk_accessible_update_state_value (GtkAccessible *self,
}
gtk_at_context_update (context);
g_object_unref (context);
}
/**
@ -412,6 +448,7 @@ gtk_accessible_reset_state (GtkAccessible *self,
gtk_at_context_set_accessible_state (context, state, NULL);
gtk_at_context_update (context);
g_object_unref (context);
}
/**
@ -483,6 +520,8 @@ gtk_accessible_update_property (GtkAccessible *self,
out:
va_end (args);
g_object_unref (context);
}
/**
@ -536,6 +575,7 @@ gtk_accessible_update_property_value (GtkAccessible *self,
}
gtk_at_context_update (context);
g_object_unref (context);
}
/**
@ -559,6 +599,7 @@ gtk_accessible_reset_property (GtkAccessible *self,
gtk_at_context_set_accessible_property (context, property, NULL);
gtk_at_context_update (context);
g_object_unref (context);
}
/**
@ -630,6 +671,8 @@ gtk_accessible_update_relation (GtkAccessible *self,
out:
va_end (args);
g_object_unref (context);
}
/**
@ -658,6 +701,8 @@ gtk_accessible_update_relation_value (GtkAccessible *self,
g_return_if_fail (n_relations > 0);
context = gtk_accessible_get_at_context (self);
if (context == NULL)
return;
for (int i = 0; i < n_relations; i++)
{
@ -676,15 +721,14 @@ gtk_accessible_update_relation_value (GtkAccessible *self,
break;
}
if (context)
gtk_at_context_set_accessible_relation (context, relation, real_value);
gtk_at_context_set_accessible_relation (context, relation, real_value);
if (real_value != NULL)
gtk_accessible_value_unref (real_value);
}
if (context)
gtk_at_context_update (context);
gtk_at_context_update (context);
g_object_unref (context);
}
/**
@ -708,6 +752,7 @@ gtk_accessible_reset_relation (GtkAccessible *self,
gtk_at_context_set_accessible_relation (context, relation, NULL);
gtk_at_context_update (context);
g_object_unref (context);
}
static const char *role_names[] = {
@ -868,13 +913,22 @@ gtk_accessible_platform_changed (GtkAccessible *self,
/* propagate changes up from ignored widgets */
if (gtk_accessible_get_accessible_role (self) == GTK_ACCESSIBLE_ROLE_NONE)
context = gtk_accessible_get_at_context (gtk_accessible_get_accessible_parent (self));
{
GtkAccessible *parent = gtk_accessible_get_accessible_parent (self);
if (parent != NULL)
{
context = gtk_accessible_get_at_context (parent);
g_object_unref (parent);
}
}
if (context == NULL)
return;
gtk_at_context_platform_changed (context, change);
gtk_at_context_update (context);
g_object_unref (context);
}
/**
@ -928,6 +982,7 @@ gtk_accessible_bounds_changed (GtkAccessible *self)
return;
gtk_at_context_bounds_changed (context);
g_object_unref (context);
}
/**
@ -980,6 +1035,7 @@ gtk_accessible_should_present (GtkAccessible *self)
{
GtkAccessibleRole role;
GtkATContext *context;
gboolean res = TRUE;
if (GTK_IS_WIDGET (self) &&
!gtk_widget_get_visible (GTK_WIDGET (self)))
@ -1000,10 +1056,12 @@ gtk_accessible_should_present (GtkAccessible *self)
value = gtk_at_context_get_accessible_state (context, GTK_ACCESSIBLE_STATE_HIDDEN);
if (gtk_boolean_accessible_value_get (value))
return FALSE;
res = FALSE;
}
return TRUE;
g_object_unref (context);
return res;
}
void
@ -1017,15 +1075,24 @@ gtk_accessible_update_children (GtkAccessible *self,
gtk_widget_get_root (GTK_WIDGET (self)) == NULL)
return;
context = gtk_accessible_get_at_context (self);
/* propagate changes up from ignored widgets */
if (gtk_accessible_get_accessible_role (self) == GTK_ACCESSIBLE_ROLE_NONE)
context = gtk_accessible_get_at_context (gtk_accessible_get_accessible_parent (self));
{
GtkAccessible *parent = gtk_accessible_get_accessible_parent (self);
context = gtk_accessible_get_at_context (parent);
g_object_unref (parent);
}
else
{
context = gtk_accessible_get_at_context (self);
}
if (context == NULL)
return;
gtk_at_context_child_changed (context, 1 << state, child);
gtk_at_context_update (context);
g_object_unref (context);
}

View File

@ -73,7 +73,7 @@ struct _GtkAccessibleInterface
* Retrieves the platform-specific accessibility context for the
* accessible implementation.
*
* Returns: (transfer none) (nullable): the accessibility context
* Returns: (transfer full) (nullable): the accessibility context
*
* Since: 4.10
*/
@ -101,7 +101,7 @@ struct _GtkAccessibleInterface
*
* This virtual function should return `NULL` for top level objects.
*
* Returns: (nullable) (transfer none): the accessible parent
* Returns: (nullable) (transfer full): the accessible parent
*
* Since: 4.10
*/
@ -113,7 +113,7 @@ struct _GtkAccessibleInterface
*
* Retrieves the first accessible child of an accessible object.
*
* Returns: (transfer none) (nullable): an accessible object
* Returns: (transfer full) (nullable): an accessible object
*
* Since: 4.10
*/
@ -125,7 +125,7 @@ struct _GtkAccessibleInterface
*
* Retrieves the next accessible sibling of an accessible object.
*
* Returns: (transfer none) (nullable): an accessible object
* Returns: (transfer full) (nullable): an accessible object
*
* Since: 4.10
*/

View File

@ -85,6 +85,20 @@ gtk_at_context_dispose (GObject *gobject)
gtk_at_context_unrealize (self);
if (self->accessible_parent != NULL)
{
g_object_remove_weak_pointer (G_OBJECT (self->accessible_parent),
(gpointer *) &self->accessible_parent);
self->accessible_parent = NULL;
}
if (self->next_accessible_sibling != NULL)
{
g_object_remove_weak_pointer (G_OBJECT (self->next_accessible_sibling),
(gpointer *) &self->next_accessible_sibling);
self->next_accessible_sibling = NULL;
}
G_OBJECT_CLASS (gtk_at_context_parent_class)->dispose (gobject);
}
@ -459,14 +473,14 @@ GtkAccessible *
gtk_at_context_get_accessible_parent (GtkATContext *self)
{
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
return self->accessible_parent;
}
/*< private >
* gtk_at_context_set_accessible_parent:
* @self: a `GtkAtContext`
* @parent: the parent `GtkAccessible` to set
* @parent: (nullable): the parent `GtkAccessible` to set
*
* Sets the parent accessible object of the given `GtkAtContext`.
*/
@ -475,8 +489,18 @@ gtk_at_context_set_accessible_parent (GtkATContext *self,
GtkAccessible *parent)
{
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
g_set_object (&self->accessible_parent, parent);
if (self->accessible_parent != parent)
{
if (self->accessible_parent != NULL)
g_object_remove_weak_pointer (G_OBJECT (self->accessible_parent),
(gpointer *) &self->accessible_parent);
self->accessible_parent = parent;
if (self->accessible_parent != NULL)
g_object_add_weak_pointer (G_OBJECT (self->accessible_parent),
(gpointer *) &self->accessible_parent);
}
}
/*< private >
@ -491,7 +515,7 @@ GtkAccessible *
gtk_at_context_get_next_accessible_sibling (GtkATContext *self)
{
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
return self->next_accessible_sibling;
}
@ -507,8 +531,19 @@ gtk_at_context_set_next_accessible_sibling (GtkATContext *self,
GtkAccessible *sibling)
{
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
g_set_object (&self->next_accessible_sibling, sibling);
if (self->next_accessible_sibling != sibling)
{
if (self->next_accessible_sibling != NULL)
g_object_remove_weak_pointer (G_OBJECT (self->next_accessible_sibling),
(gpointer *) &self->next_accessible_sibling);
self->next_accessible_sibling = sibling;
if (self->next_accessible_sibling != NULL)
g_object_add_weak_pointer (G_OBJECT (self->next_accessible_sibling),
(gpointer *) &self->next_accessible_sibling);
}
}
/*< private >
@ -993,6 +1028,8 @@ gtk_at_context_get_name_accumulate (GtkATContext *self,
GtkATContext *rel_context = gtk_accessible_get_at_context (rel);
gtk_at_context_get_name_accumulate (rel_context, names, FALSE);
g_object_unref (rel_context);
}
}
@ -1065,6 +1102,8 @@ gtk_at_context_get_description_accumulate (GtkATContext *self,
GtkATContext *rel_context = gtk_accessible_get_at_context (rel);
gtk_at_context_get_description_accumulate (rel_context, labels, FALSE);
g_object_unref (rel_context);
}
}

View File

@ -1608,6 +1608,7 @@ create_subparser (GObject *object,
subparser->object = object;
subparser->child = child;
subparser->tagname = g_strdup (element_name);
subparser->level = 1;
subparser->start = element_name;
subparser->parser = g_memdup2 (parser, sizeof (GtkBuildableParser));
subparser->data = user_data;
@ -1638,6 +1639,8 @@ subparser_start (GtkBuildableParseContext *context,
if (subparser->start)
{
subparser->level++;
if (subparser->parser->start_element)
subparser->parser->start_element (context,
element_name, names, values,
@ -1653,6 +1656,8 @@ subparser_end (GtkBuildableParseContext *context,
ParserData *data,
GError **error)
{
data->subparser->level--;
if (data->subparser->parser->end_element)
data->subparser->parser->end_element (context, element_name,
data->subparser->data, error);
@ -1660,9 +1665,11 @@ subparser_end (GtkBuildableParseContext *context,
if (*error)
return;
if (strcmp (data->subparser->start, element_name) != 0)
if (data->subparser->level > 0)
return;
g_assert (strcmp (data->subparser->start, element_name) == 0);
gtk_buildable_custom_tag_end (GTK_BUILDABLE (data->subparser->object),
data->builder,
data->subparser->child,

View File

@ -165,6 +165,7 @@ struct _GtkBuildableParseContext {
typedef struct {
GtkBuildableParser *parser;
char *tagname;
int level;
const char *start;
gpointer data;
GObject *object;

View File

@ -19,7 +19,7 @@
* Modified by the GTK+ Team and others 1997-2001. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
/**
@ -63,7 +63,6 @@
#include "gtkactionhelperprivate.h"
#include "gtkbuildable.h"
#include "gtkcheckbutton.h"
#include "gtkgestureclick.h"
#include "gtkeventcontrollerkey.h"
#include "gtkbinlayout.h"
@ -829,8 +828,6 @@ gtk_button_set_label (GtkButton *button,
gtk_label_set_use_underline (GTK_LABEL (child), priv->use_underline);
gtk_label_set_mnemonic_widget (GTK_LABEL (child), GTK_WIDGET (button));
}
if (GTK_IS_CHECK_BUTTON (button))
gtk_label_set_xalign (GTK_LABEL (child), 0.0);
gtk_button_set_child (button, child);
}

View File

@ -477,6 +477,8 @@ gtk_column_view_dispose (GObject *object)
{
GtkColumnView *self = GTK_COLUMN_VIEW (object);
gtk_column_view_sorter_clear (GTK_COLUMN_VIEW_SORTER (self->sorter));
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);

View File

@ -507,9 +507,6 @@ gtk_css_node_real_node_removed (GtkCssNode *parent,
node->previous_sibling = NULL;
node->next_sibling = NULL;
node->parent = NULL;
if (parent->children_observer)
gtk_list_list_model_item_removed (parent->children_observer, previous);
}
static void
@ -715,6 +712,8 @@ gtk_css_node_reposition (GtkCssNode *node,
if (old_parent != NULL)
{
GTK_CSS_NODE_GET_CLASS (old_parent)->node_removed (old_parent, node, node->previous_sibling);
if (old_parent->children_observer && old_parent != new_parent)
gtk_list_list_model_item_removed (old_parent->children_observer, previous);
if (old_parent->first_child && node->visible)
gtk_css_node_invalidate (old_parent->first_child, GTK_CSS_CHANGE_NTH_LAST_CHILD);
}
@ -784,7 +783,7 @@ gtk_css_node_reposition (GtkCssNode *node,
if (new_parent && new_parent->children_observer)
{
if (old_previous)
if (old_previous && old_parent == new_parent)
gtk_list_list_model_item_moved (new_parent->children_observer, node, old_previous);
else
gtk_list_list_model_item_added (new_parent->children_observer, node);

View File

@ -1306,6 +1306,9 @@ typedef enum {
* @GTK_ACCESSIBLE_ROLE_WIDGET: An interactive component of a graphical user
* interface. This is the role that GTK uses by default for widgets.
* @GTK_ACCESSIBLE_ROLE_WINDOW: An application window.
* @GTK_ACCESSIBLE_ROLE_TOGGLE_BUTTON: A type of push button
* which stays pressed until depressed by a second activation.
* Since: 4.10
*
* The accessible role for a [iface@Accessible] implementation.
*
@ -1390,7 +1393,8 @@ typedef enum {
GTK_ACCESSIBLE_ROLE_TREE_GRID,
GTK_ACCESSIBLE_ROLE_TREE_ITEM,
GTK_ACCESSIBLE_ROLE_WIDGET,
GTK_ACCESSIBLE_ROLE_WINDOW
GTK_ACCESSIBLE_ROLE_WINDOW,
GTK_ACCESSIBLE_ROLE_TOGGLE_BUTTON
} GtkAccessibleRole;
/**

View File

@ -328,7 +328,6 @@ struct _GtkFileChooserWidget
guint show_type_column : 1;
guint create_folders : 1;
guint auto_selecting_first_row : 1;
guint starting_search : 1;
guint browse_files_interaction_frozen : 1;
};
@ -638,25 +637,36 @@ _gtk_file_chooser_extract_recent_folders (GList *infos)
for (l = infos; l; l = l->next)
{
GtkRecentInfo *info = l->data;
const char *uri;
GFile *parent;
const char *uri, *mime_type;
GFile *dir;
GFile *file;
if (!gtk_recent_info_is_local (info))
continue;
uri = gtk_recent_info_get_uri (info);
file = g_file_new_for_uri (uri);
parent = g_file_get_parent (file);
g_object_unref (file);
if (parent)
mime_type = gtk_recent_info_get_mime_type (info);
if (strcmp (mime_type, "inode/directory") != 0)
{
if (!g_hash_table_lookup (folders, parent))
dir = g_file_get_parent (file);
g_object_unref (file);
}
else
{
dir = file;
}
if (dir)
{
if (!g_hash_table_lookup (folders, dir))
{
g_hash_table_insert (folders, parent, (gpointer) 1);
result = g_list_prepend (result, g_object_ref (parent));
g_hash_table_insert (folders, dir, (gpointer) 1);
result = g_list_prepend (result, g_object_ref (dir));
}
g_object_unref (parent);
g_object_unref (dir);
}
}
@ -2061,6 +2071,9 @@ file_chooser_get_location (GtkFileChooserWidget *impl,
else
location = g_file_get_path (dir_location);
if (!location)
location = g_strdup ("");
g_clear_object (&dir_location);
g_clear_object (&home_location);
@ -2660,6 +2673,16 @@ location_bar_update (GtkFileChooserWidget *impl)
gtk_widget_set_visible (impl->browse_new_folder_button, create_folder_visible);
}
static void
search_clear_engine (GtkFileChooserWidget *impl)
{
if (impl->search_engine)
{
g_signal_handlers_disconnect_by_data (impl->search_engine, impl);
g_clear_object (&impl->search_engine);
}
}
static void
operation_mode_stop (GtkFileChooserWidget *impl,
OperationMode mode)
@ -2670,6 +2693,7 @@ operation_mode_stop (GtkFileChooserWidget *impl,
search_stop_searching (impl, TRUE);
search_clear_model (impl, TRUE);
gtk_widget_set_visible (impl->remote_warning_bar, FALSE);
search_clear_engine (impl);
}
}
@ -3073,6 +3097,7 @@ cancel_all_operations (GtkFileChooserWidget *impl)
g_clear_object (&impl->file_exists_get_info_cancellable);
search_stop_searching (impl, TRUE);
search_clear_engine (impl);
}
static void sorter_changed (GtkSorter *main_sorter,
@ -3549,9 +3574,12 @@ show_and_select_files (GtkFileChooserWidget *impl,
if (!g_file_info_get_attribute_boolean (info, "filechooser::visible"))
{
gboolean has_is_hidden = g_file_info_has_attribute (info, "standard::is-hidden");
gboolean has_is_backup = g_file_info_has_attribute (info, "standard::is-backup");
if (!enabled_hidden &&
(g_file_info_get_is_hidden (info) ||
g_file_info_get_is_backup (info)))
((has_is_hidden && g_file_info_get_is_hidden (info)) ||
(has_is_backup && g_file_info_get_is_backup (info))))
{
set_show_hidden (impl, TRUE);
enabled_hidden = TRUE;
@ -5768,8 +5796,6 @@ search_stop_searching (GtkFileChooserWidget *impl,
if (impl->search_engine)
{
_gtk_search_engine_stop (impl->search_engine);
g_signal_handlers_disconnect_by_data (impl->search_engine, impl);
g_clear_object (&impl->search_engine);
set_busy_cursor (impl, FALSE);
gtk_widget_set_visible (impl->search_spinner, FALSE);
@ -5894,11 +5920,12 @@ static void
search_entry_stop_cb (GtkFileChooserWidget *impl)
{
if (impl->search_engine)
search_stop_searching (impl, FALSE);
{
search_stop_searching (impl, FALSE);
search_clear_engine (impl);
}
else
g_object_set (impl, "search-mode", FALSE, NULL);
impl->starting_search = FALSE;
}
/* Hides the path bar and creates the search entry */
@ -5977,6 +6004,9 @@ recent_start_loading (GtkFileChooserWidget *impl)
GtkRecentInfo *info = l->data;
GFile *file;
if (!gtk_recent_info_is_local (info))
continue;
if (gtk_recent_info_get_private_hint (info) &&
!gtk_recent_info_has_application (info, app_name))
continue;
@ -6983,15 +7013,20 @@ location_sort_func (gconstpointer a,
gpointer user_data)
{
GtkFileChooserWidget *impl = user_data;
char *location_a, *location_b;
char *key_a, *key_b;
GtkOrdering result;
/* FIXME: use sortkeys for these */
key_a = g_utf8_collate_key_for_filename (file_chooser_get_location (impl, (GFileInfo *)a), -1);
key_b = g_utf8_collate_key_for_filename (file_chooser_get_location (impl, (GFileInfo *)b), -1);
location_a = file_chooser_get_location (impl, (GFileInfo *)a);
location_b = file_chooser_get_location (impl, (GFileInfo *)b);
key_a = g_utf8_collate_key_for_filename (location_a, -1);
key_b = g_utf8_collate_key_for_filename (location_b, -1);
result = g_strcmp0 (key_a, key_b);
g_free (location_a);
g_free (location_b);
g_free (key_a);
g_free (key_b);

View File

@ -209,13 +209,18 @@ node_should_be_visible (GtkFileSystemModel *model,
gboolean filtered_out)
{
FileModelNode *node = get_node (model, id);
gboolean has_is_hidden;
gboolean has_is_backup;
gboolean result;
if (node->info == NULL)
return FALSE;
has_is_hidden = g_file_info_has_attribute (node->info, "standard::is-hidden");
has_is_backup = g_file_info_has_attribute (node->info, "standard::is-backup");
if (!model->show_hidden &&
(g_file_info_get_is_hidden (node->info) || g_file_info_get_is_backup (node->info)))
((has_is_hidden && g_file_info_get_is_hidden (node->info)) ||
(has_is_backup && g_file_info_get_is_backup (node->info))))
return FALSE;
if (_gtk_file_info_consider_as_directory (node->info))
@ -459,7 +464,7 @@ remove_file (GtkFileSystemModel *model,
g_return_if_fail (G_IS_FILE (file));
id = node_get_for_file (model, file);
if (id == 0)
if (id == GTK_INVALID_LIST_POSITION)
return;
node = get_node (model, id);
@ -941,7 +946,7 @@ _gtk_file_system_model_set_filter_folders (GtkFileSystemModel *model,
* @model: the model
*
* Gets the cancellable used by the @model. This is the cancellable used
* internally by the @model that will be cancelled when @model is
* internally by the @model that will be cancelled when @model is
* disposed. So you can use it for operations that should be cancelled
* when the model goes away.
*
@ -1005,7 +1010,7 @@ _gtk_file_system_model_update_files (GtkFileSystemModel *model,
* _gtk_file_system_model_set_filter:
* @mode: a `GtkFileSystemModel`
* @filter: (nullable): %NULL or filter to use
*
*
* Sets a filter to be used for deciding if a row should be visible or not.
* Whether this filter applies to directories can be toggled with
* _gtk_file_system_model_set_filter_folders().
@ -1028,7 +1033,7 @@ _gtk_file_system_model_set_filter (GtkFileSystemModel *model,
* @file: the file to add
* @attributes: attributes to query before adding the file
*
* This is a convenience function that calls g_file_query_info_async() on
* This is a convenience function that calls g_file_query_info_async() on
* the given file, and when successful, adds it to the model.
* Upon failure, the @file is discarded.
**/

View File

@ -78,7 +78,10 @@ update_image (GtkFileThumbnail *self)
int scale;
if (!g_file_info_has_attribute (self->info, G_FILE_ATTRIBUTE_STANDARD_ICON))
return FALSE;
{
gtk_image_clear (GTK_IMAGE (self->image));
return FALSE;
}
scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (self)));
@ -91,7 +94,6 @@ update_image (GtkFileThumbnail *self)
g_object_unref (icon);
return TRUE;
}
static void
@ -102,10 +104,19 @@ thumbnail_queried_cb (GObject *object,
GtkFileThumbnail *self = user_data; /* might be unreffed if operation was cancelled */
GFile *file = G_FILE (object);
GFileInfo *queried;
GError *error = NULL;
queried = g_file_query_info_finish (file, result, NULL);
if (queried == NULL)
return;
queried = g_file_query_info_finish (file, result, &error);
if (error)
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_file_info_set_attribute_boolean (self->info, "filechooser::queried", TRUE);
g_clear_error (&error);
return;
}
g_file_info_set_attribute_boolean (self->info, "filechooser::queried", TRUE);
copy_attribute (self->info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
copy_attribute (self->info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
@ -129,7 +140,10 @@ static void
get_thumbnail (GtkFileThumbnail *self)
{
if (!self->info)
return;
{
gtk_image_clear (GTK_IMAGE (self->image));
return;
}
if (!update_image (self))
{
@ -142,7 +156,6 @@ get_thumbnail (GtkFileThumbnail *self)
self->cancellable = g_cancellable_new ();
file = _gtk_file_info_get_file (self->info);
g_file_info_set_attribute_boolean (self->info, "filechooser::queried", TRUE);
g_file_query_info_async (file,
G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","

View File

@ -82,9 +82,6 @@
* use the %GTK_ACCESSIBLE_ROLE_GRID_CELL role.
*/
typedef struct _Cell Cell;
typedef struct _CellAugment CellAugment;
struct _GtkGridView
{
GtkListBase parent_instance;
@ -94,7 +91,6 @@ struct _GtkGridView
guint max_columns;
/* set in size_allocate */
guint n_columns;
int unknown_row_height;
double column_width;
};
@ -103,18 +99,6 @@ struct _GtkGridViewClass
GtkListBaseClass parent_class;
};
struct _Cell
{
GtkListItemManagerItem parent;
guint size; /* total, only counting cells in first column */
};
struct _CellAugment
{
GtkListItemManagerItemAugment parent;
guint size; /* total, only counting first column */
};
enum
{
PROP_0,
@ -141,333 +125,216 @@ static guint signals[LAST_SIGNAL] = { 0 };
static void G_GNUC_UNUSED
dump (GtkGridView *self)
{
Cell *cell;
GtkListTile *tile;
guint n_widgets, n_list_rows, n_items;
n_widgets = 0;
n_list_rows = 0;
n_items = 0;
//g_print ("ANCHOR: %u - %u\n", self->anchor_start, self->anchor_end);
for (cell = gtk_list_item_manager_get_first (self->item_manager);
cell;
cell = gtk_rb_tree_node_get_next (cell))
for (tile = gtk_list_item_manager_get_first (self->item_manager);
tile;
tile = gtk_rb_tree_node_get_next (tile))
{
if (cell->parent.widget)
if (tile->widget)
n_widgets++;
n_list_rows++;
n_items += cell->parent.n_items;
g_print ("%6u%6u %5ux%3u %s (%upx)\n",
cell->parent.n_items, n_items,
n_items += tile->n_items;
g_print ("%6u%6u %5ux%3u %s (%d,%d,%d,%d)\n",
tile->n_items, n_items,
n_items / (self->n_columns ? self->n_columns : self->min_columns),
n_items % (self->n_columns ? self->n_columns : self->min_columns),
cell->parent.widget ? " (widget)" : "", cell->size);
tile->widget ? " (widget)" : "",
tile->area.x, tile->area.y, tile->area.width, tile->area.height);
}
g_print (" => %u widgets in %u list rows\n", n_widgets, n_list_rows);
}
static void
cell_augment (GtkRbTree *tree,
gpointer node_augment,
gpointer node,
gpointer left,
gpointer right)
static GtkListTile *
gtk_grid_view_split (GtkListBase *base,
GtkListTile *tile,
guint n_items)
{
Cell *cell = node;
CellAugment *aug = node_augment;
GtkGridView *self = GTK_GRID_VIEW (base);
GtkListTile *split;
guint col, row_height;
gtk_list_item_manager_augment_node (tree, node_augment, node, left, right);
row_height = tile->area.height / MAX (tile->n_items / self->n_columns, 1);
aug->size = cell->size;
if (left)
/* split off the multirow at the top */
if (n_items >= self->n_columns)
{
CellAugment *left_aug = gtk_rb_tree_get_augment (tree, left);
guint top_rows = n_items / self->n_columns;
guint top_items = top_rows * self->n_columns;
aug->size += left_aug->size;
split = tile;
tile = gtk_list_tile_split (self->item_manager, tile, top_items);
gtk_list_tile_set_area (self->item_manager,
tile,
&(GdkRectangle) {
split->area.x,
split->area.y + row_height * top_rows,
split->area.width,
split->area.height - row_height * top_rows,
});
gtk_list_tile_set_area_size (self->item_manager,
split,
split->area.width,
row_height * top_rows);
n_items -= top_items;
if (n_items == 0)
return tile;
}
if (right)
/* split off the multirow at the bottom */
if (tile->n_items > self->n_columns)
{
CellAugment *right_aug = gtk_rb_tree_get_augment (tree, right);
aug->size += right_aug->size;
}
}
/*<private>
* gtk_grid_view_get_cell_at_y:
* @self: a `GtkGridView`
* @y: an offset in direction of @self's orientation
* @position: (out caller-allocates) (optional): stores the position
* index of the returned row
* @offset: (out caller-allocates) (optional): stores the offset
* in pixels between y and top of cell.
* @size: (out caller-allocates) (optional): stores the height
* of the cell
*
* Gets the Cell that occupies the leftmost position in the row at offset
* @y into the primary direction.
*
* If y is larger than the height of all cells, %NULL will be returned.
* In particular that means that for an empty grid, %NULL is returned
* for any value.
*
* Returns: (nullable): The first cell at offset y
**/
static Cell *
gtk_grid_view_get_cell_at_y (GtkGridView *self,
int y,
guint *position,
int *offset,
int *size)
{
Cell *cell, *tmp;
guint pos;
cell = gtk_list_item_manager_get_root (self->item_manager);
pos = 0;
while (cell)
{
tmp = gtk_rb_tree_node_get_left (cell);
if (tmp)
{
CellAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, tmp);
if (y < aug->size)
{
cell = tmp;
continue;
}
y -= aug->size;
pos += aug->parent.n_items;
}
if (y < cell->size)
break;
y -= cell->size;
pos += cell->parent.n_items;
cell = gtk_rb_tree_node_get_right (cell);
split = gtk_list_tile_split (self->item_manager, tile, self->n_columns);
gtk_list_tile_set_area (self->item_manager,
split,
&(GdkRectangle) {
tile->area.x,
tile->area.y + row_height,
tile->area.width,
tile->area.height - row_height,
});
gtk_list_tile_set_area_size (self->item_manager,
tile,
tile->area.width,
row_height);
}
if (cell == NULL)
{
if (position)
*position = 0;
if (offset)
*offset = 0;
if (size)
*size = 0;
return NULL;
}
g_assert (n_items < tile->n_items);
g_assert (tile->n_items <= self->n_columns);
/* We know have the (range of) cell(s) that contains this offset.
* Now for the hard part of computing which index this actually is.
*/
if (offset || position || size)
{
guint n_items = cell->parent.n_items;
guint no_widget_rows, skip;
/* skip remaining items at end of row */
if (pos % self->n_columns)
{
skip = self->n_columns - pos % self->n_columns;
if (n_items <= skip)
{
g_warning ("ran out of items");
if (position)
*position = 0;
if (offset)
*offset = 0;
if (size)
*size = 0;
return NULL;
}
n_items -= skip;
pos += skip;
}
/* Skip all the rows this index doesn't go into */
no_widget_rows = (n_items - 1) / self->n_columns;
skip = MIN (y / self->unknown_row_height, no_widget_rows);
y -= skip * self->unknown_row_height;
pos += self->n_columns * skip;
if (position)
*position = pos;
if (offset)
*offset = y;
if (size)
{
if (skip < no_widget_rows)
*size = self->unknown_row_height;
else
*size = cell->size - no_widget_rows * self->unknown_row_height;
}
}
return cell;
/* now it's a single row, do a split at the column boundary */
col = tile->area.x / self->column_width;
split = gtk_list_tile_split (self->item_manager, tile, n_items);
gtk_list_tile_set_area (self->item_manager,
split,
&(GdkRectangle) {
ceil ((col + n_items) * self->column_width),
tile->area.y,
ceil ((col + n_items + split->n_items) * self->column_width),
tile->area.height,
});
gtk_list_tile_set_area_size (self->item_manager,
tile,
ceil ((col + n_items) * self->column_width) - tile->area.x,
tile->area.height);
return split;
}
static gboolean
gtk_grid_view_get_allocation_along (GtkListBase *base,
guint pos,
int *offset,
int *size)
gtk_grid_view_get_allocation (GtkListBase *base,
guint pos,
GdkRectangle *area)
{
GtkGridView *self = GTK_GRID_VIEW (base);
Cell *cell, *tmp;
int y;
GtkListTile *tile;
guint offset;
cell = gtk_list_item_manager_get_root (self->item_manager);
y = 0;
pos -= pos % self->n_columns;
tile = gtk_list_item_manager_get_nth (self->item_manager, pos, &offset);
if (tile == NULL || tile->area.width <= 0 || tile->area.height <= 0)
return FALSE;
while (cell)
*area = tile->area;
if (tile->n_items > self->n_columns)
{
tmp = gtk_rb_tree_node_get_left (cell);
if (tmp)
{
CellAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, tmp);
if (pos < aug->parent.n_items)
{
cell = tmp;
continue;
}
pos -= aug->parent.n_items;
y += aug->size;
}
if (pos < cell->parent.n_items)
break;
y += cell->size;
pos -= cell->parent.n_items;
cell = gtk_rb_tree_node_get_right (cell);
area->height /= (tile->n_items / self->n_columns);
area->y += (offset / self->n_columns) * area->height;
offset %= self->n_columns;
}
if (cell == NULL)
if (tile->n_items > 1)
{
if (offset)
*offset = 0;
if (size)
*size = 0;
return FALSE;
}
/* We know have the (range of) cell(s) that contains this offset.
* Now for the hard part of computing which index this actually is.
*/
if (offset || size)
{
guint n_items = cell->parent.n_items;
guint skip;
/* skip remaining items at end of row */
if (pos % self->n_columns)
{
skip = pos % self->n_columns;
n_items -= skip;
pos -= skip;
}
/* Skip all the rows this index doesn't go into */
skip = pos / self->n_columns;
n_items -= skip * self->n_columns;
y += skip * self->unknown_row_height;
if (offset)
*offset = y;
if (size)
{
if (n_items > self->n_columns)
*size = self->unknown_row_height;
else
*size = cell->size - skip * self->unknown_row_height;
}
guint col = area->x / self->column_width;
area->x = ceil ((col + offset) * self->column_width);
area->width = ceil ((col + offset + 1) * self->column_width) - area->x;
}
return TRUE;
}
static gboolean
gtk_grid_view_get_allocation_across (GtkListBase *base,
guint pos,
int *offset,
int *size)
{
GtkGridView *self = GTK_GRID_VIEW (base);
guint start;
pos %= self->n_columns;
start = ceil (self->column_width * pos);
if (offset)
*offset = start;
if (size)
*size = ceil (self->column_width * (pos + 1)) - start;
return TRUE;
}
static int
gtk_grid_view_compute_total_height (GtkGridView *self)
{
Cell *cell;
CellAugment *aug;
cell = gtk_list_item_manager_get_root (self->item_manager);
if (cell == NULL)
return 0;
aug = gtk_list_item_manager_get_item_augment (self->item_manager, cell);
return aug->size;
}
static gboolean
gtk_grid_view_get_position_from_allocation (GtkListBase *base,
int across,
int along,
int x,
int y,
guint *position,
cairo_rectangle_int_t *area)
{
GtkGridView *self = GTK_GRID_VIEW (base);
int offset, size;
guint pos, n_items;
GtkListTile *tile;
guint pos;
GdkRectangle bounds;
if (across >= self->column_width * self->n_columns)
gtk_list_item_manager_get_tile_bounds (self->item_manager, &bounds);
if (bounds.width <= 0 || bounds.height <= 0)
return FALSE;
x = CLAMP (x, bounds.x, bounds.x + bounds.width - 1);
y = CLAMP (y, bounds.y, bounds.y + bounds.height - 1);
tile = gtk_list_item_manager_get_tile_at (self->item_manager, x, y);
if (tile == NULL)
return FALSE;
n_items = gtk_list_base_get_n_items (base);
along = CLAMP (along, 0, gtk_grid_view_compute_total_height (self) - 1);
across = across < 0 ? 0 : across;
if (!gtk_grid_view_get_cell_at_y (self,
along,
&pos,
&offset,
&size))
return FALSE;
pos += floor (across / self->column_width);
if (pos >= n_items)
while (tile && tile->n_items == 0)
tile = gtk_rb_tree_node_get_previous (tile);
if (tile == NULL)
{
/* Ugh, we're in the last row and don't have enough items
* to fill the row.
* Do it the hard way then... */
pos = n_items - 1;
tile = gtk_list_item_manager_get_first (self->item_manager);
while (tile && tile->n_items == 0)
tile = gtk_rb_tree_node_get_next (tile);
if (tile == NULL)
return FALSE;
}
pos = gtk_list_tile_get_position (self->item_manager, tile);
if (tile->n_items > 1)
{
/* offset in x direction */
pos += (x - tile->area.x) / self->column_width;
if (area)
{
guint col = MIN (x / self->column_width, self->n_columns - 1);
area->x = ceil (col * self->column_width);
area->width = ceil ((col + 1) * self->column_width) - area->x;
}
/* offset in y direction */
if (tile->n_items > self->n_columns)
{
guint rows_in_tile = tile->n_items / self->n_columns;
guint row_height = tile->area.height / rows_in_tile;
guint row_index = (y - tile->area.y) / row_height;
pos += self->n_columns * row_index;
if (area)
{
area->y = tile->area.y + row_index * row_height;
area->height = row_height;
}
}
else
{
if (area)
{
area->y = tile->area.y;
area->height = tile->area.height;
}
}
}
else
{
if (area)
*area = tile->area;
}
*position = pos;
if (area)
{
area->x = ceil (self->column_width * (pos % self->n_columns));
area->width = ceil (self->column_width * (1 + pos % self->n_columns)) - area->x;
area->y = along - offset;
area->height = size;
}
return TRUE;
}
@ -477,24 +344,24 @@ gtk_grid_view_get_items_in_rect (GtkListBase *base,
const GdkRectangle *rect)
{
GtkGridView *self = GTK_GRID_VIEW (base);
guint first_row, last_row, first_column, last_column, n_items;
guint first_row, last_row, first_column, last_column;
GdkRectangle bounds;
GtkBitset *result;
result = gtk_bitset_new_empty ();
if (rect->y >= gtk_grid_view_compute_total_height (self))
/* limit rect to the region that actually overlaps items */
gtk_list_item_manager_get_tile_bounds (self->item_manager, &bounds);
if (!gdk_rectangle_intersect (&bounds, rect, &bounds))
return result;
n_items = gtk_list_base_get_n_items (base);
if (n_items == 0)
return result;
first_column = fmax (floor (rect->x / self->column_width), 0);
last_column = fmin (floor ((rect->x + rect->width) / self->column_width), self->n_columns - 1);
if (!gtk_grid_view_get_cell_at_y (self, rect->y, &first_row, NULL, NULL))
first_row = rect->y < 0 ? 0 : n_items - 1;
if (!gtk_grid_view_get_cell_at_y (self, rect->y + rect->height, &last_row, NULL, NULL))
last_row = rect->y + rect->height < 0 ? 0 : n_items - 1;
first_column = fmax (floor (bounds.x / self->column_width), 0);
last_column = fmin (floor ((bounds.x + bounds.width) / self->column_width), self->n_columns - 1);
/* match y = 0 here because we care about the rows, not the cells */
if (!gtk_grid_view_get_position_from_allocation (base, 0, bounds.y, &first_row, NULL))
g_return_val_if_reached (result);
if (!gtk_grid_view_get_position_from_allocation (base, 0, bounds.y + bounds.height - 1, &last_row, NULL))
g_return_val_if_reached (result);
gtk_bitset_add_rectangle (result,
first_row + first_column,
@ -570,22 +437,21 @@ gtk_grid_view_measure_column_size (GtkGridView *self,
int *natural)
{
GtkOrientation opposite;
Cell *cell;
GtkListTile *tile;
int min, nat, child_min, child_nat;
min = 0;
nat = 0;
opposite = gtk_list_base_get_opposite_orientation (GTK_LIST_BASE (self));
for (cell = gtk_list_item_manager_get_first (self->item_manager);
cell != NULL;
cell = gtk_rb_tree_node_get_next (cell))
for (tile = gtk_list_item_manager_get_first (self->item_manager);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
/* ignore unavailable cells */
if (cell->parent.widget == NULL)
if (tile->widget == NULL)
continue;
gtk_widget_measure (cell->parent.widget,
gtk_widget_measure (tile->widget,
opposite, -1,
&child_min, &child_nat, NULL, NULL);
min = MAX (min, child_min);
@ -640,7 +506,7 @@ gtk_grid_view_measure_list (GtkWidget *widget,
{
GtkGridView *self = GTK_GRID_VIEW (widget);
GtkScrollablePolicy scroll_policy;
Cell *cell;
GtkListTile *tile;
int height, row_height, child_min, child_nat, column_size, col_min, col_nat;
gboolean measured;
GArray *heights;
@ -660,13 +526,13 @@ gtk_grid_view_measure_list (GtkWidget *widget,
i = 0;
row_height = 0;
measured = FALSE;
for (cell = gtk_list_item_manager_get_first (self->item_manager);
cell != NULL;
cell = gtk_rb_tree_node_get_next (cell))
for (tile = gtk_list_item_manager_get_first (self->item_manager);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
if (cell->parent.widget)
if (tile->widget)
{
gtk_widget_measure (cell->parent.widget,
gtk_widget_measure (tile->widget,
gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
column_size,
&child_min, &child_nat, NULL, NULL);
@ -677,7 +543,7 @@ gtk_grid_view_measure_list (GtkWidget *widget,
measured = TRUE;
}
i += cell->parent.n_items;
i += tile->n_items;
if (i >= n_columns)
{
@ -731,17 +597,6 @@ gtk_grid_view_measure (GtkWidget *widget,
gtk_grid_view_measure_across (widget, for_size, minimum, natural);
}
static void
cell_set_size (Cell *cell,
guint size)
{
if (cell->size == size)
return;
cell->size = size;
gtk_rb_tree_node_mark_dirty (cell);
}
static void
gtk_grid_view_size_allocate (GtkWidget *widget,
int width,
@ -749,24 +604,23 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
int baseline)
{
GtkGridView *self = GTK_GRID_VIEW (widget);
Cell *cell, *start;
GtkListTile *tile, *start;
GArray *heights;
int min_row_height, row_height, col_min, col_nat;
GtkOrientation orientation, opposite_orientation;
int min_row_height, unknown_row_height, row_height, col_min, col_nat;
GtkOrientation orientation;
GtkScrollablePolicy scroll_policy;
gboolean known;
int x, y;
int y;
guint i;
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
scroll_policy = gtk_list_base_get_scroll_policy (GTK_LIST_BASE (self), orientation);
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
min_row_height = ceil ((double) height / GTK_GRID_VIEW_MAX_VISIBLE_ROWS);
/* step 0: exit early if list is empty */
if (gtk_list_item_manager_get_root (self->item_manager) == NULL)
tile = gtk_list_tile_gc (self->item_manager, gtk_list_item_manager_get_first (self->item_manager));
if (tile == NULL)
{
gtk_list_base_update_adjustments (GTK_LIST_BASE (self), 0, 0, 0, 0, &x, &y);
gtk_list_base_allocate (GTK_LIST_BASE (self));
return;
}
@ -781,149 +635,126 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
/* step 2: determine height of known rows */
heights = g_array_new (FALSE, FALSE, sizeof (int));
i = 0;
row_height = 0;
start = NULL;
for (cell = gtk_list_item_manager_get_first (self->item_manager);
cell != NULL;
cell = gtk_rb_tree_node_get_next (cell))
for (;
tile != NULL;
tile = gtk_list_tile_gc (self->item_manager, tile))
{
if (i == 0)
start = cell;
if (cell->parent.widget)
/* if it's a multirow tile, handle it here */
if (tile->n_items > 1 && tile->n_items >= self->n_columns)
{
int min, nat, size;
gtk_widget_measure (cell->parent.widget,
gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
self->column_width,
&min, &nat, NULL, NULL);
if (scroll_policy == GTK_SCROLL_MINIMUM)
size = min;
else
size = nat;
size = MAX (size, min_row_height);
g_array_append_val (heights, size);
row_height = MAX (row_height, size);
if (tile->n_items % self->n_columns)
gtk_list_tile_split (self->item_manager, tile, tile->n_items / self->n_columns * self->n_columns);
tile = gtk_rb_tree_node_get_next (tile);
continue;
}
cell_set_size (cell, 0);
i += cell->parent.n_items;
if (i >= self->n_columns)
/* Not a multirow tile */
i = 0;
row_height = 0;
for (i = 0, start = tile;
i < self->n_columns && tile != NULL;
tile = gtk_list_tile_gc (self->item_manager, gtk_rb_tree_node_get_next (tile)))
{
i %= self->n_columns;
cell_set_size (start, start->size + row_height);
start = cell;
row_height = 0;
if (tile->widget)
{
int min, nat, size;
gtk_widget_measure (tile->widget,
gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
self->column_width,
&min, &nat, NULL, NULL);
if (scroll_policy == GTK_SCROLL_MINIMUM)
size = min;
else
size = nat;
size = MAX (size, min_row_height);
g_array_append_val (heights, size);
row_height = MAX (row_height, size);
}
if (tile->n_items > self->n_columns - i)
gtk_list_tile_split (self->item_manager, tile, self->n_columns - i);
i += tile->n_items;
}
if (row_height > 0)
{
for (i = 0;
start != tile;
start = gtk_rb_tree_node_get_next (start))
{
gtk_list_tile_set_area_size (self->item_manager,
start,
ceil (self->column_width * (i + start->n_items)) - ceil (self->column_width * i),
row_height);
i += start->n_items;
}
g_assert (i <= self->n_columns);
}
}
if (i > 0)
cell_set_size (start, start->size + row_height);
/* step 3: determine height of rows with only unknown items */
self->unknown_row_height = gtk_grid_view_get_unknown_row_size (self, heights);
unknown_row_height = gtk_grid_view_get_unknown_row_size (self, heights);
g_array_free (heights, TRUE);
/* step 4: determine height for remaining rows and set each row's position */
y = 0;
i = 0;
known = FALSE;
for (start = cell = gtk_list_item_manager_get_first (self->item_manager);
cell != NULL;
cell = gtk_rb_tree_node_get_next (cell))
for (tile = gtk_list_item_manager_get_first (self->item_manager);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
if (i == 0)
start = cell;
if (cell->parent.widget)
known = TRUE;
i += cell->parent.n_items;
if (i >= self->n_columns)
gtk_list_tile_set_area_position (self->item_manager,
tile,
ceil (self->column_width * i),
y);
if (tile->n_items >= self->n_columns && tile->widget == NULL)
{
if (!known)
cell_set_size (start, start->size + self->unknown_row_height);
i -= self->n_columns;
known = FALSE;
if (i >= self->n_columns)
{
cell_set_size (cell, cell->size + self->unknown_row_height * (i / self->n_columns));
i %= self->n_columns;
}
start = cell;
}
}
if (i > 0 && !known)
cell_set_size (start, start->size + self->unknown_row_height);
/* step 4: update the adjustments */
gtk_list_base_update_adjustments (GTK_LIST_BASE (self),
self->column_width * self->n_columns,
gtk_grid_view_compute_total_height (self),
gtk_widget_get_size (widget, opposite_orientation),
gtk_widget_get_size (widget, orientation),
&x, &y);
/* step 5: run the size_allocate loop */
x = -x;
y = -y;
i = 0;
row_height = 0;
g_assert (self->n_columns > 0);
for (cell = gtk_list_item_manager_get_first (self->item_manager);
cell != NULL;
cell = gtk_rb_tree_node_get_next (cell))
{
if (cell->parent.widget)
{
row_height += cell->size;
gtk_list_base_size_allocate_child (GTK_LIST_BASE (self),
cell->parent.widget,
x + ceil (self->column_width * i),
y,
ceil (self->column_width * (i + 1)) - ceil (self->column_width * i),
row_height);
i++;
if (i >= self->n_columns)
{
y += row_height;
i -= self->n_columns;
row_height = 0;
}
g_assert (i == 0);
g_assert (tile->n_items % self->n_columns == 0);
gtk_list_tile_set_area_size (self->item_manager,
tile,
ceil (self->column_width * self->n_columns),
unknown_row_height * (tile->n_items / self->n_columns));
y += tile->area.height;
}
else
{
i += cell->parent.n_items;
/* skip remaining row if we didn't start one */
if (i > cell->parent.n_items && i >= self->n_columns)
if (tile->area.height == 0)
{
i -= self->n_columns;
y += row_height;
row_height = 0;
/* this case is for the last row - it may not be a full row so it won't
* be a multirow tile but it may have no widgets either */
gtk_list_tile_set_area_size (self->item_manager,
tile,
ceil (self->column_width * (i + tile->n_items)) - ceil (self->column_width * i),
unknown_row_height);
}
i += tile->n_items;
}
row_height += cell->size;
/* skip rows that are completely contained by this cell */
if (i >= self->n_columns)
{
guint unknown_rows, unknown_height;
unknown_rows = i / self->n_columns;
unknown_height = unknown_rows * self->unknown_row_height;
row_height -= unknown_height;
y += unknown_height;
i %= self->n_columns;
g_assert (row_height >= 0);
}
if (i >= self->n_columns)
{
g_assert (i == self->n_columns);
y += tile->area.height;
i = 0;
}
}
/* Add a filler tile for empty space in the bottom right */
if (i < self->n_columns)
{
GtkListTile *filler;
tile = gtk_list_item_manager_get_last (self->item_manager);
filler = gtk_list_tile_split (self->item_manager, tile, tile->n_items);
gtk_list_tile_set_area_position (self->item_manager,
filler,
ceil (self->column_width * i),
y);
gtk_list_tile_set_area_size (self->item_manager,
filler,
ceil (self->column_width * self->n_columns) - filler->area.x,
tile->area.height);
}
gtk_list_base_allocate_rubberband (GTK_LIST_BASE (widget));
/* step 4: allocate the rest */
gtk_list_base_allocate (GTK_LIST_BASE (self));
}
static void
@ -1043,11 +874,8 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
list_base_class->list_item_name = "child";
list_base_class->list_item_role = GTK_ACCESSIBLE_ROLE_GRID_CELL;
list_base_class->list_item_size = sizeof (Cell);
list_base_class->list_item_augment_size = sizeof (CellAugment);
list_base_class->list_item_augment_func = cell_augment;
list_base_class->get_allocation_along = gtk_grid_view_get_allocation_along;
list_base_class->get_allocation_across = gtk_grid_view_get_allocation_across;
list_base_class->split = gtk_grid_view_split;
list_base_class->get_allocation = gtk_grid_view_get_allocation;
list_base_class->get_items_in_rect = gtk_grid_view_get_items_in_rect;
list_base_class->get_position_from_allocation = gtk_grid_view_get_position_from_allocation;
list_base_class->move_focus_along = gtk_grid_view_move_focus_along;

View File

@ -351,6 +351,8 @@ init_compose_table_thread_cb (GTask *task,
gtk_im_context_simple_init_compose_table ();
g_task_return_boolean (task, TRUE);
gdk_profiler_end_mark (before, "im compose table load (thread)", NULL);
}

View File

@ -327,49 +327,22 @@ gtk_list_base_move_focus (GtkListBase *self,
}
/*
* gtk_list_base_get_allocation_along:
* gtk_list_base_get_allocation:
* @self: a `GtkListBase`
* @pos: item to get the size of
* @offset: (out caller-allocates) (optional): set to the offset
* of the top/left of the item
* @size: (out caller-allocates) (optional): set to the size of
* the item in the direction
* @pos: item to get the area of
* @area: (out caller-allocates): set to the area
* occupied by the item
*
* Computes the allocation of the item in the direction along the sizing
* axis.
* Computes the allocation of the item in the given position
*
* Returns: %TRUE if the item exists and has an allocation, %FALSE otherwise
**/
static gboolean
gtk_list_base_get_allocation_along (GtkListBase *self,
guint pos,
int *offset,
int *size)
gtk_list_base_get_allocation (GtkListBase *self,
guint pos,
GdkRectangle *area)
{
return GTK_LIST_BASE_GET_CLASS (self)->get_allocation_along (self, pos, offset, size);
}
/*
* gtk_list_base_get_allocation_across:
* @self: a `GtkListBase`
* @pos: item to get the size of
* @offset: (out caller-allocates) (optional): set to the offset
* of the top/left of the item
* @size: (out caller-allocates) (optional): set to the size of
* the item in the direction
*
* Computes the allocation of the item in the direction across to the sizing
* axis.
*
* Returns: %TRUE if the item exists and has an allocation, %FALSE otherwise
**/
static gboolean
gtk_list_base_get_allocation_across (GtkListBase *self,
guint pos,
int *offset,
int *size)
{
return GTK_LIST_BASE_GET_CLASS (self)->get_allocation_across (self, pos, offset, size);
return GTK_LIST_BASE_GET_CLASS (self)->get_allocation (self, pos, area);
}
/*
@ -492,7 +465,7 @@ gtk_list_base_focus (GtkWidget *widget,
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
guint old, pos, n_items;
GtkWidget *focus_child;
GtkListItemManagerItem *item;
GtkListTile *tile;
focus_child = gtk_widget_get_focus_child (widget);
/* focus is moving around fine inside the focus child, don't disturb it */
@ -558,15 +531,15 @@ gtk_list_base_focus (GtkWidget *widget,
if (old == pos)
return TRUE;
item = gtk_list_item_manager_get_nth (priv->item_manager, pos, NULL);
if (item == NULL)
tile = gtk_list_item_manager_get_nth (priv->item_manager, pos, NULL);
if (tile == NULL)
return FALSE;
/* This shouldn't really happen, but if it does, oh well */
if (item->widget == NULL)
if (tile->widget == NULL)
return gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), pos, TRUE, FALSE, FALSE);
return gtk_widget_child_focus (item->widget, direction);
return gtk_widget_child_focus (tile->widget, direction);
}
static gboolean
@ -807,29 +780,22 @@ gtk_list_base_scroll_to_item (GtkListBase *self,
guint pos)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
int start, end;
double align_along, align_across;
GtkPackType side_along, side_across;
GdkRectangle area;
/* figure out primary orientation and if position is valid */
if (!gtk_list_base_get_allocation_along (GTK_LIST_BASE (self), pos, &start, &end))
if (!gtk_list_base_get_allocation (GTK_LIST_BASE (self), pos, &area))
return;
end += start;
gtk_list_base_compute_scroll_align (self,
gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
start, end,
area.y, area.y + area.height,
priv->anchor_align_along, priv->anchor_side_along,
&align_along, &side_along);
/* now do the same thing with the other orientation */
if (!gtk_list_base_get_allocation_across (GTK_LIST_BASE (self), pos, &start, &end))
return;
end += start;
gtk_list_base_compute_scroll_align (self,
gtk_list_base_get_opposite_orientation (GTK_LIST_BASE (self)),
start, end,
area.x, area.x + area.width,
priv->anchor_align_across, priv->anchor_side_across,
&align_across, &side_across);
@ -959,8 +925,7 @@ gtk_list_base_move_cursor_page_up (GtkWidget *widget,
pos = gtk_list_base_get_focus_position (self);
page_size = gtk_adjustment_get_page_size (priv->adjustment[priv->orientation]);
if (!gtk_list_base_get_allocation_along (self, pos, &area.y, &area.height) ||
!gtk_list_base_get_allocation_across (self, pos, &area.x, &area.width))
if (!gtk_list_base_get_allocation (self, pos, &area))
return TRUE;
if (!gtk_list_base_get_position_from_allocation (self,
area.x + area.width / 2,
@ -1005,8 +970,7 @@ gtk_list_base_move_cursor_page_down (GtkWidget *widget,
if (end == 0)
return TRUE;
if (!gtk_list_base_get_allocation_along (self, pos, &area.y, &area.height) ||
!gtk_list_base_get_allocation_across (self, pos, &area.x, &area.width))
if (!gtk_list_base_get_allocation (self, pos, &area))
return TRUE;
if (!gtk_list_base_get_position_from_allocation (self,
@ -1355,7 +1319,7 @@ update_autoscroll (GtkListBase *self,
remove_autoscroll (self);
}
/**
/*
* gtk_list_base_size_allocate_child:
* @self: The listbase
* @child: The child
@ -1368,7 +1332,7 @@ update_autoscroll (GtkListBase *self,
* but with the coordinates already offset by the scroll
* offset.
**/
void
static void
gtk_list_base_size_allocate_child (GtkListBase *self,
GtkWidget *child,
int x,
@ -1432,6 +1396,32 @@ gtk_list_base_size_allocate_child (GtkListBase *self,
gtk_widget_size_allocate (child, &child_allocation, -1);
}
static void
gtk_list_base_allocate_children (GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
GtkListTile *tile;
int dx, dy;
gtk_list_base_get_adjustment_values (self, OPPOSITE_ORIENTATION (priv->orientation), &dx, NULL, NULL);
gtk_list_base_get_adjustment_values (self, priv->orientation, &dy, NULL, NULL);
for (tile = gtk_list_item_manager_get_first (priv->item_manager);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
if (tile->widget)
{
gtk_list_base_size_allocate_child (GTK_LIST_BASE (self),
tile->widget,
tile->area.x - dx,
tile->area.y - dy,
tile->area.width,
tile->area.height);
}
}
}
static void
gtk_list_base_widget_to_list (GtkListBase *self,
double x_widget,
@ -1484,13 +1474,13 @@ gtk_list_base_get_rubberband_coords (GtkListBase *self,
}
else
{
GdkRectangle area;
guint pos = gtk_list_item_tracker_get_position (priv->item_manager, priv->rubberband->start_tracker);
if (gtk_list_base_get_allocation_along (self, pos, &y1, &y2) &&
gtk_list_base_get_allocation_across (self, pos, &x1, &x2))
if (gtk_list_base_get_allocation (self, pos, &area))
{
x1 += x2 * priv->rubberband->start_align_across;
y1 += y2 * priv->rubberband->start_align_along;
x1 = area.x + area.width * priv->rubberband->start_align_across;
y1 = area.y + area.height * priv->rubberband->start_align_along;
}
else
{
@ -1511,7 +1501,7 @@ gtk_list_base_get_rubberband_coords (GtkListBase *self,
return TRUE;
}
void
static void
gtk_list_base_allocate_rubberband (GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
@ -1648,17 +1638,17 @@ static void
gtk_list_base_stop_rubberband (GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
GtkListItemManagerItem *item;
GtkListTile *tile;
if (!priv->rubberband)
return;
for (item = gtk_list_item_manager_get_first (priv->item_manager);
item != NULL;
item = gtk_rb_tree_node_get_next (item))
for (tile = gtk_list_item_manager_get_first (priv->item_manager);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
if (item->widget)
gtk_widget_unset_state_flags (item->widget, GTK_STATE_FLAG_ACTIVE);
if (tile->widget)
gtk_widget_unset_state_flags (tile->widget, GTK_STATE_FLAG_ACTIVE);
}
gtk_list_item_tracker_free (priv->item_manager, priv->rubberband->start_tracker);
@ -1673,7 +1663,7 @@ static void
gtk_list_base_update_rubberband_selection (GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
GtkListItemManagerItem *item;
GtkListTile *tile;
GdkRectangle rect;
guint pos;
GtkBitset *rubberband_selection;
@ -1684,19 +1674,19 @@ gtk_list_base_update_rubberband_selection (GtkListBase *self)
rubberband_selection = gtk_list_base_get_items_in_rect (self, &rect);
pos = 0;
for (item = gtk_list_item_manager_get_first (priv->item_manager);
item != NULL;
item = gtk_rb_tree_node_get_next (item))
for (tile = gtk_list_item_manager_get_first (priv->item_manager);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
if (item->widget)
if (tile->widget)
{
if (gtk_bitset_contains (rubberband_selection, pos))
gtk_widget_set_state_flags (item->widget, GTK_STATE_FLAG_ACTIVE, FALSE);
gtk_widget_set_state_flags (tile->widget, GTK_STATE_FLAG_ACTIVE, FALSE);
else
gtk_widget_unset_state_flags (item->widget, GTK_STATE_FLAG_ACTIVE);
gtk_widget_unset_state_flags (tile->widget, GTK_STATE_FLAG_ACTIVE);
}
pos += item->n_items;
pos += tile->n_items;
}
gtk_bitset_unref (rubberband_selection);
@ -1844,6 +1834,14 @@ gtk_list_base_drag_leave (GtkDropControllerMotion *motion,
remove_autoscroll (GTK_LIST_BASE (widget));
}
static GtkListTile *
gtk_list_base_split_func (gpointer data,
GtkListTile *tile,
guint n_items)
{
return GTK_LIST_BASE_GET_CLASS (data)->split (data, tile, n_items);
}
static void
gtk_list_base_init_real (GtkListBase *self,
GtkListBaseClass *g_class)
@ -1851,12 +1849,11 @@ gtk_list_base_init_real (GtkListBase *self,
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
GtkEventController *controller;
priv->item_manager = gtk_list_item_manager_new_for_size (GTK_WIDGET (self),
g_class->list_item_name,
g_class->list_item_role,
g_class->list_item_size,
g_class->list_item_augment_size,
g_class->list_item_augment_func);
priv->item_manager = gtk_list_item_manager_new (GTK_WIDGET (self),
g_class->list_item_name,
g_class->list_item_role,
gtk_list_base_split_func,
self);
priv->anchor = gtk_list_item_tracker_new (priv->item_manager);
priv->anchor_side_along = GTK_PACK_START;
priv->anchor_side_across = GTK_PACK_START;
@ -1879,7 +1876,7 @@ gtk_list_base_init_real (GtkListBase *self,
gtk_widget_add_controller (GTK_WIDGET (self), controller);
}
static int
static void
gtk_list_base_set_adjustment_values (GtkListBase *self,
GtkOrientation orientation,
int value,
@ -1907,23 +1904,24 @@ gtk_list_base_set_adjustment_values (GtkListBase *self,
g_signal_handlers_unblock_by_func (priv->adjustment[orientation],
gtk_list_base_adjustment_value_changed_cb,
self);
return value;
}
void
gtk_list_base_update_adjustments (GtkListBase *self,
int total_across,
int total_along,
int page_across,
int page_along,
int *across,
int *along)
static void
gtk_list_base_update_adjustments (GtkListBase *self)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
int value_along, value_across, size;
GdkRectangle bounds;
int value_along, value_across;
int page_along, page_across;
guint pos;
gtk_list_item_manager_get_tile_bounds (priv->item_manager, &bounds);
g_assert (bounds.x == 0);
g_assert (bounds.y == 0);
page_across = gtk_widget_get_size (GTK_WIDGET (self), OPPOSITE_ORIENTATION (priv->orientation));
page_along = gtk_widget_get_size (GTK_WIDGET (self), priv->orientation);
pos = gtk_list_item_tracker_get_position (priv->item_manager, priv->anchor);
if (pos == GTK_INVALID_LIST_POSITION)
{
@ -1932,38 +1930,45 @@ gtk_list_base_update_adjustments (GtkListBase *self,
}
else
{
if (gtk_list_base_get_allocation_across (self, pos, &value_across, &size))
GdkRectangle area;
if (gtk_list_base_get_allocation (self, pos, &area))
{
value_across = area.x;
value_along = area.y;
if (priv->anchor_side_across == GTK_PACK_END)
value_across += size;
value_across -= priv->anchor_align_across * page_across;
}
else
{
value_along = 0;
}
if (gtk_list_base_get_allocation_along (self, pos, &value_along, &size))
{
value_across += area.width;
if (priv->anchor_side_along == GTK_PACK_END)
value_along += size;
value_along += area.height;
value_across -= priv->anchor_align_across * page_across;
value_along -= priv->anchor_align_along * page_along;
}
else
{
value_across = 0;
value_along = 0;
}
}
*across = gtk_list_base_set_adjustment_values (self,
OPPOSITE_ORIENTATION (priv->orientation),
value_across,
total_across,
page_across);
*along = gtk_list_base_set_adjustment_values (self,
priv->orientation,
value_along,
total_along,
page_along);
gtk_list_base_set_adjustment_values (self,
OPPOSITE_ORIENTATION (priv->orientation),
value_across,
bounds.width,
page_across);
gtk_list_base_set_adjustment_values (self,
priv->orientation,
value_along,
bounds.height,
page_along);
}
void
gtk_list_base_allocate (GtkListBase *self)
{
gtk_list_base_update_adjustments (self);
gtk_list_base_allocate_children (self);
gtk_list_base_allocate_rubberband (self);
}
GtkScrollablePolicy
@ -2124,14 +2129,14 @@ gtk_list_base_grab_focus_on_item (GtkListBase *self,
gboolean extend)
{
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
GtkListItemManagerItem *item;
GtkListTile *tile;
gboolean success;
item = gtk_list_item_manager_get_nth (priv->item_manager, pos, NULL);
if (item == NULL)
tile = gtk_list_item_manager_get_nth (priv->item_manager, pos, NULL);
if (tile == NULL)
return FALSE;
if (!item->widget)
if (!tile->widget)
{
GtkListItemTracker *tracker = gtk_list_item_tracker_new (priv->item_manager);
@ -2141,16 +2146,16 @@ gtk_list_base_grab_focus_on_item (GtkListBase *self,
* so we create a temporary one. */
gtk_list_item_tracker_set_position (priv->item_manager, tracker, pos, 0, 0);
item = gtk_list_item_manager_get_nth (priv->item_manager, pos, NULL);
g_assert (item->widget);
tile = gtk_list_item_manager_get_nth (priv->item_manager, pos, NULL);
g_assert (tile->widget);
success = gtk_widget_grab_focus (item->widget);
success = gtk_widget_grab_focus (tile->widget);
gtk_list_item_tracker_free (priv->item_manager, tracker);
}
else
{
success = gtk_widget_grab_focus (item->widget);
success = gtk_widget_grab_focus (tile->widget);
}
if (!success)

View File

@ -36,20 +36,14 @@ struct _GtkListBaseClass
const char * list_item_name;
GtkAccessibleRole list_item_role;
gsize list_item_size;
gsize list_item_augment_size;
GtkRbTreeAugmentFunc list_item_augment_func;
void (* adjustment_value_changed) (GtkListBase *self,
GtkOrientation orientation);
gboolean (* get_allocation_along) (GtkListBase *self,
GtkListTile * (* split) (GtkListBase *self,
GtkListTile *tile,
guint n_items);
gboolean (* get_allocation) (GtkListBase *self,
guint pos,
int *offset,
int *size);
gboolean (* get_allocation_across) (GtkListBase *self,
guint pos,
int *offset,
int *size);
GdkRectangle *area);
gboolean (* get_position_from_allocation) (GtkListBase *self,
int across,
int along,
@ -75,13 +69,6 @@ guint gtk_list_base_get_n_items (GtkListBase
GtkSelectionModel * gtk_list_base_get_model (GtkListBase *self);
gboolean gtk_list_base_set_model (GtkListBase *self,
GtkSelectionModel *model);
void gtk_list_base_update_adjustments (GtkListBase *self,
int total_across,
int total_along,
int page_across,
int page_along,
int *across,
int *along);
guint gtk_list_base_get_anchor (GtkListBase *self);
void gtk_list_base_set_anchor (GtkListBase *self,
@ -106,13 +93,7 @@ gboolean gtk_list_base_grab_focus_on_item (GtkListBase
void gtk_list_base_set_enable_rubberband (GtkListBase *self,
gboolean enable);
gboolean gtk_list_base_get_enable_rubberband (GtkListBase *self);
void gtk_list_base_allocate_rubberband (GtkListBase *self);
void gtk_list_base_size_allocate_child (GtkListBase *self,
GtkWidget *child,
int x,
int y,
int width,
int height);
void gtk_list_base_allocate (GtkListBase *self);
#endif /* __GTK_LIST_BASE_PRIVATE_H__ */

View File

@ -39,6 +39,9 @@ struct _GtkListItemManager
GtkRbTree *items;
GSList *trackers;
GtkListTile * (* split_func) (gpointer, GtkListTile *, guint);
gpointer user_data;
};
struct _GtkListItemManagerClass
@ -74,54 +77,70 @@ static void gtk_list_item_manager_release_list_item (GtkListItemMana
GtkWidget *widget);
G_DEFINE_TYPE (GtkListItemManager, gtk_list_item_manager, G_TYPE_OBJECT)
void
static void
potentially_empty_rectangle_union (cairo_rectangle_int_t *self,
const cairo_rectangle_int_t *area)
{
if (area->width <= 0 || area->height <= 0)
return;
if (self->width <= 0 || self->height <= 0)
{
*self = *area;
return;
}
gdk_rectangle_union (self, area, self);
}
static void
gtk_list_item_manager_augment_node (GtkRbTree *tree,
gpointer node_augment,
gpointer node,
gpointer left,
gpointer right)
{
GtkListItemManagerItem *item = node;
GtkListItemManagerItemAugment *aug = node_augment;
GtkListTile *tile = node;
GtkListTileAugment *aug = node_augment;
aug->n_items = item->n_items;
aug->n_items = tile->n_items;
aug->area = tile->area;
if (left)
{
GtkListItemManagerItemAugment *left_aug = gtk_rb_tree_get_augment (tree, left);
GtkListTileAugment *left_aug = gtk_rb_tree_get_augment (tree, left);
aug->n_items += left_aug->n_items;
potentially_empty_rectangle_union (&aug->area, &left_aug->area);
}
if (right)
{
GtkListItemManagerItemAugment *right_aug = gtk_rb_tree_get_augment (tree, right);
GtkListTileAugment *right_aug = gtk_rb_tree_get_augment (tree, right);
aug->n_items += right_aug->n_items;
potentially_empty_rectangle_union (&aug->area, &right_aug->area);
}
}
static void
gtk_list_item_manager_clear_node (gpointer _item)
gtk_list_item_manager_clear_node (gpointer _tile)
{
GtkListItemManagerItem *item G_GNUC_UNUSED = _item;
GtkListTile *tile G_GNUC_UNUSED = _tile;
g_assert (item->widget == NULL);
g_assert (tile->widget == NULL);
}
GtkListItemManager *
gtk_list_item_manager_new_for_size (GtkWidget *widget,
const char *item_css_name,
GtkAccessibleRole item_role,
gsize element_size,
gsize augment_size,
GtkRbTreeAugmentFunc augment_func)
gtk_list_item_manager_new (GtkWidget *widget,
const char *item_css_name,
GtkAccessibleRole item_role,
GtkListTile * (* split_func) (gpointer, GtkListTile *, guint),
gpointer user_data)
{
GtkListItemManager *self;
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
g_return_val_if_fail (element_size >= sizeof (GtkListItemManagerItem), NULL);
g_return_val_if_fail (augment_size >= sizeof (GtkListItemManagerItemAugment), NULL);
self = g_object_new (GTK_TYPE_LIST_ITEM_MANAGER, NULL);
@ -129,22 +148,48 @@ gtk_list_item_manager_new_for_size (GtkWidget *widget,
self->widget = widget;
self->item_css_name = g_intern_string (item_css_name);
self->item_role = item_role;
self->split_func = split_func;
self->user_data = user_data;
self->items = gtk_rb_tree_new_for_size (element_size,
augment_size,
augment_func,
self->items = gtk_rb_tree_new_for_size (sizeof (GtkListTile),
sizeof (GtkListTileAugment),
gtk_list_item_manager_augment_node,
gtk_list_item_manager_clear_node,
NULL);
return self;
}
void
gtk_list_item_manager_get_tile_bounds (GtkListItemManager *self,
GdkRectangle *out_bounds)
{
GtkListTile *tile;
GtkListTileAugment *aug;
tile = gtk_rb_tree_get_root (self->items);
if (tile == NULL)
{
*out_bounds = (GdkRectangle) { 0, 0, 0, 0 };
return;
}
aug = gtk_rb_tree_get_augment (self->items, tile);
*out_bounds = aug->area;
}
gpointer
gtk_list_item_manager_get_first (GtkListItemManager *self)
{
return gtk_rb_tree_get_first (self->items);
}
gpointer
gtk_list_item_manager_get_last (GtkListItemManager *self)
{
return gtk_rb_tree_get_last (self->items);
}
gpointer
gtk_list_item_manager_get_root (GtkListItemManager *self)
{
@ -155,15 +200,15 @@ gtk_list_item_manager_get_root (GtkListItemManager *self)
* gtk_list_item_manager_get_nth:
* @self: a `GtkListItemManager`
* @position: position of the item
* @offset: (out): offset into the returned item
* @offset: (out): offset into the returned tile
*
* Looks up the GtkListItemManagerItem that represents @position.
* Looks up the GtkListTile that represents @position.
*
* If a the returned item represents multiple rows, the @offset into
* the returned item for @position will be set. If the returned item
* If a the returned tile represents multiple rows, the @offset into
* the returned tile for @position will be set. If the returned tile
* represents a row with an existing widget, @offset will always be 0.
*
* Returns: (type GtkListItemManagerItem): the item for @position or
* Returns: (type GtkListTile): the tile for @position or
* %NULL if position is out of range
**/
gpointer
@ -171,48 +216,103 @@ gtk_list_item_manager_get_nth (GtkListItemManager *self,
guint position,
guint *offset)
{
GtkListItemManagerItem *item, *tmp;
GtkListTile *tile, *tmp;
item = gtk_rb_tree_get_root (self->items);
tile = gtk_rb_tree_get_root (self->items);
while (item)
while (tile)
{
tmp = gtk_rb_tree_node_get_left (item);
tmp = gtk_rb_tree_node_get_left (tile);
if (tmp)
{
GtkListItemManagerItemAugment *aug = gtk_rb_tree_get_augment (self->items, tmp);
GtkListTileAugment *aug = gtk_rb_tree_get_augment (self->items, tmp);
if (position < aug->n_items)
{
item = tmp;
tile = tmp;
continue;
}
position -= aug->n_items;
}
if (position < item->n_items)
if (position < tile->n_items)
break;
position -= item->n_items;
position -= tile->n_items;
item = gtk_rb_tree_node_get_right (item);
tile = gtk_rb_tree_node_get_right (tile);
}
if (offset)
*offset = item ? position : 0;
*offset = tile ? position : 0;
return item;
return tile;
}
static GtkListTile *
gtk_list_tile_get_tile_at (GtkListItemManager *self,
GtkListTile *tile,
int x,
int y)
{
GtkListTileAugment *aug;
GtkListTile *subtile;
aug = gtk_list_tile_get_augment (self, tile);
if (!gdk_rectangle_contains_point (&aug->area, x, y))
return NULL;
subtile = gtk_rb_tree_node_get_left (tile);
if (subtile)
{
subtile = gtk_list_tile_get_tile_at (self, subtile, x, y);
if (subtile)
return subtile;
}
if (gdk_rectangle_contains_point (&tile->area, x, y))
return tile;
subtile = gtk_rb_tree_node_get_right (tile);
if (subtile)
{
subtile = gtk_list_tile_get_tile_at (self, subtile, x, y);
if (subtile)
return subtile;
}
return NULL;
}
/*
* gtk_list_item_manager_get_tile_at:
* @self: a GtkListItemManager
* @x: x coordinate of tile
* @y: y coordinate of tile
*
* Finds the tile occupying the coordinates at (x, y). If no
* tile occupies the coordinates (for example, if the tile is out of bounds),
* NULL is returned.
*
* Returns: (nullable): The tile at (x, y) or NULL
**/
GtkListTile *
gtk_list_item_manager_get_tile_at (GtkListItemManager *self,
int x,
int y)
{
return gtk_list_tile_get_tile_at (self, gtk_list_item_manager_get_root (self), x, y);
}
guint
gtk_list_item_manager_get_item_position (GtkListItemManager *self,
gpointer item)
gtk_list_tile_get_position (GtkListItemManager *self,
GtkListTile *tile)
{
GtkListItemManagerItem *parent, *left;
GtkListTile *parent, *left;
int pos;
left = gtk_rb_tree_node_get_left (item);
left = gtk_rb_tree_node_get_left (tile);
if (left)
{
GtkListItemManagerItemAugment *aug = gtk_rb_tree_get_augment (self->items, left);
GtkListTileAugment *aug = gtk_rb_tree_get_augment (self->items, left);
pos = aug->n_items;
}
else
@ -220,33 +320,93 @@ gtk_list_item_manager_get_item_position (GtkListItemManager *self,
pos = 0;
}
for (parent = gtk_rb_tree_node_get_parent (item);
for (parent = gtk_rb_tree_node_get_parent (tile);
parent != NULL;
parent = gtk_rb_tree_node_get_parent (item))
parent = gtk_rb_tree_node_get_parent (tile))
{
left = gtk_rb_tree_node_get_left (parent);
if (left != item)
if (left != tile)
{
if (left)
{
GtkListItemManagerItemAugment *aug = gtk_rb_tree_get_augment (self->items, left);
GtkListTileAugment *aug = gtk_rb_tree_get_augment (self->items, left);
pos += aug->n_items;
}
pos += parent->n_items;
}
item = parent;
tile = parent;
}
return pos;
}
gpointer
gtk_list_item_manager_get_item_augment (GtkListItemManager *self,
gpointer item)
gtk_list_tile_get_augment (GtkListItemManager *self,
GtkListTile *tile)
{
return gtk_rb_tree_get_augment (self->items, item);
return gtk_rb_tree_get_augment (self->items, tile);
}
/*
* gtk_list_tile_set_area:
* @self: the list item manager
* @tile: tile to set area for
* @area: (nullable): area to set or NULL to clear
* the area
*
* Updates the area of the tile.
*
* The area is given in the internal coordinate system,
* so the x/y flip due to orientation and the left/right
* flip for RTL languages will happen later.
*
* This function should only be called from inside size_allocate().
**/
void
gtk_list_tile_set_area (GtkListItemManager *self,
GtkListTile *tile,
const cairo_rectangle_int_t *area)
{
cairo_rectangle_int_t empty_area = { 0, 0, 0, 0 };
if (!area)
area = &empty_area;
if (gdk_rectangle_equal (&tile->area, area))
return;
tile->area = *area;
gtk_rb_tree_node_mark_dirty (tile);
}
void
gtk_list_tile_set_area_position (GtkListItemManager *self,
GtkListTile *tile,
int x,
int y)
{
if (tile->area.x == x && tile->area.y == y)
return;
tile->area.x = x;
tile->area.y = y;
gtk_rb_tree_node_mark_dirty (tile);
}
void
gtk_list_tile_set_area_size (GtkListItemManager *self,
GtkListTile *tile,
int width,
int height)
{
if (tile->area.width == width && tile->area.height == height)
return;
tile->area.width = width;
tile->area.height = height;
gtk_rb_tree_node_mark_dirty (tile);
}
static void
@ -347,37 +507,47 @@ restart:
}
}
static GtkListTile *
gtk_list_item_manager_ensure_split (GtkListItemManager *self,
GtkListTile *tile,
guint n_items)
{
return self->split_func (self->user_data, tile, n_items);
}
static void
gtk_list_item_manager_remove_items (GtkListItemManager *self,
GHashTable *change,
guint position,
guint n_items)
{
GtkListItemManagerItem *item;
GtkListTile *tile, *next;
guint offset;
if (n_items == 0)
return;
item = gtk_list_item_manager_get_nth (self, position, NULL);
tile = gtk_list_item_manager_get_nth (self, position, &offset);
if (offset)
tile = gtk_list_item_manager_ensure_split (self, tile, offset);
while (n_items > 0)
{
if (item->n_items > n_items)
if (tile->n_items > n_items)
{
item->n_items -= n_items;
gtk_rb_tree_node_mark_dirty (item);
n_items = 0;
}
else
{
GtkListItemManagerItem *next = gtk_rb_tree_node_get_next (item);
if (item->widget)
gtk_list_item_manager_release_list_item (self, change, item->widget);
item->widget = NULL;
n_items -= item->n_items;
gtk_rb_tree_remove (self->items, item);
item = next;
gtk_list_item_manager_ensure_split (self, tile, n_items);
g_assert (tile->n_items <= n_items);
}
next = gtk_rb_tree_node_get_next (tile);
if (tile->widget)
gtk_list_item_manager_release_list_item (self, change, tile->widget);
tile->widget = NULL;
n_items -= tile->n_items;
tile->n_items = 0;
gtk_rb_tree_node_mark_dirty (tile);
tile = next;
}
gtk_widget_queue_resize (GTK_WIDGET (self->widget));
@ -388,26 +558,27 @@ gtk_list_item_manager_add_items (GtkListItemManager *self,
guint position,
guint n_items)
{
GtkListItemManagerItem *item;
GtkListTile *tile;
guint offset;
if (n_items == 0)
return;
item = gtk_list_item_manager_get_nth (self, position, &offset);
tile = gtk_list_item_manager_get_nth (self, position, &offset);
if (offset)
tile = gtk_list_item_manager_ensure_split (self, tile, offset);
if (item == NULL || item->widget)
item = gtk_rb_tree_insert_before (self->items, item);
item->n_items += n_items;
gtk_rb_tree_node_mark_dirty (item);
tile = gtk_rb_tree_insert_before (self->items, tile);
tile->n_items = n_items;
gtk_rb_tree_node_mark_dirty (tile);
gtk_widget_queue_resize (GTK_WIDGET (self->widget));
}
static gboolean
gtk_list_item_manager_merge_list_items (GtkListItemManager *self,
GtkListItemManagerItem *first,
GtkListItemManagerItem *second)
gtk_list_item_manager_merge_list_items (GtkListItemManager *self,
GtkListTile *first,
GtkListTile *second)
{
if (first->widget || second->widget)
return FALSE;
@ -419,11 +590,86 @@ gtk_list_item_manager_merge_list_items (GtkListItemManager *self,
return TRUE;
}
/*
* gtk_list_tile_split:
* @self: the listitemmanager
* @tile: a tile to split into two
* @n_items: nuber of items to keep in tile
*
* Splits the given tile into two tiles. The original
* tile will remain with @n_items items, the remaining
* items will be given to the new tile, which will be
* nserted after the tile.
*
* It is valid for either tile to have 0 items after
* the split.
*
* Returns: The new tile
**/
GtkListTile *
gtk_list_tile_split (GtkListItemManager *self,
GtkListTile *tile,
guint n_items)
{
GtkListTile *result;
g_assert (n_items <= tile->n_items);
result = gtk_rb_tree_insert_after (self->items, tile);
result->n_items = tile->n_items - n_items;
tile->n_items = n_items;
gtk_rb_tree_node_mark_dirty (tile);
return result;
}
/*
* gtk_list_tile_gc:
* @self: the listitemmanager
* @tile: a tile
*
* Tries to get rid of tiles when they aren't needed anymore,
* either because their referenced listitems were deleted or
* because they can be merged with the next item(s).
*
* Note that this only looks forward, but never backward.
*
* Returns: The next tile
**/
GtkListTile *
gtk_list_tile_gc (GtkListItemManager *self,
GtkListTile *tile)
{
GtkListTile *next;
while (tile)
{
next = gtk_rb_tree_node_get_next (tile);
if (tile->n_items == 0)
{
gtk_rb_tree_remove (self->items, tile);
tile = next;
continue;
}
if (next == NULL)
break;
if (gtk_list_item_manager_merge_list_items (self, tile, next))
continue;
break;
}
return tile;
}
static void
gtk_list_item_manager_release_items (GtkListItemManager *self,
GQueue *released)
{
GtkListItemManagerItem *item, *prev, *next;
GtkListTile *tile;
guint position, i, n_items, query_n_items;
gboolean tracked;
@ -439,37 +685,18 @@ gtk_list_item_manager_release_items (GtkListItemManager *self,
continue;
}
item = gtk_list_item_manager_get_nth (self, position, &i);
tile = gtk_list_item_manager_get_nth (self, position, &i);
i = position - i;
while (i < position + query_n_items)
{
g_assert (item != NULL);
if (item->widget)
g_assert (tile != NULL);
if (tile->widget)
{
g_queue_push_tail (released, item->widget);
item->widget = NULL;
i++;
prev = gtk_rb_tree_node_get_previous (item);
if (prev && gtk_list_item_manager_merge_list_items (self, prev, item))
item = prev;
next = gtk_rb_tree_node_get_next (item);
if (next && next->widget == NULL)
{
i += next->n_items;
if (!gtk_list_item_manager_merge_list_items (self, next, item))
g_assert_not_reached ();
item = gtk_rb_tree_node_get_next (next);
}
else
{
item = next;
}
}
else
{
i += item->n_items;
item = gtk_rb_tree_node_get_next (item);
g_queue_push_tail (released, tile->widget);
tile->widget = NULL;
}
i += tile->n_items;
tile = gtk_rb_tree_node_get_next (tile);
}
position += query_n_items;
}
@ -480,7 +707,7 @@ gtk_list_item_manager_ensure_items (GtkListItemManager *self,
GHashTable *change,
guint update_start)
{
GtkListItemManagerItem *item, *new_item;
GtkListTile *tile, *other_tile;
GtkWidget *widget, *insert_after;
guint position, i, n_items, query_n_items, offset;
GQueue released = G_QUEUE_INIT;
@ -503,70 +730,61 @@ gtk_list_item_manager_ensure_items (GtkListItemManager *self,
continue;
}
item = gtk_list_item_manager_get_nth (self, position, &offset);
for (new_item = item;
new_item && new_item->widget == NULL;
new_item = gtk_rb_tree_node_get_previous (new_item))
tile = gtk_list_item_manager_get_nth (self, position, &offset);
for (other_tile = tile;
other_tile && other_tile->widget == NULL;
other_tile = gtk_rb_tree_node_get_previous (other_tile))
{ /* do nothing */ }
insert_after = new_item ? new_item->widget : NULL;
insert_after = other_tile ? other_tile->widget : NULL;
if (offset > 0)
{
g_assert (item != NULL);
new_item = gtk_rb_tree_insert_before (self->items, item);
new_item->n_items = offset;
item->n_items -= offset;
gtk_rb_tree_node_mark_dirty (item);
}
tile = gtk_list_item_manager_ensure_split (self, tile, offset);
for (i = 0; i < query_n_items; i++)
{
g_assert (item != NULL);
if (item->n_items > 1)
{
new_item = gtk_rb_tree_insert_before (self->items, item);
new_item->n_items = 1;
item->n_items--;
gtk_rb_tree_node_mark_dirty (item);
}
else
{
new_item = item;
item = gtk_rb_tree_node_get_next (item);
}
if (new_item->widget == NULL)
g_assert (tile != NULL);
while (tile->n_items == 0)
tile = gtk_rb_tree_node_get_next (tile);
if (tile->n_items > 1)
gtk_list_item_manager_ensure_split (self, tile, 1);
if (tile->widget == NULL)
{
if (change)
{
new_item->widget = gtk_list_item_manager_try_reacquire_list_item (self,
change,
position + i,
insert_after);
tile->widget = gtk_list_item_manager_try_reacquire_list_item (self,
change,
position + i,
insert_after);
}
if (new_item->widget == NULL)
if (tile->widget == NULL)
{
new_item->widget = g_queue_pop_head (&released);
if (new_item->widget)
tile->widget = g_queue_pop_head (&released);
if (tile->widget)
{
gtk_list_item_manager_move_list_item (self,
new_item->widget,
tile->widget,
position + i,
insert_after);
}
else
{
new_item->widget = gtk_list_item_manager_acquire_list_item (self,
position + i,
insert_after);
tile->widget = gtk_list_item_manager_acquire_list_item (self,
position + i,
insert_after);
}
}
}
else
{
if (update_start <= position + i)
gtk_list_item_manager_update_list_item (self, new_item->widget, position + i);
gtk_list_item_manager_update_list_item (self, tile->widget, position + i);
}
insert_after = new_item->widget;
insert_after = tile->widget;
tile = gtk_rb_tree_node_get_next (tile);
}
position += query_n_items;
}
@ -608,17 +826,17 @@ gtk_list_item_manager_model_items_changed_cb (GListModel *model,
* trying to find where it moved */
if (l)
{
GtkListItemManagerItem *item, *new_item;
GtkListTile *tile, *new_tile;
GtkWidget *insert_after;
guint i, offset;
item = gtk_list_item_manager_get_nth (self, position, &offset);
for (new_item = item ? gtk_rb_tree_node_get_previous (item) : gtk_rb_tree_get_last (self->items);
new_item && new_item->widget == NULL;
new_item = gtk_rb_tree_node_get_previous (new_item))
tile = gtk_list_item_manager_get_nth (self, position, &offset);
for (new_tile = tile ? gtk_rb_tree_node_get_previous (tile) : gtk_rb_tree_get_last (self->items);
new_tile && new_tile->widget == NULL;
new_tile = gtk_rb_tree_node_get_previous (new_tile))
{ }
if (new_item)
insert_after = new_item->widget;
if (new_tile)
insert_after = new_tile->widget;
else
insert_after = NULL; /* we're at the start */
@ -636,29 +854,24 @@ gtk_list_item_manager_model_items_changed_cb (GListModel *model,
continue;
}
while (offset >= tile->n_items)
{
offset -= tile->n_items;
tile = gtk_rb_tree_node_get_next (tile);
}
if (offset > 0)
{
new_item = gtk_rb_tree_insert_before (self->items, item);
new_item->n_items = offset;
item->n_items -= offset;
tile = gtk_list_item_manager_ensure_split (self, tile, offset);
offset = 0;
gtk_rb_tree_node_mark_dirty (item);
}
if (item->n_items == 1)
{
new_item = item;
item = gtk_rb_tree_node_get_next (item);
}
new_tile = tile;
if (tile->n_items == 1)
tile = gtk_rb_tree_node_get_next (tile);
else
{
new_item = gtk_rb_tree_insert_before (self->items, item);
new_item->n_items = 1;
item->n_items--;
gtk_rb_tree_node_mark_dirty (item);
}
tile = gtk_list_item_manager_ensure_split (self, tile, 1);
new_item->widget = widget;
new_tile->widget = widget;
insert_after = widget;
}
}
@ -719,16 +932,16 @@ gtk_list_item_manager_model_items_changed_cb (GListModel *model,
for (l = self->trackers; l; l = l->next)
{
GtkListItemTracker *tracker = l->data;
GtkListItemManagerItem *item;
GtkListTile *tile;
if (tracker->widget != NULL ||
tracker->position == GTK_INVALID_LIST_POSITION)
continue;
item = gtk_list_item_manager_get_nth (self, tracker->position, NULL);
g_assert (item != NULL);
g_assert (item->widget);
tracker->widget = GTK_LIST_ITEM_WIDGET (item->widget);
tile = gtk_list_item_manager_get_nth (self, tracker->position, NULL);
g_assert (tile != NULL);
g_assert (tile->widget);
tracker->widget = GTK_LIST_ITEM_WIDGET (tile->widget);
}
g_hash_table_unref (change);
@ -742,28 +955,28 @@ gtk_list_item_manager_model_selection_changed_cb (GListModel *model,
guint n_items,
GtkListItemManager *self)
{
GtkListItemManagerItem *item;
GtkListTile *tile;
guint offset;
item = gtk_list_item_manager_get_nth (self, position, &offset);
tile = gtk_list_item_manager_get_nth (self, position, &offset);
if (offset)
{
position += item->n_items - offset;
if (item->n_items - offset > n_items)
position += tile->n_items - offset;
if (tile->n_items - offset > n_items)
n_items = 0;
else
n_items -= item->n_items - offset;
item = gtk_rb_tree_node_get_next (item);
n_items -= tile->n_items - offset;
tile = gtk_rb_tree_node_get_next (tile);
}
while (n_items > 0)
{
if (item->widget)
gtk_list_item_manager_update_list_item (self, item->widget, position);
position += item->n_items;
n_items -= MIN (n_items, item->n_items);
item = gtk_rb_tree_node_get_next (item);
if (tile->widget)
gtk_list_item_manager_update_list_item (self, tile->widget, position);
position += tile->n_items;
n_items -= MIN (n_items, tile->n_items);
tile = gtk_rb_tree_node_get_next (tile);
}
}
@ -842,14 +1055,14 @@ gtk_list_item_manager_set_factory (GtkListItemManager *self,
for (l = self->trackers; l; l = l->next)
{
GtkListItemTracker *tracker = l->data;
GtkListItemManagerItem *item;
GtkListTile *tile;
if (tracker->widget == NULL)
continue;
item = gtk_list_item_manager_get_nth (self, tracker->position, NULL);
g_assert (item);
tracker->widget = GTK_LIST_ITEM_WIDGET (item->widget);
tile = gtk_list_item_manager_get_nth (self, tracker->position, NULL);
g_assert (tile);
tracker->widget = GTK_LIST_ITEM_WIDGET (tile->widget);
}
}
@ -1087,18 +1300,18 @@ void
gtk_list_item_manager_set_single_click_activate (GtkListItemManager *self,
gboolean single_click_activate)
{
GtkListItemManagerItem *item;
GtkListTile *tile;
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
self->single_click_activate = single_click_activate;
for (item = gtk_rb_tree_get_first (self->items);
item != NULL;
item = gtk_rb_tree_node_get_next (item))
for (tile = gtk_rb_tree_get_first (self->items);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
if (item->widget)
gtk_list_item_widget_set_single_click_activate (GTK_LIST_ITEM_WIDGET (item->widget), single_click_activate);
if (tile->widget)
gtk_list_item_widget_set_single_click_activate (GTK_LIST_ITEM_WIDGET (tile->widget), single_click_activate);
}
}
@ -1148,7 +1361,7 @@ gtk_list_item_tracker_set_position (GtkListItemManager *self,
guint n_before,
guint n_after)
{
GtkListItemManagerItem *item;
GtkListTile *tile;
guint n_items;
gtk_list_item_tracker_unset_position (self, tracker);
@ -1166,9 +1379,9 @@ gtk_list_item_tracker_set_position (GtkListItemManager *self,
gtk_list_item_manager_ensure_items (self, NULL, G_MAXUINT);
item = gtk_list_item_manager_get_nth (self, position, NULL);
if (item)
tracker->widget = GTK_LIST_ITEM_WIDGET (item->widget);
tile = gtk_list_item_manager_get_nth (self, position, NULL);
if (tile)
tracker->widget = GTK_LIST_ITEM_WIDGET (tile->widget);
gtk_widget_queue_resize (self->widget);
}

View File

@ -39,47 +39,67 @@ G_BEGIN_DECLS
typedef struct _GtkListItemManager GtkListItemManager;
typedef struct _GtkListItemManagerClass GtkListItemManagerClass;
typedef struct _GtkListItemManagerItem GtkListItemManagerItem; /* sorry */
typedef struct _GtkListItemManagerItemAugment GtkListItemManagerItemAugment;
typedef struct _GtkListTile GtkListTile;
typedef struct _GtkListTileAugment GtkListTileAugment;
typedef struct _GtkListItemTracker GtkListItemTracker;
struct _GtkListItemManagerItem
struct _GtkListTile
{
GtkWidget *widget;
guint n_items;
/* area occupied by tile. May be empty if tile has no allcoation */
cairo_rectangle_int_t area;
};
struct _GtkListItemManagerItemAugment
struct _GtkListTileAugment
{
guint n_items;
/* union of all areas of tile and children */
cairo_rectangle_int_t area;
};
GType gtk_list_item_manager_get_type (void) G_GNUC_CONST;
GtkListItemManager * gtk_list_item_manager_new_for_size (GtkWidget *widget,
GtkListItemManager * gtk_list_item_manager_new (GtkWidget *widget,
const char *item_css_name,
GtkAccessibleRole item_role,
gsize element_size,
gsize augment_size,
GtkRbTreeAugmentFunc augment_func);
#define gtk_list_item_manager_new(widget, item_css_name, type, augment_type, augment_func) \
gtk_list_item_manager_new_for_size (widget, item_css_name, sizeof (type), sizeof (augment_type), (augment_func))
GtkListTile * (* split_func) (gpointer, GtkListTile *, guint),
gpointer user_data);
void gtk_list_item_manager_augment_node (GtkRbTree *tree,
gpointer node_augment,
gpointer node,
gpointer left,
gpointer right);
void gtk_list_item_manager_get_tile_bounds (GtkListItemManager *self,
GdkRectangle *out_bounds);
gpointer gtk_list_item_manager_get_root (GtkListItemManager *self);
gpointer gtk_list_item_manager_get_first (GtkListItemManager *self);
gpointer gtk_list_item_manager_get_last (GtkListItemManager *self);
gpointer gtk_list_item_manager_get_nth (GtkListItemManager *self,
guint position,
guint *offset);
guint gtk_list_item_manager_get_item_position (GtkListItemManager *self,
gpointer item);
gpointer gtk_list_item_manager_get_item_augment (GtkListItemManager *self,
gpointer item);
GtkListTile * gtk_list_item_manager_get_tile_at (GtkListItemManager *self,
int x,
int y);
guint gtk_list_tile_get_position (GtkListItemManager *self,
GtkListTile *tile);
gpointer gtk_list_tile_get_augment (GtkListItemManager *self,
GtkListTile *tile);
void gtk_list_tile_set_area (GtkListItemManager *self,
GtkListTile *tile,
const cairo_rectangle_int_t *area);
void gtk_list_tile_set_area_position (GtkListItemManager *self,
GtkListTile *tile,
int x,
int y);
void gtk_list_tile_set_area_size (GtkListItemManager *self,
GtkListTile *tile,
int width,
int height);
GtkListTile * gtk_list_tile_split (GtkListItemManager *self,
GtkListTile *tile,
guint n_items);
GtkListTile * gtk_list_tile_gc (GtkListItemManager *self,
GtkListTile *tile);
void gtk_list_item_manager_set_factory (GtkListItemManager *self,
GtkListItemFactory *factory);

View File

@ -141,21 +141,6 @@
* items use the %GTK_ACCESSIBLE_ROLE_LIST_ITEM role.
*/
typedef struct _ListRow ListRow;
typedef struct _ListRowAugment ListRowAugment;
struct _ListRow
{
GtkListItemManagerItem parent;
guint height; /* per row */
};
struct _ListRowAugment
{
GtkListItemManagerItemAugment parent;
guint height; /* total */
};
enum
{
PROP_0,
@ -181,188 +166,88 @@ static guint signals[LAST_SIGNAL] = { 0 };
static void G_GNUC_UNUSED
dump (GtkListView *self)
{
ListRow *row;
GtkListTile *tile;
guint n_widgets, n_list_rows;
n_widgets = 0;
n_list_rows = 0;
//g_print ("ANCHOR: %u - %u\n", self->anchor_start, self->anchor_end);
for (row = gtk_list_item_manager_get_first (self->item_manager);
row;
row = gtk_rb_tree_node_get_next (row))
for (tile = gtk_list_item_manager_get_first (self->item_manager);
tile;
tile = gtk_rb_tree_node_get_next (tile))
{
if (row->parent.widget)
if (tile->widget)
n_widgets++;
n_list_rows++;
g_print (" %4u%s (%upx)\n", row->parent.n_items, row->parent.widget ? " (widget)" : "", row->height);
g_print (" %4u%s %d,%d,%d,%d\n", tile->n_items, tile->widget ? " (widget)" : "",
tile->area.x, tile->area.y, tile->area.width, tile->area.height);
}
g_print (" => %u widgets in %u list rows\n", n_widgets, n_list_rows);
}
static void
list_row_augment (GtkRbTree *tree,
gpointer node_augment,
gpointer node,
gpointer left,
gpointer right)
{
ListRow *row = node;
ListRowAugment *aug = node_augment;
gtk_list_item_manager_augment_node (tree, node_augment, node, left, right);
aug->height = row->height * row->parent.n_items;
if (left)
{
ListRowAugment *left_aug = gtk_rb_tree_get_augment (tree, left);
aug->height += left_aug->height;
}
if (right)
{
ListRowAugment *right_aug = gtk_rb_tree_get_augment (tree, right);
aug->height += right_aug->height;
}
}
static ListRow *
gtk_list_view_get_row_at_y (GtkListView *self,
int y,
int *offset)
{
ListRow *row, *tmp;
row = gtk_list_item_manager_get_root (self->item_manager);
while (row)
{
tmp = gtk_rb_tree_node_get_left (row);
if (tmp)
{
ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, tmp);
if (y < aug->height)
{
row = tmp;
continue;
}
y -= aug->height;
}
if (y < row->height * row->parent.n_items)
break;
y -= row->height * row->parent.n_items;
row = gtk_rb_tree_node_get_right (row);
}
if (offset)
*offset = row ? y : 0;
return row;
}
static int
list_row_get_y (GtkListView *self,
ListRow *row)
{
ListRow *parent, *left;
int y;
left = gtk_rb_tree_node_get_left (row);
if (left)
{
ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, left);
y = aug->height;
}
else
y = 0;
for (parent = gtk_rb_tree_node_get_parent (row);
parent != NULL;
parent = gtk_rb_tree_node_get_parent (row))
{
left = gtk_rb_tree_node_get_left (parent);
if (left != row)
{
if (left)
{
ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, left);
y += aug->height;
}
y += parent->height * parent->parent.n_items;
}
row = parent;
}
return y ;
}
static int
gtk_list_view_get_list_height (GtkListView *self)
{
ListRow *row;
ListRowAugment *aug;
GtkListTile *tile;
GtkListTileAugment *aug;
row = gtk_list_item_manager_get_root (self->item_manager);
if (row == NULL)
tile = gtk_list_item_manager_get_root (self->item_manager);
if (tile == NULL)
return 0;
aug = gtk_list_item_manager_get_item_augment (self->item_manager, row);
return aug->height;
aug = gtk_list_tile_get_augment (self->item_manager, tile);
return aug->area.height;
}
static GtkListTile *
gtk_list_view_split (GtkListBase *base,
GtkListTile *tile,
guint n_items)
{
GtkListView *self = GTK_LIST_VIEW (base);
GtkListTile *new_tile;
guint row_height;
row_height = tile->area.height / tile->n_items;
new_tile = gtk_list_tile_split (self->item_manager, tile, n_items);
gtk_list_tile_set_area_size (self->item_manager,
tile,
tile->area.width,
row_height * tile->n_items);
gtk_list_tile_set_area (self->item_manager,
new_tile,
&(GdkRectangle) {
tile->area.x,
tile->area.y + tile->area.height,
tile->area.width,
row_height * new_tile->n_items
});
return new_tile;
}
static gboolean
gtk_list_view_get_allocation_along (GtkListBase *base,
guint pos,
int *offset,
int *size)
gtk_list_view_get_allocation (GtkListBase *base,
guint pos,
GdkRectangle *area)
{
GtkListView *self = GTK_LIST_VIEW (base);
ListRow *row;
guint skip;
int y;
GtkListTile *tile;
guint offset;
row = gtk_list_item_manager_get_nth (self->item_manager, pos, &skip);
if (row == NULL)
{
if (offset)
*offset = 0;
if (size)
*size = 0;
return FALSE;
}
y = list_row_get_y (self, row);
y += skip * row->height;
tile = gtk_list_item_manager_get_nth (self->item_manager, pos, &offset);
if (tile == NULL)
return FALSE;
*area = tile->area;
if (tile->n_items)
area->height /= tile->n_items;
if (offset)
*offset = y;
if (size)
*size = row->height;
area->y += offset * area->height;
return TRUE;
}
static gboolean
gtk_list_view_get_allocation_across (GtkListBase *base,
guint pos,
int *offset,
int *size)
{
GtkListView *self = GTK_LIST_VIEW (base);
if (offset)
*offset = 0;
if (size)
*size = self->list_width;
return TRUE;
return area->width > 0 && area->height > 0;
}
static GtkBitset *
@ -372,7 +257,7 @@ gtk_list_view_get_items_in_rect (GtkListBase *base,
GtkListView *self = GTK_LIST_VIEW (base);
guint first, last, n_items;
GtkBitset *result;
ListRow *row;
GtkListTile *tile;
result = gtk_bitset_new_empty ();
@ -383,14 +268,14 @@ gtk_list_view_get_items_in_rect (GtkListBase *base,
if (n_items == 0)
return result;
row = gtk_list_view_get_row_at_y (self, rect->y, NULL);
if (row)
first = gtk_list_item_manager_get_item_position (self->item_manager, row);
tile = gtk_list_item_manager_get_tile_at (self->item_manager, 0, rect->y);
if (tile)
first = gtk_list_tile_get_position (self->item_manager, tile);
else
first = rect->y < 0 ? 0 : n_items - 1;
row = gtk_list_view_get_row_at_y (self, rect->y + rect->height, NULL);
if (row)
last = gtk_list_item_manager_get_item_position (self->item_manager, row);
tile = gtk_list_item_manager_get_tile_at (self->item_manager, 0, rect->y + rect->height);
if (tile)
last = gtk_list_tile_get_position (self->item_manager, tile);
else
last = rect->y + rect->height < 0 ? 0 : n_items - 1;
@ -415,34 +300,42 @@ gtk_list_view_move_focus_along (GtkListBase *base,
static gboolean
gtk_list_view_get_position_from_allocation (GtkListBase *base,
int across,
int along,
int x,
int y,
guint *pos,
cairo_rectangle_int_t *area)
{
GtkListView *self = GTK_LIST_VIEW (base);
ListRow *row;
int remaining;
GtkListTile *tile;
int row_height, tile_pos;
if (across >= self->list_width)
tile = gtk_list_item_manager_get_tile_at (self->item_manager, x, y);
if (tile == NULL)
return FALSE;
along = CLAMP (along, 0, gtk_list_view_get_list_height (self) - 1);
while (tile && tile->n_items == 0)
tile = gtk_rb_tree_node_get_previous (tile);
if (tile == NULL)
{
tile = gtk_list_item_manager_get_first (self->item_manager);
while (tile && tile->n_items == 0)
tile = gtk_rb_tree_node_get_next (tile);
if (tile == NULL)
return FALSE;
}
row = gtk_list_view_get_row_at_y (self, along, &remaining);
if (row == NULL)
return FALSE;
*pos = gtk_list_tile_get_position (self->item_manager, tile);
row_height = (tile->area.height / tile->n_items);
tile_pos = (y - tile->area.y) / row_height;
*pos = gtk_list_item_manager_get_item_position (self->item_manager, row);
g_assert (remaining < row->height * row->parent.n_items);
*pos += remaining / row->height;
*pos += tile_pos;
if (area)
{
area->x = 0;
area->width = self->list_width;
area->y = along - remaining % row->height;
area->height = row->height;
area->x = tile->area.x;
area->width = tile->area.width;
area->y = tile->area.y + tile_pos * row_height;
area->height = row_height;
}
return TRUE;
@ -483,7 +376,7 @@ gtk_list_view_measure_across (GtkWidget *widget,
int *natural)
{
GtkListView *self = GTK_LIST_VIEW (widget);
ListRow *row;
GtkListTile *tile;
int min, nat, child_min, child_nat;
/* XXX: Figure out how to split a given height into per-row heights.
* Good luck! */
@ -492,15 +385,15 @@ gtk_list_view_measure_across (GtkWidget *widget,
min = 0;
nat = 0;
for (row = gtk_list_item_manager_get_first (self->item_manager);
row != NULL;
row = gtk_rb_tree_node_get_next (row))
for (tile = gtk_list_item_manager_get_first (self->item_manager);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
/* ignore unavailable rows */
if (row->parent.widget == NULL)
if (tile->widget == NULL)
continue;
gtk_widget_measure (row->parent.widget,
gtk_widget_measure (tile->widget,
orientation, for_size,
&child_min, &child_nat, NULL, NULL);
min = MAX (min, child_min);
@ -519,7 +412,7 @@ gtk_list_view_measure_list (GtkWidget *widget,
int *natural)
{
GtkListView *self = GTK_LIST_VIEW (widget);
ListRow *row;
GtkListTile *tile;
int min, nat, child_min, child_nat;
GArray *min_heights, *nat_heights;
guint n_unknown;
@ -530,13 +423,13 @@ gtk_list_view_measure_list (GtkWidget *widget,
min = 0;
nat = 0;
for (row = gtk_list_item_manager_get_first (self->item_manager);
row != NULL;
row = gtk_rb_tree_node_get_next (row))
for (tile = gtk_list_item_manager_get_first (self->item_manager);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
if (row->parent.widget)
if (tile->widget)
{
gtk_widget_measure (row->parent.widget,
gtk_widget_measure (tile->widget,
orientation, for_size,
&child_min, &child_nat, NULL, NULL);
g_array_append_val (min_heights, child_min);
@ -546,7 +439,7 @@ gtk_list_view_measure_list (GtkWidget *widget,
}
else
{
n_unknown += row->parent.n_items;
n_unknown += tile->n_items;
}
}
@ -586,10 +479,9 @@ gtk_list_view_size_allocate (GtkWidget *widget,
int baseline)
{
GtkListView *self = GTK_LIST_VIEW (widget);
ListRow *row;
GtkListTile *tile;
GArray *heights;
int min, nat, row_height;
int x, y;
int min, nat, row_height, y, list_width;
GtkOrientation orientation, opposite_orientation;
GtkScrollablePolicy scroll_policy, opposite_scroll_policy;
@ -599,9 +491,10 @@ gtk_list_view_size_allocate (GtkWidget *widget,
opposite_scroll_policy = gtk_list_base_get_scroll_policy (GTK_LIST_BASE (self), opposite_orientation);
/* step 0: exit early if list is empty */
if (gtk_list_item_manager_get_root (self->item_manager) == NULL)
tile = gtk_list_tile_gc (self->item_manager, gtk_list_item_manager_get_first (self->item_manager));
if (tile == NULL)
{
gtk_list_base_update_adjustments (GTK_LIST_BASE (self), 0, 0, 0, 0, &x, &y);
gtk_list_base_allocate (GTK_LIST_BASE (self));
return;
}
@ -609,85 +502,51 @@ gtk_list_view_size_allocate (GtkWidget *widget,
gtk_list_view_measure_across (widget, opposite_orientation,
-1,
&min, &nat);
self->list_width = orientation == GTK_ORIENTATION_VERTICAL ? width : height;
list_width = orientation == GTK_ORIENTATION_VERTICAL ? width : height;
if (opposite_scroll_policy == GTK_SCROLL_MINIMUM)
self->list_width = MAX (min, self->list_width);
list_width = MAX (min, list_width);
else
self->list_width = MAX (nat, self->list_width);
list_width = MAX (nat, list_width);
/* step 2: determine height of known list items */
/* step 2: determine height of known list items and gc the list */
heights = g_array_new (FALSE, FALSE, sizeof (int));
for (row = gtk_list_item_manager_get_first (self->item_manager);
row != NULL;
row = gtk_rb_tree_node_get_next (row))
for (;
tile != NULL;
tile = gtk_list_tile_gc (self->item_manager, gtk_rb_tree_node_get_next (tile)))
{
if (row->parent.widget == NULL)
if (tile->widget == NULL)
continue;
gtk_widget_measure (row->parent.widget, orientation,
self->list_width,
gtk_widget_measure (tile->widget, orientation,
list_width,
&min, &nat, NULL, NULL);
if (scroll_policy == GTK_SCROLL_MINIMUM)
row_height = min;
else
row_height = nat;
if (row->height != row_height)
{
row->height = row_height;
gtk_rb_tree_node_mark_dirty (row);
}
gtk_list_tile_set_area_size (self->item_manager, tile, list_width, row_height);
g_array_append_val (heights, row_height);
}
/* step 3: determine height of unknown items */
/* step 3: determine height of unknown items and set the positions */
row_height = gtk_list_view_get_unknown_row_height (self, heights);
g_array_free (heights, TRUE);
for (row = gtk_list_item_manager_get_first (self->item_manager);
row != NULL;
row = gtk_rb_tree_node_get_next (row))
y = 0;
for (tile = gtk_list_item_manager_get_first (self->item_manager);
tile != NULL;
tile = gtk_rb_tree_node_get_next (tile))
{
if (row->parent.widget)
continue;
gtk_list_tile_set_area_position (self->item_manager, tile, 0, y);
if (tile->widget == NULL)
gtk_list_tile_set_area_size (self->item_manager, tile, list_width, row_height * tile->n_items);
if (row->height != row_height)
{
row->height = row_height;
gtk_rb_tree_node_mark_dirty (row);
}
y += tile->area.height;
}
/* step 3: update the adjustments */
gtk_list_base_update_adjustments (GTK_LIST_BASE (self),
self->list_width,
gtk_list_view_get_list_height (self),
gtk_widget_get_size (widget, opposite_orientation),
gtk_widget_get_size (widget, orientation),
&x, &y);
x = -x;
y = -y;
/* step 4: actually allocate the widgets */
for (row = gtk_list_item_manager_get_first (self->item_manager);
row != NULL;
row = gtk_rb_tree_node_get_next (row))
{
if (row->parent.widget)
{
gtk_list_base_size_allocate_child (GTK_LIST_BASE (self),
row->parent.widget,
x,
y,
self->list_width,
row->height);
}
y += row->height * row->parent.n_items;
}
gtk_list_base_allocate_rubberband (GTK_LIST_BASE (self));
/* step 4: allocate the rest */
gtk_list_base_allocate (GTK_LIST_BASE (self));
}
static void
@ -799,11 +658,8 @@ gtk_list_view_class_init (GtkListViewClass *klass)
list_base_class->list_item_name = "row";
list_base_class->list_item_role = GTK_ACCESSIBLE_ROLE_LIST_ITEM;
list_base_class->list_item_size = sizeof (ListRow);
list_base_class->list_item_augment_size = sizeof (ListRowAugment);
list_base_class->list_item_augment_func = list_row_augment;
list_base_class->get_allocation_along = gtk_list_view_get_allocation_along;
list_base_class->get_allocation_across = gtk_list_view_get_allocation_across;
list_base_class->split = gtk_list_view_split;
list_base_class->get_allocation = gtk_list_view_get_allocation;
list_base_class->get_items_in_rect = gtk_list_view_get_items_in_rect;
list_base_class->get_position_from_allocation = gtk_list_view_get_position_from_allocation;
list_base_class->move_focus_along = gtk_list_view_move_focus_along;

View File

@ -31,8 +31,6 @@ struct _GtkListView
GtkListItemManager *item_manager;
gboolean show_separators;
int list_width;
};
struct _GtkListViewClass

View File

@ -329,6 +329,8 @@ update_at_context (GtkModelButton *button)
if (was_realized)
gtk_at_context_realize (context);
g_object_unref (context);
}
static void
@ -1199,7 +1201,7 @@ gtk_model_button_class_init (GtkModelButtonClass *class)
* A GIcon that will be used if iconic appearance for the button is
* desired.
*/
properties[PROP_ICON] =
properties[PROP_ICON] =
g_param_spec_object ("icon", NULL, NULL,
G_TYPE_ICON,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);

View File

@ -4033,9 +4033,6 @@ gtk_notebook_insert_notebook_page (GtkNotebook *notebook,
g_signal_connect (controller, "enter", G_CALLBACK (gtk_notebook_tab_drop_enter), page);
g_signal_connect (controller, "leave", G_CALLBACK (gtk_notebook_tab_drop_leave), page);
gtk_widget_add_controller (page->tab_widget, controller);
gtk_accessible_update_property (GTK_ACCESSIBLE (page->tab_widget),
GTK_ACCESSIBLE_PROPERTY_LABEL, _("Tab"),
-1);
page->expand = FALSE;
page->fill = TRUE;
@ -4335,6 +4332,11 @@ gtk_notebook_update_labels (GtkNotebook *notebook)
text = page->tab_text;
else
text = string;
gtk_accessible_update_property (GTK_ACCESSIBLE (page->tab_widget),
GTK_ACCESSIBLE_PROPERTY_LABEL, text,
-1);
if (notebook->show_tabs)
{
if (page->default_tab)

View File

@ -218,7 +218,7 @@ gtk_path_bar_init (GtkPathBar *path_bar)
desktop = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
if (desktop != NULL)
path_bar->desktop_file = g_file_new_for_path (desktop);
else
else
path_bar->desktop_file = NULL;
}
else
@ -306,7 +306,7 @@ update_visibility_up_to_next_root (GtkPathBar *path_bar,
{
gboolean fake_root_found = FALSE;
GList *l;
for (l = start_from_button; l; l = l->next)
{
GtkWidget *button = BUTTON_DATA (l->data)->button;
@ -776,6 +776,7 @@ gtk_path_bar_get_info_callback (GObject *source,
GFileInfo *info;
ButtonData *button_data;
const char *display_name;
gboolean has_is_hidden, has_is_backup;
gboolean is_hidden;
info = g_file_query_info_finish (file, result, NULL);
@ -794,7 +795,10 @@ gtk_path_bar_get_info_callback (GObject *source,
file_info->cancellable = NULL;
display_name = g_file_info_get_display_name (info);
is_hidden = g_file_info_get_is_hidden (info) || g_file_info_get_is_backup (info);
has_is_hidden = g_file_info_has_attribute (info, "standard::is-hidden");
has_is_backup = g_file_info_has_attribute (info, "standard::is-backup");
is_hidden = (has_is_hidden && g_file_info_get_is_hidden (info)) ||
(has_is_backup && g_file_info_get_is_backup (info));
button_data = make_directory_button (file_info->path_bar, display_name,
file_info->file,
@ -879,7 +883,7 @@ _gtk_path_bar_set_file (GtkPathBar *path_bar,
/**
* _gtk_path_bar_up:
* @path_bar: a `GtkPathBar`
*
*
* If the selected button in the pathbar is not the furthest button up (in the
* root direction), act as if the user clicked on the next button up.
**/
@ -906,7 +910,7 @@ _gtk_path_bar_up (GtkPathBar *path_bar)
/**
* _gtk_path_bar_down:
* @path_bar: a `GtkPathBar`
*
*
* If the selected button in the pathbar is not the furthest button down (in the
* leaf direction), act as if the user clicked on the next button down.
**/

View File

@ -45,7 +45,8 @@ gtk_render_node_paintable_paintable_snapshot (GdkPaintable *paintable,
GtkRenderNodePaintable *self = GTK_RENDER_NODE_PAINTABLE (paintable);
if (self->bounds.size.width <= 0 ||
self->bounds.size.height <= 0)
self->bounds.size.height <= 0 ||
self->node == NULL)
return;
gtk_snapshot_save (snapshot);
@ -141,12 +142,12 @@ gtk_render_node_paintable_new (GskRenderNode *node,
{
GtkRenderNodePaintable *self;
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
g_return_val_if_fail (node == NULL || GSK_IS_RENDER_NODE (node), NULL);
g_return_val_if_fail (bounds != NULL, NULL);
self = g_object_new (GTK_TYPE_RENDER_NODE_PAINTABLE, NULL);
self->node = gsk_render_node_ref (node);
self->node = node ? gsk_render_node_ref (node) : NULL;
self->bounds = *bounds;
return GDK_PAINTABLE (self);

View File

@ -34,12 +34,14 @@
#include "gtksearchenginetracker3private.h"
#define N_RESULT_BATCH_ITEMS 50
#define MINER_FS_BUS_NAME "org.freedesktop.Tracker3.Miner.Files"
#define SEARCH_QUERY_BASE(__PATTERN__) \
"SELECT ?url " \
" nfo:fileName(?urn) " \
" nie:mimeType(?urn)" \
" nie:mimeType(?ie)" \
" nfo:fileSize(?urn)" \
" nfo:fileLastModified(?urn)" \
"FROM tracker:FileSystem " \
@ -47,13 +49,13 @@
" ?urn a nfo:FileDataObject ;" \
" nie:url ?url ; " \
" fts:match ~match . " \
" OPTIONAL { ?urn nie:interpretedAs ?ie } ." \
__PATTERN__ \
"} " \
"ORDER BY DESC(fts:rank(?urn)) DESC(?url)"
"ORDER BY ASC(?url)"
#define SEARCH_QUERY SEARCH_QUERY_BASE("")
#define SEARCH_RECURSIVE_QUERY SEARCH_QUERY_BASE("?urn (nfo:belongsToContainer/nie:isStoredAs)+/nie:url ~location")
#define SEARCH_LOCATION_QUERY SEARCH_QUERY_BASE("?urn nfo:belongsToContainer/nie:isStoredAs/nie:url ~location")
#define SEARCH_RECURSIVE_QUERY SEARCH_QUERY_BASE("FILTER (STRSTARTS (?url, CONCAT (~location, '/')))")
struct _GtkSearchEngineTracker3
{
@ -61,8 +63,8 @@ struct _GtkSearchEngineTracker3
TrackerSparqlConnection *sparql_conn;
TrackerSparqlStatement *search_query;
TrackerSparqlStatement *search_recursive_query;
TrackerSparqlStatement *search_location_query;
GCancellable *cancellable;
guint idle_id;
GtkQuery *query;
gboolean query_pending;
};
@ -72,6 +74,13 @@ struct _GtkSearchEngineTracker3Class
GtkSearchEngineClass parent_class;
};
typedef struct
{
TrackerSparqlCursor *cursor;
GtkSearchEngineTracker3 *engine;
gboolean got_results;
} CursorData;
static void gtk_search_engine_tracker3_initable_iface_init (GInitableIface *iface);
G_DEFINE_TYPE_WITH_CODE (GtkSearchEngineTracker3,
@ -95,8 +104,11 @@ finalize (GObject *object)
g_object_unref (engine->cancellable);
}
g_clear_handle_id (&engine->idle_id, g_source_remove);
g_clear_object (&engine->search_query);
g_clear_object (&engine->search_location_query);
g_clear_object (&engine->search_recursive_query);
if (engine->sparql_conn != NULL)
{
tracker_sparql_connection_close (engine->sparql_conn);
@ -131,7 +143,18 @@ create_file_info (GFile *file,
str = tracker_sparql_cursor_get_string (cursor, 2, NULL);
if (str)
g_file_info_set_content_type (info, str);
{
g_file_info_set_content_type (info, str);
g_file_info_set_attribute_uint32 (info, "standard::type",
strcmp (str, "inode/directory") == 0 ?
G_FILE_TYPE_DIRECTORY :
G_FILE_TYPE_REGULAR);
}
else
{
g_file_info_set_content_type (info, "application/text");
g_file_info_set_attribute_uint32 (info, "standard::type", G_FILE_TYPE_UNKNOWN);
}
g_file_info_set_size (info,
tracker_sparql_cursor_get_integer (cursor, 3));
@ -151,6 +174,59 @@ create_file_info (GFile *file,
return info;
}
static gboolean
handle_cursor_idle_cb (gpointer user_data)
{
CursorData *data = user_data;
GtkSearchEngineTracker3 *engine = data->engine;
TrackerSparqlCursor *cursor = data->cursor;
gboolean has_next;
GList *hits = NULL;
GtkSearchHit *hit;
int i = 0;
for (i = 0; i < N_RESULT_BATCH_ITEMS; i++)
{
const gchar *url;
has_next = tracker_sparql_cursor_next (cursor, NULL, NULL);
if (!has_next)
break;
url = tracker_sparql_cursor_get_string (cursor, 0, NULL);
hit = g_slice_new0 (GtkSearchHit);
hit->file = g_file_new_for_uri (url);
hit->info = create_file_info (hit->file, cursor);
hits = g_list_prepend (hits, hit);
data->got_results = TRUE;
}
_gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (engine), hits);
g_list_free_full (hits, free_hit);
if (has_next)
return G_SOURCE_CONTINUE;
else
{
engine->idle_id = 0;
return G_SOURCE_REMOVE;
}
}
static void
cursor_data_free (gpointer user_data)
{
CursorData *data = user_data;
tracker_sparql_cursor_close (data->cursor);
_gtk_search_engine_finished (GTK_SEARCH_ENGINE (data->engine),
data->got_results);
g_object_unref (data->cursor);
g_object_unref (data->engine);
g_free (data);
}
static void
query_callback (TrackerSparqlStatement *statement,
GAsyncResult *res,
@ -158,9 +234,8 @@ query_callback (TrackerSparqlStatement *statement,
{
GtkSearchEngineTracker3 *engine;
TrackerSparqlCursor *cursor;
GList *hits = NULL;
GError *error = NULL;
GtkSearchHit *hit;
CursorData *data;
engine = GTK_SEARCH_ENGINE_TRACKER3 (user_data);
@ -176,25 +251,14 @@ query_callback (TrackerSparqlStatement *statement,
return;
}
while (tracker_sparql_cursor_next (cursor, NULL, NULL))
{
const char *url;
data = g_new0 (CursorData, 1);
data->cursor = cursor;
data->engine = engine;
url = tracker_sparql_cursor_get_string (cursor, 0, NULL);
hit = g_slice_new0 (GtkSearchHit);
hit->file = g_file_new_for_uri (url);
hit->info = create_file_info (hit->file, cursor);
hits = g_list_prepend (hits, hit);
}
tracker_sparql_cursor_close (cursor);
_gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (engine), hits);
_gtk_search_engine_finished (GTK_SEARCH_ENGINE (engine), hits != NULL);
g_list_free_full (hits, free_hit);
g_object_unref (engine);
g_object_unref (cursor);
engine->idle_id =
g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
handle_cursor_idle_cb,
data, cursor_data_free);
}
static void
@ -205,7 +269,6 @@ gtk_search_engine_tracker3_start (GtkSearchEngine *engine)
const char *search_text;
char *match;
GFile *location;
gboolean recursive;
tracker = GTK_SEARCH_ENGINE_TRACKER3 (engine);
@ -221,25 +284,20 @@ gtk_search_engine_tracker3_start (GtkSearchEngine *engine)
return;
}
tracker->query_pending = TRUE;
search_text = gtk_query_get_text (tracker->query);
location = gtk_query_get_location (tracker->query);
recursive = TRUE;
if (strlen (search_text) <= 1)
return;
tracker->query_pending = TRUE;
if (location)
{
char *location_uri = g_file_get_uri (location);
if (recursive)
{
g_debug ("Recursive search query in location: %s", location_uri);
statement = tracker->search_recursive_query;
}
else
{
g_debug ("Search query in location: %s", location_uri);
statement = tracker->search_location_query;
}
g_debug ("Recursive search query in location: %s", location_uri);
statement = tracker->search_recursive_query;
tracker_sparql_statement_bind_string (statement,
"location",
@ -273,6 +331,8 @@ gtk_search_engine_tracker3_stop (GtkSearchEngine *engine)
g_cancellable_cancel (tracker->cancellable);
tracker->query_pending = FALSE;
}
g_clear_handle_id (&tracker->idle_id, g_source_remove);
}
static void
@ -343,14 +403,6 @@ gtk_search_engine_tracker3_initable_init (GInitable *initable,
if (!engine->search_recursive_query)
return FALSE;
engine->search_location_query =
tracker_sparql_connection_query_statement (engine->sparql_conn,
SEARCH_LOCATION_QUERY,
cancellable,
error);
if (!engine->search_location_query)
return FALSE;
return TRUE;
}

View File

@ -1623,17 +1623,22 @@ gtk_snapshot_to_paintable (GtkSnapshot *snapshot,
{
graphene_size_init_from_size (&bounds.size, size);
}
else
else if (node)
{
gsk_render_node_get_bounds (node, &bounds);
bounds.size.width += bounds.origin.x;
bounds.size.height += bounds.origin.y;
}
else
{
bounds.size.width = 0;
bounds.size.height = 0;
}
bounds.origin.x = 0;
bounds.origin.y = 0;
paintable = gtk_render_node_paintable_new (node, &bounds);
gsk_render_node_unref (node);
g_clear_pointer (&node, gsk_render_node_unref);
return paintable;
}

View File

@ -219,6 +219,7 @@ struct _GtkStackPage
guint needs_attention : 1;
guint visible : 1;
guint use_underline : 1;
guint in_destruction : 1;
};
typedef struct _GtkStackPageClass GtkStackPageClass;
@ -235,6 +236,14 @@ gtk_stack_page_accessible_get_at_context (GtkAccessible *accessible)
{
GtkStackPage *page = GTK_STACK_PAGE (accessible);
if (page->in_destruction)
{
GTK_DEBUG (A11Y, "ATContext for “%s” [%p] accessed during destruction",
G_OBJECT_TYPE_NAME (accessible),
accessible);
return NULL;
}
if (page->at_context == NULL)
{
GtkAccessibleRole role = GTK_ACCESSIBLE_ROLE_TAB_PANEL;
@ -246,9 +255,11 @@ gtk_stack_page_accessible_get_at_context (GtkAccessible *accessible)
display = gdk_display_get_default ();
page->at_context = gtk_at_context_create (role, accessible, display);
if (page->at_context == NULL)
return NULL;
}
return page->at_context;
return g_object_ref (page->at_context);
}
static gboolean
@ -262,30 +273,36 @@ static GtkAccessible *
gtk_stack_page_accessible_get_accessible_parent (GtkAccessible *accessible)
{
GtkStackPage *page = GTK_STACK_PAGE (accessible);
GtkWidget *parent;
if (page->widget == NULL)
return NULL;
else
return GTK_ACCESSIBLE (gtk_widget_get_parent (page->widget));
parent = _gtk_widget_get_parent (page->widget);
return GTK_ACCESSIBLE (g_object_ref (parent));
}
static GtkAccessible *
gtk_stack_page_accessible_get_first_accessible_child(GtkAccessible *accessible)
gtk_stack_page_accessible_get_first_accessible_child (GtkAccessible *accessible)
{
GtkStackPage *page = GTK_STACK_PAGE (accessible);
if (page->widget != NULL)
return GTK_ACCESSIBLE (page->widget);
else
if (page->widget == NULL)
return NULL;
return GTK_ACCESSIBLE (g_object_ref (page->widget));
}
static GtkAccessible *
gtk_stack_page_accessible_get_next_accessible_sibling(GtkAccessible *accessible)
gtk_stack_page_accessible_get_next_accessible_sibling (GtkAccessible *accessible)
{
GtkStackPage *page = GTK_STACK_PAGE (accessible);
return GTK_ACCESSIBLE (page->next_page);
if (page->next_page == NULL)
return NULL;
return GTK_ACCESSIBLE (g_object_ref (page->next_page));
}
static gboolean
@ -345,6 +362,8 @@ gtk_stack_page_dispose (GObject *object)
{
GtkStackPage *page = GTK_STACK_PAGE (object);
page->in_destruction = TRUE;
g_clear_object (&page->at_context);
G_OBJECT_CLASS (gtk_stack_page_parent_class)->dispose (object);
@ -791,7 +810,8 @@ gtk_stack_accessible_get_first_accessible_child (GtkAccessible *accessible)
GtkStack *stack = GTK_STACK (accessible);
GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
GtkStackPage *page = g_ptr_array_index (priv->children, 0);
return GTK_ACCESSIBLE (page);
return GTK_ACCESSIBLE (g_object_ref (page));
}
static void

View File

@ -172,6 +172,7 @@ gtk_test_accessible_has_property (GtkAccessible *accessible,
GtkAccessibleProperty property)
{
GtkATContext *context;
gboolean res;
g_return_val_if_fail (GTK_IS_ACCESSIBLE (accessible), FALSE);
@ -179,7 +180,11 @@ gtk_test_accessible_has_property (GtkAccessible *accessible,
if (context == NULL)
return FALSE;
return gtk_at_context_has_accessible_property (context, property);
res = gtk_at_context_has_accessible_property (context, property);
g_object_unref (context);
return res;
}
/**
@ -230,6 +235,7 @@ gtk_test_accessible_check_property (GtkAccessible *accessible,
out:
gtk_accessible_value_unref (check_value);
g_object_unref (context);
return res;
}
@ -248,6 +254,7 @@ gtk_test_accessible_has_state (GtkAccessible *accessible,
GtkAccessibleState state)
{
GtkATContext *context;
gboolean res;
g_return_val_if_fail (GTK_IS_ACCESSIBLE (accessible), FALSE);
@ -255,7 +262,11 @@ gtk_test_accessible_has_state (GtkAccessible *accessible,
if (context == NULL)
return FALSE;
return gtk_at_context_has_accessible_state (context, state);
res = gtk_at_context_has_accessible_state (context, state);
g_object_unref (context);
return res;
}
/**
@ -306,6 +317,7 @@ gtk_test_accessible_check_state (GtkAccessible *accessible,
out:
gtk_accessible_value_unref (check_value);
g_object_unref (context);
return res;
}
@ -324,6 +336,7 @@ gtk_test_accessible_has_relation (GtkAccessible *accessible,
GtkAccessibleRelation relation)
{
GtkATContext *context;
gboolean res;
g_return_val_if_fail (GTK_IS_ACCESSIBLE (accessible), FALSE);
@ -331,7 +344,11 @@ gtk_test_accessible_has_relation (GtkAccessible *accessible,
if (context == NULL)
return FALSE;
return gtk_at_context_has_accessible_relation (context, relation);
res = gtk_at_context_has_accessible_relation (context, relation);
g_object_unref (context);
return res;
}
/**
@ -382,6 +399,7 @@ gtk_test_accessible_check_relation (GtkAccessible *accessible,
out:
gtk_accessible_value_unref (check_value);
g_object_unref (context);
return res;
}

View File

@ -247,6 +247,7 @@ struct _GtkTextPrivate
guint populate_all : 1;
guint propagate_text_width : 1;
guint text_handles_enabled : 1;
guint enable_undo : 1;
};
struct _GtkTextPasswordHint
@ -397,6 +398,9 @@ static void gtk_text_set_max_width_chars (GtkText *self,
static void gtk_text_set_alignment (GtkText *self,
float xalign);
static void gtk_text_set_enable_undo (GtkText *self,
gboolean enable_undo);
/* Default signal handlers
*/
static GMenuModel *gtk_text_get_menu_model (GtkText *self);
@ -561,6 +565,7 @@ static void begin_change (GtkText *self);
static void end_change (GtkText *self);
static void emit_changed (GtkText *self);
static void gtk_text_update_history (GtkText *self);
static void gtk_text_update_clipboard_actions (GtkText *self);
static void gtk_text_update_emoji_action (GtkText *self);
static void gtk_text_update_handles (GtkText *self);
@ -1602,11 +1607,7 @@ gtk_text_set_property (GObject *object,
break;
case NUM_PROPERTIES + GTK_EDITABLE_PROP_ENABLE_UNDO:
if (g_value_get_boolean (value) != gtk_text_history_get_enabled (priv->history))
{
gtk_text_history_set_enabled (priv->history, g_value_get_boolean (value));
g_object_notify_by_pspec (object, pspec);
}
gtk_text_set_enable_undo (self, g_value_get_boolean (value));
break;
/* GtkText properties */
@ -1732,7 +1733,7 @@ gtk_text_get_property (GObject *object,
break;
case NUM_PROPERTIES + GTK_EDITABLE_PROP_ENABLE_UNDO:
g_value_set_boolean (value, gtk_text_history_get_enabled (priv->history));
g_value_set_boolean (value, priv->enable_undo);
break;
/* GtkText properties */
@ -1858,6 +1859,7 @@ gtk_text_init (GtkText *self)
priv->cursor_alpha = 1.0;
priv->invisible_char = 0;
priv->history = gtk_text_history_new (&history_funcs, self);
priv->enable_undo = TRUE;
gtk_text_history_set_max_undo_levels (priv->history, DEFAULT_MAX_UNDO);
@ -3400,11 +3402,15 @@ gtk_text_insert_text (GtkText *self,
* The incoming text may a password or other secret. We make sure
* not to copy it into temporary buffers.
*/
if (priv->change_count == 0)
gtk_text_history_begin_irreversible_action (priv->history);
begin_change (self);
n_inserted = gtk_entry_buffer_insert_text (get_buffer (self), *position, text, n_chars);
end_change (self);
if (priv->change_count == 0)
gtk_text_history_end_irreversible_action (priv->history);
if (n_inserted != n_chars)
gtk_widget_error_bell (GTK_WIDGET (self));
@ -3426,11 +3432,16 @@ gtk_text_delete_text (GtkText *self,
if (start_pos == end_pos)
return;
if (priv->change_count == 0)
gtk_text_history_begin_irreversible_action (priv->history);
begin_change (self);
gtk_entry_buffer_delete_text (get_buffer (self), start_pos, end_pos - start_pos);
end_change (self);
if (priv->change_count == 0)
gtk_text_history_end_irreversible_action (priv->history);
update_placeholder_visibility (self);
if (priv->propagate_text_width)
gtk_widget_queue_resize (GTK_WIDGET (self));
@ -5525,6 +5536,7 @@ gtk_text_set_editable (GtkText *self,
gtk_event_controller_key_set_im_context (GTK_EVENT_CONTROLLER_KEY (priv->key_controller),
is_editable ? priv->im_context : NULL);
gtk_text_update_history (self);
gtk_text_update_clipboard_actions (self);
gtk_text_update_emoji_action (self);
@ -5605,7 +5617,7 @@ gtk_text_set_visibility (GtkText *self,
gtk_text_recompute (self);
/* disable undo when invisible text is used */
gtk_text_history_set_enabled (priv->history, visible);
gtk_text_update_history (self);
gtk_text_update_clipboard_actions (self);
}
@ -6342,6 +6354,9 @@ gtk_text_drag_drop (GtkDropTarget *dest,
drop_position = gtk_text_find_position (self, x + priv->scroll_offset);
str = g_value_get_string (value);
if (str == NULL)
str = "";
if (priv->truncate_multiline)
length = truncate_multiline (str);
else
@ -7284,3 +7299,28 @@ gtk_text_history_select_cb (gpointer funcs_data,
selection_insert,
selection_bound);
}
static void
gtk_text_set_enable_undo (GtkText *self,
gboolean enable_undo)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
if (priv->enable_undo == enable_undo)
return;
priv->enable_undo = enable_undo;
gtk_text_update_history (self);
g_object_notify (G_OBJECT (self), "enable-undo");
}
static void
gtk_text_update_history (GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
gtk_text_history_set_enabled (priv->history,
priv->enable_undo &&
priv->visible &&
priv->editable);
}

View File

@ -5336,7 +5336,7 @@ gtk_text_buffer_get_run_attributes (GtkTextBuffer *buffer,
GdkRGBA *rgba;
char *value;
g_object_get (tag, "foreground", &rgba, NULL);
g_object_get (tag, "foreground-rgba", &rgba, NULL);
value = g_strdup_printf ("%u,%u,%u",
(guint) rgba->red * 65535,
(guint) rgba->green * 65535,

View File

@ -511,7 +511,7 @@ gtk_text_line_display_cache_invalidate_range (GtkTextLineDisplayCache *cache,
/* gtk_text_iter_order() preserving const */
if (gtk_text_iter_compare (begin, end) > 0)
{
const GtkTextIter *tmp = begin;
const GtkTextIter *tmp = end;
end = begin;
begin = tmp;
}

View File

@ -67,6 +67,10 @@
* `GtkToggleButton` has a single CSS node with name button. To differentiate
* it from a plain `GtkButton`, it gets the `.toggle` style class.
*
* ## Accessibility
*
* `GtkToggleButton` uses the %GTK_ACCESSIBLE_ROLE_TOGGLE_BUTTON role.
*
* ## Creating two `GtkToggleButton` widgets.
*
* ```c
@ -311,6 +315,8 @@ gtk_toggle_button_class_init (GtkToggleButtonClass *class)
G_TYPE_NONE, 0);
gtk_widget_class_set_css_name (widget_class, I_("button"));
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_TOGGLE_BUTTON);
}
static void

View File

@ -633,6 +633,7 @@ static void remove_parent_surface_transform_changed_listener (GtkWidget *wid
static void add_parent_surface_transform_changed_listener (GtkWidget *widget);
static void gtk_widget_queue_compute_expand (GtkWidget *widget);
static GtkATContext *create_at_context (GtkWidget *self);
static int GtkWidget_private_offset = 0;
@ -904,8 +905,18 @@ gtk_widget_get_accessible_role (GtkWidget *self)
GtkATContext *context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (self));
GtkWidgetClassPrivate *class_priv;
if (context != NULL && gtk_at_context_is_realized (context))
return gtk_at_context_get_accessible_role (context);
if (context != NULL)
{
GtkAccessibleRole role = GTK_ACCESSIBLE_ROLE_NONE;
if (gtk_at_context_is_realized (context))
role = gtk_at_context_get_accessible_role (context);
g_object_unref (context);
if (role != GTK_ACCESSIBLE_ROLE_NONE)
return role;
}
if (priv->accessible_role != GTK_ACCESSIBLE_ROLE_WIDGET)
return priv->accessible_role;
@ -2361,7 +2372,7 @@ gtk_widget_init (GTypeInstance *instance, gpointer g_class)
gtk_widget_add_controller (widget, controller);
}
priv->at_context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (widget));
priv->at_context = create_at_context (widget);
}
static void
@ -3860,6 +3871,29 @@ gtk_widget_adjust_size_allocation (GtkWidget *widget,
}
}
static void
gtk_widget_ensure_allocate_on_children (GtkWidget *widget)
{
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
GtkWidget *child;
g_assert (!priv->resize_needed);
g_assert (!priv->alloc_needed);
if (!priv->alloc_needed_on_child)
return;
priv->alloc_needed_on_child = FALSE;
for (child = _gtk_widget_get_first_child (widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
if (gtk_widget_should_layout (child))
gtk_widget_ensure_allocate (child);
}
}
/**
* gtk_widget_allocate:
* @widget: A `GtkWidget`
@ -4026,56 +4060,58 @@ gtk_widget_allocate (GtkWidget *widget,
size_changed = (priv->width != adjusted.width) || (priv->height != adjusted.height);
if (!alloc_needed && !size_changed && !baseline_changed)
goto skip_allocate;
priv->width = adjusted.width;
priv->height = adjusted.height;
priv->baseline = baseline;
if (priv->layout_manager != NULL)
{
gtk_layout_manager_allocate (priv->layout_manager, widget,
priv->width,
priv->height,
baseline);
gtk_widget_ensure_allocate_on_children (widget);
}
else
else
{
GTK_WIDGET_GET_CLASS (widget)->size_allocate (widget,
priv->width,
priv->height,
baseline);
}
priv->width = adjusted.width;
priv->height = adjusted.height;
priv->baseline = baseline;
/* Size allocation is god... after consulting god, no further requests or allocations are needed */
priv->alloc_needed_on_child = FALSE;
if (priv->layout_manager != NULL)
{
gtk_layout_manager_allocate (priv->layout_manager, widget,
priv->width,
priv->height,
baseline);
}
else
{
GTK_WIDGET_GET_CLASS (widget)->size_allocate (widget,
priv->width,
priv->height,
baseline);
}
/* Size allocation is god... after consulting god, no further requests or allocations are needed */
#ifdef G_ENABLE_DEBUG
if (GTK_DISPLAY_DEBUG_CHECK (_gtk_widget_get_display (widget), GEOMETRY) &&
gtk_widget_get_resize_needed (widget))
{
g_warning ("%s %p or a child called gtk_widget_queue_resize() during size_allocate().",
gtk_widget_get_name (widget), widget);
}
if (GTK_DISPLAY_DEBUG_CHECK (_gtk_widget_get_display (widget), GEOMETRY) &&
gtk_widget_get_resize_needed (widget))
{
g_warning ("%s %p or a child called gtk_widget_queue_resize() during size_allocate().",
gtk_widget_get_name (widget), widget);
}
#endif
gtk_widget_ensure_resize (widget);
priv->alloc_needed = FALSE;
priv->alloc_needed_on_child = FALSE;
gtk_widget_ensure_resize (widget);
priv->alloc_needed = FALSE;
gtk_widget_update_paintables (widget);
gtk_widget_update_paintables (widget);
if (size_changed)
gtk_accessible_bounds_changed (GTK_ACCESSIBLE (widget));
if (size_changed)
gtk_accessible_bounds_changed (GTK_ACCESSIBLE (widget));
skip_allocate:
if (size_changed || baseline_changed)
gtk_widget_queue_draw (widget);
else if (transform_changed && priv->parent)
if (size_changed || baseline_changed)
gtk_widget_queue_draw (widget);
}
if (transform_changed && priv->parent)
gtk_widget_queue_draw (priv->parent);
out:
if (priv->alloc_needed_on_child)
gtk_widget_ensure_allocate (widget);
gtk_widget_pop_verify_invariants (widget);
}
@ -7352,7 +7388,6 @@ gtk_widget_dispose (GObject *object)
GtkWidget *widget = GTK_WIDGET (object);
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
GSList *sizegroups;
GtkATContext *at_context;
if (priv->muxer != NULL)
g_object_run_dispose (G_OBJECT (priv->muxer));
@ -7402,9 +7437,11 @@ gtk_widget_dispose (GObject *object)
gtk_size_group_remove_widget (size_group, widget);
}
at_context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (widget));
if (at_context != NULL)
gtk_at_context_unrealize (at_context);
if (priv->at_context != NULL)
{
gtk_at_context_unrealize (priv->at_context);
g_clear_object (&priv->at_context);
}
g_clear_object (&priv->muxer);
@ -8427,9 +8464,8 @@ gtk_widget_set_vexpand_set (GtkWidget *widget,
*/
static GtkATContext *
gtk_widget_accessible_get_at_context (GtkAccessible *accessible)
create_at_context (GtkWidget *self)
{
GtkWidget *self = GTK_WIDGET (accessible);
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (self);
GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (self);
GtkWidgetClassPrivate *class_priv = widget_class->priv;
@ -8443,9 +8479,6 @@ gtk_widget_accessible_get_at_context (GtkAccessible *accessible)
return NULL;
}
if (priv->at_context != NULL)
return priv->at_context;
/* Widgets have two options to set the accessible role: either they
* define it in their class_init() function, and the role applies to
* all instances; or an instance is created with the :accessible-role
@ -8460,9 +8493,35 @@ gtk_widget_accessible_get_at_context (GtkAccessible *accessible)
role = class_priv->accessible_role;
priv->accessible_role = role;
priv->at_context = gtk_at_context_create (role, accessible, gdk_display_get_default ());
priv->at_context = gtk_at_context_create (role, GTK_ACCESSIBLE (self), gdk_display_get_default ());
if (priv->at_context != NULL)
return g_object_ref (priv->at_context);
return priv->at_context;
return NULL;
}
static GtkATContext *
gtk_widget_accessible_get_at_context (GtkAccessible *accessible)
{
GtkWidget *self = GTK_WIDGET (accessible);
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (self);
if (priv->in_destruction)
{
GTK_DEBUG (A11Y, "ATContext for widget “%s” [%p] accessed during destruction",
G_OBJECT_TYPE_NAME (self),
self);
return NULL;
}
if (priv->at_context != NULL)
return g_object_ref (priv->at_context);
priv->at_context = create_at_context (self);
if (priv->at_context != NULL)
return g_object_ref (priv->at_context);
return NULL;
}
static gboolean
@ -8485,19 +8544,34 @@ gtk_widget_accessible_get_platform_state (GtkAccessible *self,
static GtkAccessible *
gtk_widget_accessible_get_accessible_parent (GtkAccessible *self)
{
return GTK_ACCESSIBLE (gtk_widget_get_parent (GTK_WIDGET (self)));
GtkWidget *parent = _gtk_widget_get_parent (GTK_WIDGET (self));
if (parent == NULL)
return NULL;
return GTK_ACCESSIBLE (g_object_ref (parent));
}
static GtkAccessible *
gtk_widget_accessible_get_next_accessible_sibling (GtkAccessible *self)
{
return GTK_ACCESSIBLE (gtk_widget_get_next_sibling (GTK_WIDGET (self)));
GtkWidget *sibling = _gtk_widget_get_next_sibling (GTK_WIDGET (self));
if (sibling == NULL)
return NULL;
return GTK_ACCESSIBLE (g_object_ref (sibling));
}
static GtkAccessible *
gtk_widget_accessible_get_first_accessible_child (GtkAccessible *self)
{
return GTK_ACCESSIBLE (gtk_widget_get_first_child (GTK_WIDGET (self)));
GtkWidget *child = _gtk_widget_get_first_child (GTK_WIDGET (self));
if (child == NULL)
return NULL;
return GTK_ACCESSIBLE (g_object_ref (child));
}
static gboolean
@ -9284,6 +9358,8 @@ gtk_widget_buildable_finish_accessibility_properties (GtkWidget *widget,
}
g_slist_free_full (attributes, accessibility_attribute_info_free);
g_object_unref (context);
}
static void
@ -10585,7 +10661,10 @@ gtk_widget_set_alloc_needed (GtkWidget *widget)
break;
if (GTK_IS_NATIVE (widget))
gtk_native_queue_relayout (GTK_NATIVE (widget));
{
gtk_native_queue_relayout (GTK_NATIVE (widget));
return;
}
widget = priv->parent;
if (widget == NULL)
@ -10633,18 +10712,9 @@ gtk_widget_ensure_allocate (GtkWidget *widget)
priv->allocated_size_baseline,
gsk_transform_ref (priv->allocated_transform));
}
else if (priv->alloc_needed_on_child)
else
{
GtkWidget *child;
priv->alloc_needed_on_child = FALSE;
for (child = _gtk_widget_get_first_child (widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
gtk_widget_ensure_allocate (child);
}
gtk_widget_ensure_allocate_on_children (widget);
}
}
@ -11128,7 +11198,8 @@ gtk_widget_dispose_template (GtkWidget *widget,
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (g_type_name (widget_type) != NULL);
GtkWidgetTemplate *template = GTK_WIDGET_GET_CLASS (widget)->priv->template;
GObjectClass *object_class = g_type_class_peek (widget_type);
GtkWidgetTemplate *template = GTK_WIDGET_CLASS (object_class)->priv->template;
g_return_if_fail (template != NULL);
/* Tear down the automatic child data */

View File

@ -3595,9 +3595,20 @@ gtk_window_set_default_size_internal (GtkWindow *window,
*
* Sets the default size of a window.
*
* If the windows natural size (its size request) is larger than
* The default size of a window is the size that will be used if no other constraints apply.
*
* The default size will be updated whenever the window is resized
* to reflect the new size, unless the window is forced to a size,
* like when it is maximized or fullscreened.
*
* If the windows minimum size request is larger than
* the default, the default will be ignored.
*
* Setting the default size to a value <= 0 will cause it to be
* ignored and the natural size request will be used instead. It
* is possible to do this while the window is showing to "reset"
* it to its initial size.
*
* Unlike [method@Gtk.Widget.set_size_request], which sets a size
* request for a widget and thus would keep users from shrinking
* the window, this function only sets the initial size, just as
@ -3606,13 +3617,6 @@ gtk_window_set_default_size_internal (GtkWindow *window,
* size of -1 means to use the natural default size (the size request
* of the window).
*
* The default size of a window only affects the first time a window is
* shown; if a window is hidden and re-shown, it will remember the size
* it had prior to hiding, rather than using the default size.
*
* Windows cant actually be 0x0 in size, they must be at least 1x1, but
* passing 0 for @width and @height is OK, resulting in a 1x1 default size.
*
* If you use this function to reestablish a previously saved window size,
* note that the appropriate size to save is the one returned by
* [method@Gtk.Window.get_default_size]. Using the window allocation
@ -3643,6 +3647,9 @@ gtk_window_set_default_size (GtkWindow *window,
* A value of 0 for the width or height indicates that a default
* size has not been explicitly set for that dimension, so the
* natural size of the window will be used.
*
* This function is the recommended way for [saving window state
* across restarts of applications](https://developer.gnome.org/documentation/tutorials/save-state.html).
*/
void
gtk_window_get_default_size (GtkWindow *window,
@ -4430,6 +4437,7 @@ gtk_window_unrealize (GtkWidget *widget)
g_signal_handlers_disconnect_by_func (surface, surface_state_changed, widget);
g_signal_handlers_disconnect_by_func (surface, surface_render, widget);
g_signal_handlers_disconnect_by_func (surface, surface_event, widget);
g_signal_handlers_disconnect_by_func (surface, toplevel_compute_size, widget);
frame_clock = gdk_surface_get_frame_clock (surface);

View File

@ -244,6 +244,8 @@ update_path (GtkInspectorA11y *sl)
}
else
path = "not on bus";
g_clear_object (&context);
#endif
gtk_label_set_label (GTK_LABEL (sl->path), path);
@ -270,7 +272,7 @@ update_attributes (GtkInspectorA11y *sl)
gboolean has_value;
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (sl->object));
if (!context)
if (context == NULL)
return;
store = g_list_store_new (G_TYPE_OBJECT);
@ -347,6 +349,8 @@ update_attributes (GtkInspectorA11y *sl)
g_object_unref (selection);
gtk_widget_set_visible (sl->attributes, g_list_model_get_n_items (G_LIST_MODEL (filter_model)) > 0);
g_object_unref (context);
}
static void
@ -420,8 +424,11 @@ gtk_inspector_a11y_set_object (GtkInspectorA11y *sl,
if (sl->object && GTK_IS_ACCESSIBLE (sl->object))
{
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (sl->object));
if (context)
g_signal_handlers_disconnect_by_func (context, refresh_all, sl);
if (context != NULL)
{
g_signal_handlers_disconnect_by_func (context, refresh_all, sl);
g_object_unref (context);
}
}
g_set_object (&sl->object, object);
@ -432,8 +439,12 @@ gtk_inspector_a11y_set_object (GtkInspectorA11y *sl,
if (GTK_IS_ACCESSIBLE (sl->object))
{
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (sl->object));
if (context)
g_signal_connect_swapped (context, "state-change", G_CALLBACK (refresh_all), sl);
if (context != NULL)
{
g_signal_connect_swapped (context, "state-change", G_CALLBACK (refresh_all), sl);
g_object_unref (context);
}
gtk_stack_page_set_visible (page, TRUE);
update_role (sl);
update_path (sl);
@ -461,7 +472,11 @@ dispose (GObject *o)
GtkATContext *context;
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (sl->object));
g_signal_handlers_disconnect_by_func (context, refresh_all, sl);
if (context != NULL)
{
g_signal_handlers_disconnect_by_func (context, refresh_all, sl);
g_object_unref (context);
}
}
g_clear_object (&sl->object);

View File

@ -185,7 +185,7 @@ action_state_changed_cb (GActionGroup *group,
static void
update_widgets (GtkInspectorActionEditor *r)
{
GVariant *state;
GVariant *state = NULL;
if (G_IS_ACTION_GROUP (r->owner))
g_action_group_query_action (G_ACTION_GROUP (r->owner), r->name,

View File

@ -1131,11 +1131,19 @@ toplevel_filter_func (gpointer item,
gpointer data)
{
GdkDisplay *display = data;
gpointer iw;
if (!GTK_IS_WINDOW (item))
return FALSE;
return gtk_widget_get_display (item) == display;
if (gtk_widget_get_display (item) != display)
return FALSE;
iw = g_object_get_data (G_OBJECT (display), "-gtk-inspector");
if (iw == item)
return FALSE;
return TRUE;
}
static GListModel *

View File

@ -658,6 +658,7 @@ init_icons (GtkInspectorVisual *vis)
GList *list, *l;
int i;
GtkStringList *names;
const char * const *dirs;
t = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
@ -669,6 +670,14 @@ init_icons (GtkInspectorVisual *vis)
fill_icons (path, t);
g_free (path);
dirs = g_get_system_data_dirs ();
for (i = 0; dirs[i]; i++)
{
path = g_build_filename (dirs[i], "icons", NULL);
fill_icons (path, t);
g_free (path);
}
list = NULL;
g_hash_table_iter_init (&iter, t);
while (g_hash_table_iter_next (&iter, (gpointer *)&theme, NULL))
@ -723,6 +732,7 @@ init_cursors (GtkInspectorVisual *vis)
GList *list, *l;
GtkStringList *names;
int i;
const char * const *dirs;
t = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
@ -734,6 +744,14 @@ init_cursors (GtkInspectorVisual *vis)
fill_cursors (path, t);
g_free (path);
dirs = g_get_system_data_dirs ();
for (i = 0; dirs[i]; i++)
{
path = g_build_filename (dirs[i], "icons", NULL);
fill_cursors (path, t);
g_free (path);
}
list = NULL;
g_hash_table_iter_init (&iter, t);
while (g_hash_table_iter_next (&iter, (gpointer *)&theme, NULL))

View File

@ -913,7 +913,7 @@ foreach lang : [
gnome.compile_resources(lang,
resxml,
source_dir: 'emoji',
source_dir: meson.current_source_dir() / 'emoji',
gresource_bundle: true,
install: true,
install_dir: gtk_datadir / 'gtk-4.0/emoji',

View File

@ -1,5 +1,5 @@
project('gtk', 'c',
version: '4.9.5',
version: '4.10.1',
default_options: [
'buildtype=debugoptimized',
'warning_level=1',
@ -370,6 +370,13 @@ glib_dep = dependency('glib-2.0', version: glib_req)
gobject_dep = dependency('gobject-2.0', version: glib_req)
if os_win32
giowin32_dep = dependency('gio-windows-2.0', version: glib_req, required: win32_enabled)
if giowin32_dep.version().version_compare('<2.75.0')
if cc.get_id() == 'msvc' and get_option('default_library') != 'static'
# Override _GLIB_EXTERN on Visual Studio for media modules for glib <= 2.74.x, so that we
# avoid error C2375 (redefinition; different linkage) when building the g_io_module_*() bits
cdata.set('MODULES_OVERRIDE_GLIB_EXTERN', true)
endif
endif
endif
if os_unix
giounix_dep = dependency('gio-unix-2.0', version: glib_req, required: false)
@ -728,8 +735,13 @@ endif
# Introspection
gir = find_program('g-ir-scanner', required : get_option('introspection'))
build_gir = gir.found() and get_option('introspection').enabled()
if not gir.found() and get_option('introspection').enabled()
error('Introspection enabled, but g-ir-scanner not found.')
endif
build_gir = gir.found() and (get_option('introspection').enabled() or
(get_option('introspection').allowed() and get_option('gtk_doc')))
project_build_root = meson.current_build_dir()
@ -888,6 +900,7 @@ summary('Optimization', get_option('optimization'), section: 'Build')
summary('Introspection', build_gir, section: 'Build')
summary('Documentation', get_option('gtk_doc'), section: 'Build')
summary('Man pages', get_option('man-pages'), section: 'Build')
summary('Testsuite', get_option('build-testsuite'), section: 'Build')
summary('Tests', get_option('build-tests'), section: 'Build')
summary('Demos', get_option('demos'), section: 'Build')
summary('Examples', get_option('build-examples'), section: 'Build')

View File

@ -19,6 +19,15 @@
#include "config.h"
/*
* Sadly, we need this to build on Visual Studio against glib-2.74.x or earlier,
* otherwise the build will fail when building the g_io_module_*() bits with error C2375
* (redefinition; different linkage). This must be before including the Gio headers.
*/
#if defined (_MSC_VER) && defined (MODULES_OVERRIDE_GLIB_EXTERN)
# define _GLIB_EXTERN __declspec(dllexport) extern
#endif
#include "gtkffmediafileprivate.h"
#include <glib/gi18n-lib.h>

View File

@ -19,6 +19,15 @@
#include "config.h"
/*
* Sadly, we need this to build on Visual Studio against glib-2.74.x or earlier,
* otherwise the build will fail when building the g_io_module_*() bits with error C2375
* (redefinition; different linkage). This must be before including the Gio headers.
*/
#if defined (_MSC_VER) && defined (MODULES_OVERRIDE_GLIB_EXTERN)
# define _GLIB_EXTERN __declspec(dllexport) extern
#endif
#include "gtkgstmediafileprivate.h"
#include "gtkgstpaintableprivate.h"

1792
po/bg.po

File diff suppressed because it is too large Load Diff

1102
po/ca.po

File diff suppressed because it is too large Load Diff

2790
po/cs.po

File diff suppressed because it is too large Load Diff

2840
po/da.po

File diff suppressed because it is too large Load Diff

574
po/de.po

File diff suppressed because it is too large Load Diff

567
po/gl.po

File diff suppressed because it is too large Load Diff

1716
po/he.po

File diff suppressed because it is too large Load Diff

2006
po/hu.po

File diff suppressed because it is too large Load Diff

2946
po/ko.po

File diff suppressed because it is too large Load Diff

1610
po/pl.po

File diff suppressed because it is too large Load Diff

567
po/pt.po

File diff suppressed because it is too large Load Diff

1805
po/ru.po

File diff suppressed because it is too large Load Diff

1527
po/sv.po

File diff suppressed because it is too large Load Diff

681
po/tr.po

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,7 @@ test_object_accessible_get_at_context (GtkAccessible *accessible)
accessible,
gdk_display_get_default ());
return self->at_context;
return g_object_ref (self->at_context);
}
static void

View File

@ -10,7 +10,7 @@ styletest_env.set('G_ENABLE_DIAGNOSTIC', '0')
cssresources = gnome.compile_resources(
'cssresources',
'test-css-style.gresource.xml',
source_dir: '.',
source_dir: meson.current_source_dir(),
)
test_style = executable('test-css-style',

View File

@ -0,0 +1,8 @@
clip {
clip: 24995 24995 10 10;
child: texture-scale {
texture: url("data:;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAKElEQVQYlWNkYGD4z4AG/v/HEGJgwhDBAQZQIQs2hzMyMtLBauorBACQUgcSISWLRgAAAABJRU5ErkJggg==");
bounds: 0 0 50000 50000;
filter: nearest;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

View File

@ -0,0 +1,8 @@
clip {
clip: 3950 3950 100 100;
child: texture-scale {
bounds: 0 0 19991 19991;
filter: nearest;
texture: url('data:,<svg><rect width="10" height="10" style="fill:red" /></svg>');
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

View File

@ -66,19 +66,21 @@ compare_render_tests = [
'outset_shadow_offset_y',
'outset_shadow_rounded_top',
'outset_shadow_simple',
'scaled-cairo',
'scale-textures-negative-ngl',
'scale-up-down',
'shadow-in-opacity',
'texture-url',
'repeat',
'repeat-no-repeat',
'repeat-negative-coords',
'repeat-texture',
'rounded-clip-in-clip-3d', # not really 3d, but cairo fails it
'scale-textures-negative-ngl',
'scale-up-down',
'scaled-cairo',
'scaled-texture',
'shadow-in-opacity',
'texture-scale-magnify-10000x',
'texture-scale-stripes',
'texture-url',
'transform-in-transform',
'transform-in-transform-in-transform',
'rounded-clip-in-clip-3d', # not really 3d, but cairo fails it
'scaled-texture',
]
# these are too sensitive to differences in the renderers
@ -140,6 +142,9 @@ endforeach
node_parser_tests = [
'blend.node',
'blend-unknown-mode.errors',
'blend-unknown-mode.node',
'blend-unknown-mode.ref.node',
'border.node',
'color.node',
'conic-gradient.node',
@ -199,6 +204,8 @@ node_parser_tests = [
'empty-text.ref.node',
'empty-texture.node',
'empty-texture.ref.node',
'empty-texture-scale.node',
'empty-texture-scale.ref.node',
'empty-transform.node',
'empty-transform.ref.node',
'glshader.node',
@ -227,6 +234,9 @@ node_parser_tests = [
'texture-fail.node',
'texture-fail.ref.node',
'texture-fail.ref.errors',
'texture-scale-unknown-filter.errors',
'texture-scale-unknown-filter.node',
'texture-scale-unknown-filter.ref.node',
'transform-fail.node',
'transform-fail.ref.node',
'transform-fail.errors',

View File

@ -0,0 +1 @@
<data>:2:9-13: error: GTK_CSS_PARSER_ERROR_SYNTAX

View File

@ -0,0 +1,3 @@
blend {
mode: diff;
}

View File

@ -0,0 +1,10 @@
blend {
bottom: color {
bounds: 0 0 50 50;
color: rgb(170,255,0);
}
top: color {
bounds: 0 0 50 50;
color: rgb(255,0,204);
}
}

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