Compare commits

...

67 Commits

Author SHA1 Message Date
Matthias Clasen e155c3fe36 Move path point getters to GskPath
This is more in line with treating GskPathPoint as just
a struct, not an object. And gi-docgen already puts the
apis there anyway. As an extra bonus, the function names
get shorter.

Update all callers.
2023-08-14 10:16:48 -04:00
Piotr Drąg 595152bdba Update POTFILES.in 2023-08-14 15:16:29 +02:00
Matthias Clasen 625e83e1de Merge branch 'wip/alice/menu-button' into 'main'
menubutton: Fix focus on the internal gizmo

Closes #6025

See merge request GNOME/gtk!6283
2023-08-14 11:19:21 +00:00
Matthias Clasen daac04be82 Merge branch 'bilelmoussaoui/gi' into 'main'
gi: Add missing since annotation

See merge request GNOME/gtk!6282
2023-08-14 11:15:28 +00:00
Matthias Clasen 8ec9bba892 Merge branch 'dboles/inspector-object-tree-flash' into 'main'
inspector/object-tree: Fix ! flash @ select widget

Closes #6022

See merge request GNOME/gtk!6279
2023-08-14 11:14:49 +00:00
Alice Mikhaylenko b63e365fd2 menubutton: Fix focus on the internal gizmo
Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/6025
2023-08-14 14:24:06 +04:00
Daniel Boles 859a25b0e7 Inspector: Fix ! flash on select widget @ obj tree
Widgets are flashed by the window when it receives Tree::object-selected
- but we were emitting said signal from select_object(), i.e. if we were
made to select by an external caller. We should also emit it if the user
interactively selects an item, so the window receives+flashes the widget

fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/6022
2023-08-14 10:48:26 +01:00
Bilal Elmoussaoui f5d47a4658 gi: Add missing since annotation 2023-08-14 08:41:12 +00:00
Matthias Clasen fa6645f157 Merge branch 'fix-add-segment' into 'main'
Fix gsk_path_builder_add_segment

See merge request GNOME/gtk!6280
2023-08-14 01:01:37 +00:00
Efstathios Iosifidis 2929686c1d Update Greek translation 2023-08-13 21:18:23 +00:00
Andika Triwidada c83256e080 Update Indonesian translation 2023-08-13 13:42:39 +00:00
Matthias Clasen 4b7e1b1233 Fix gsk_path_builder_add_segment
We were splitting the last segment, but did not
actually use the result of the split. Oops.
2023-08-13 09:40:19 -04:00
Matthias Clasen 95a8821614 text: Track user changes better
Make all the action signal handlers call
begin/end_change(), so we can rely on
change_count being >0 to check later if
an action is user-initiated or programmatic.

Fixes: #6018
2023-08-13 09:31:42 -04:00
Benjamin Otte 73500f9ebb Merge branch 'wip/otte/for-main' into 'main'
treeviewcolumn: Set ascending class for ascending order

Closes #6002

See merge request GNOME/gtk!6277
2023-08-13 03:54:19 +00:00
Matthias Clasen eae38756c2 Merge branch 'matthiasc/for-main' into 'main'
path-tool: Make the docs match

See merge request GNOME/gtk!6278
2023-08-12 23:51:21 +00:00
Matthias Clasen 8bf7187b89 path-tool: Make the docs match
We've removed various things from the tool,
and forgot to keep the docs up-to-date.
2023-08-12 19:29:27 -04:00
Matthias Clasen e7fb8842b5 Merge branch 'path-for-merge-8' into 'main'
Add gtk-path-tool

See merge request GNOME/gtk!6263
2023-08-12 21:39:15 +00:00
Matthias Clasen aaac0cb4a1 Merge branch 'bilelmoussaoui/linkify' into 'main'
docs: Drop uneeded ` in gi-docgen links

See merge request GNOME/gtk!6276
2023-08-12 21:23:13 +00:00
Benjamin Otte 67a4279d3f rendernode: Put typedef into gsktypes.h
Makes includes simpler for headers that want to use GskRenderNode.
2023-08-12 23:13:47 +02:00
Benjamin Otte dd4b8a4c89 gl: Pass swizzle array directly
There's no need to pass pointers to arrays, as arrays are already
pointers.
2023-08-12 23:13:23 +02:00
Benjamin Otte 4cc5a54da3 viewport: Fix leak
Fixes #6002
2023-08-12 23:13:23 +02:00
Benjamin Otte 07add0144e columnview: Fix refcounting woes
The new code did not deal with the GtkScrollInfo properly.

Related: #6002
2023-08-12 23:13:23 +02:00
Benjamin Otte d1c7376b05 gdkcontext: Make has_unpack_subimage() do the version check
Makes the code look simpler when we actually use that function.
2023-08-12 23:13:23 +02:00
Benjamin Otte a71388d2d6 treeviewcolumn: Set ascending class for ascending order
The alternative button order confused the code and so the "ascending"
class was set for descending order by default, not when alternate order
is set.
2023-08-12 23:13:23 +02:00
Matthias Clasen 62176a3bd0 tools: Add gtk4-path-tool
This comes in handy for testing, among other things.

For now, this supports decomposing, reversing, rendering,
info and preview.
2023-08-12 16:34:19 -04:00
Bilal Elmoussaoui 8475a29836 docs: Drop uneeded ` in gi-docgen links
Causes the rust docs to fail parsing those as links
2023-08-12 22:26:25 +02:00
Matthias Clasen 44bc5a139c Add rounded rects to the tests 2023-08-12 16:09:31 -04:00
Matthias Clasen 11f48f3c5b Add gsk_path_builder_add_rounded_rect
For now, this is using gsk_path_builder_svg_arc_to
to approximate elliptical arcs.
2023-08-12 15:34:54 -04:00
Matthias Clasen fa4ad1590d Merge branch 'matthiasc/for-main' into 'main'
path: Some documentation improvements

See merge request GNOME/gtk!6273
2023-08-11 16:42:25 +00:00
Matthias Clasen 47ddc742fc path: Some documentation improvements
Among other things, add a quick summary of
SVG path syntax, and add a few illustrations.
2023-08-11 12:09:12 -04:00
Matthias Clasen 31d994837b Merge branch 'path-tests-3' into 'main'
path builder: Fix add_cairo_path

Closes #6011

See merge request GNOME/gtk!6270
2023-08-11 12:44:00 +00:00
Matthias Clasen 83d692e2d3 path: Add tests for gsk_path_builder_add methods
Check that they all end the current contour.

Fixes: #6011
2023-08-11 07:24:19 -04:00
Matthias Clasen f2835bb48b pathbuilder: Treat current point consistently
Arrange that none of the 'add' methods change
the current point.
2023-08-10 22:56:47 -04:00
Fran Dieguez 857c22db32 Update Galician translation 2023-08-11 00:35:58 +00:00
Matthias Clasen fff1645060 path builder: Fix add_cairo_path
We were forgetting to end the current contour.
2023-08-10 16:08:32 -04:00
Matthias Clasen 4f0c3c81fb Merge branch 'matthiasc/for-main' into 'main'
Matthiasc/for main

See merge request GNOME/gtk!6269
2023-08-10 04:28:26 +00:00
Matthias Clasen 421365cfc3 Add more path point tests 2023-08-09 23:55:08 -04:00
Matthias Clasen 59c5b41f3d Cosmetics 2023-08-09 23:48:50 -04:00
Matthias Clasen 267367355f Merge branch 'path-fill-tests2' into 'main'
Add more path fill tests

See merge request GNOME/gtk!6268
2023-08-10 03:10:14 +00:00
Matthias Clasen 0aa3356499 Add more path fill tests
These also used to live with measure tests.
2023-08-09 22:47:13 -04:00
Matthias Clasen 14a3b35333 Merge branch 'path-fill-tests' into 'main'
Add path fill tests

See merge request GNOME/gtk!6267
2023-08-10 02:21:34 +00:00
Matthias Clasen 77def95659 Add path fill tests
These used to live with the measure tests, but
path_in_fill no longer involves a measure.
2023-08-09 21:50:29 -04:00
Matthias Clasen 4e91cd1155 Merge branch 'matthiasc/for-main' into 'main'
pathpoint: Use the right type

See merge request GNOME/gtk!6266
2023-08-09 20:37:14 +00:00
Matthias Clasen 2d923d362f Merge branch 'bilelmoussaoui/gi-gsk' into 'main'
gsk/gi: new Path api fixes

See merge request GNOME/gtk!6265
2023-08-09 19:40:53 +00:00
Matthias Clasen b7544636d4 contour: Skip the move
When jumping between segments, we always
need to skip index 0, since thats the move.

This fixes the tangent checks in the
rectangle tests.
2023-08-09 15:12:58 -04:00
Matthias Clasen 6220baf230 get_closest_point: Prefer earlier points
Only switch to a later point if it is strictly
closer than the existing match. This fixes
start- vs endpoint confusion in the rectangle
tests.
2023-08-09 15:12:52 -04:00
Matthias Clasen 917307a6a4 pathpoint: Use the right type 2023-08-09 14:37:14 -04:00
Matthias Clasen 5f49b7b492 Merge branch 'matthiasc/for-main' into 'main'
pathpoint: Clarify docs

See merge request GNOME/gtk!6262
2023-08-09 18:36:05 +00:00
Bilal Elmoussaoui b03abea402 gsk: Don't change the GskRenderNodeType order
As we use their values to check that a node can be downcasted to a node
type in the Rust bindings
such change would break existing code
2023-08-09 20:26:46 +02:00
Bilal Elmoussaoui d4fcc883cb gsk/gi: Add missing since annotations & docs 2023-08-09 20:26:46 +02:00
Daniel Boles ca2a2697d6 Merge branch 'dboles/for-main' into 'main'
docs: Add/fix some links; avoid pluralisation

See merge request GNOME/gtk!6261
2023-08-09 16:57:19 +00:00
Matthias Clasen 68c6f63e90 pathpoint: Clarify docs
Add a paragraph explaining the situation
for comparing the start- and endpoint of
a closed path.
2023-08-09 11:30:26 -04:00
Daniel Boles 0e89fed8ed docs: Add/fix some links; avoid pluralisation
Appending `s` breaks the [type@NS.Object] notation, so fix that in
ListHeader. Add links to ListItem and Overlay, and avoid appending `s`
after `backtick`s just for consistency with the [type@NS.Object] issue.
2023-08-09 15:35:12 +01:00
Benjamin Otte 4d726543d0 Merge branch 'path-for-merge-6' into 'main'
Simplify GskPathPoint

See merge request GNOME/gtk!6260
2023-08-09 14:24:33 +00:00
Matthias Clasen 2bcc998e90 Add tests for gsk_path_builder_add_segment
Test at least some of the special cases:
- start and end point on the same contour
- start and end point on the same segment
- end before start
2023-08-09 07:01:01 -04:00
Matthias Clasen f707185248 Add gsk_path_builder_add_segment
With GskPathPoint, we can subset paths without
a measure object.
2023-08-09 07:01:01 -04:00
Matthias Clasen ded30049db path: Add gsk_path_get_start/end_point
These are useful to have, now that we are
relying more on GskPathPoint in our api.
2023-08-08 22:05:34 -04:00
Matthias Clasen 5c55e9d11e Add gsk_path_point_equal/compare
These just make sense to have.
2023-08-08 22:05:34 -04:00
Matthias Clasen c35bb11c18 Simplify GskPathPoint
Use simple representation of just
{ contour index, segment index, t value }
2023-08-08 20:31:56 -04:00
Matthias Clasen cc19781d27 Merge branch 'cpdb-fix' into 'main'
print: Update printerprivate header location for cpdb backend

See merge request GNOME/gtk!6259
2023-08-08 23:01:12 +00:00
Jeremy Bícha 08d08678e8 print: Update printerprivate header location for cpdb backend
This fixes the print-cpdb build option
2023-08-08 17:55:54 -04:00
Matthias Clasen 11b632a691 Merge branch 'timezone' into 'main'
test-calendar: Update timezone name

See merge request GNOME/gtk!6258
2023-08-08 19:31:31 +00:00
Jeremy Bícha 4881ef251e test-calendar: Update timezone name
Debian no longer provides the MET timezone
but instead provides geographical region and city name
2023-08-08 15:01:42 -04:00
Luca Bacci 5b39abd1a4 Merge branch 'fix-server-side-decorations' into 'main'
win32: Fix minimize and maximize buttons missing on ssd

Closes #5142

See merge request GNOME/gtk!6052
2023-08-08 15:17:09 +00:00
Daniel Mustieles f0089097dd Update Spanish translation 2023-08-08 10:41:17 +00:00
Fran Dieguez d603925ab2 Update Galician translation 2023-08-08 00:05:18 +00:00
dgsasha 5240d8cdf5 win32: Fix minimize and maximize buttons missing on ssd
Completely fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/5142
2023-06-10 23:08:21 -04:00
66 changed files with 7306 additions and 4313 deletions
+6
View File
@@ -34,5 +34,11 @@ base_url = "https://gitlab.gnome.org/GNOME/gtk/-/blob/main/"
[extra]
content_images = [
"gtk-logo.svg",
"images/caps-dark.png",
"images/caps-light.png",
"images/join-dark.png",
"images/join-light.png",
"images/path-dark.png",
"images/path-light.png",
]
urlmap_file = "urlmap.js"
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+81
View File
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg1"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
sodipodi:docname="caps.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="2.1493149"
inkscape:cx="438.74445"
inkscape:cy="288.69664"
inkscape:window-width="1920"
inkscape:window-height="1123"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<linearGradient
id="swatch1"
inkscape:swatch="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop1" />
</linearGradient>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000064;stroke-width:1;stroke-miterlimit:0;stroke-dasharray:none"
d="m 73.096455,77.084329 c 0,0 6.439436,18.711677 18.172676,26.635721"
id="path1"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;fill:none;stroke:#000064;stroke-width:0.2;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 94.214979,99.722163 c -4.705477,-3.177832 -9.013472,-9.233699 -11.888672,-14.531169 -2.8752,-5.29748 -4.353516,-9.587833 -4.353516,-9.587833 L 68.51576,78.85704 c 0,0 1.743663,5.064163 5.021484,11.103457 3.277822,6.039297 8.05428,13.302573 15.082031,18.048773"
id="path1-1"
sodipodi:nodetypes="csccsc" />
<path
style="fill:none;stroke:#000064;stroke-width:1;stroke-miterlimit:0;stroke-dasharray:none"
d="m 112.14213,75.736452 c 0,0 6.43944,18.711675 18.17268,26.635728"
id="path1-4"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;fill:none;stroke:#000064;stroke-width:0.2;stroke-linecap:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1"
d="m 132.83284,98.590758 c -4.70551,-3.177807 -9.01347,-9.233742 -11.88867,-14.53125 -2.8752,-5.297508 -4.35351,-9.587891 -4.35351,-9.587891 -0.89828,-2.611719 -3.74396,-4.000453 -6.35547,-3.101562 -2.61172,0.898277 -4.00046,3.743958 -3.10157,6.355468 0,0 1.74367,5.064185 5.02149,11.103516 3.27782,6.039331 8.05428,13.302641 15.08203,18.048851"
id="path1-4-5"
sodipodi:nodetypes="cscccsc" />
<path
style="fill:none;stroke:#000064;stroke-width:1;stroke-miterlimit:0;stroke-dasharray:none"
d="m 154.10911,73.84561 c 0,0 6.43943,18.711677 18.17267,26.63572"
id="path1-0"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;fill:none;stroke:#000064;stroke-width:0.2;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="M 175.16683,96.47344 C 170.46135,93.295592 166.1514,87.239699 163.2762,81.94219 160.401,76.644682 158.92269,72.3543 158.92269,72.3543 l -1.62696,-4.728516 -9.45508,3.253906 1.62696,4.728516 c 0,0 1.74171,5.066138 5.01953,11.105469 3.27782,6.039331 8.05428,13.300675 15.08203,18.046895"
id="path1-4-1"
sodipodi:nodetypes="csccccsc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+72
View File
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg1"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
sodipodi:docname="join.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.75989759"
inkscape:cx="118.43701"
inkscape:cy="570.47161"
inkscape:window-width="1920"
inkscape:window-height="1123"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000064;stroke-width:1;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none"
d="m 12.748727,122.15935 c 4.449842,-15.44496 4.175632,-15.92044 16.908788,-26.568585 0,0 8.478091,16.421195 35.150804,33.660325"
id="path1"
sodipodi:nodetypes="ccc" />
<path
style="color:#000000;fill:none;stroke:#000064;stroke-width:0.2;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 17.59375,123.72266 c 3.810222,-13.2249 3.591775,-14.13123 11.107422,-20.53516 1.289688,1.82186 2.604788,3.71164 4.800781,6.23242 5.743296,6.59272 14.891452,15.32766 28.632813,24.20899 M 67.5625,125.23047 C 54.631174,116.87268 46.204933,108.77726 41.041016,102.84961 35.877098,96.921954 34.140625,93.474609 34.140625,93.474609 l -2.861328,-5.544922 -4.789063,4.003907 C 13.673794,102.6514 12.411529,105.58692 7.984375,120.95313"
id="path1-3"
sodipodi:nodetypes="ccsccscccc" />
<path
style="fill:none;stroke:#000064;stroke-width:1;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none"
d="m 147.99215,122.60257 c 4.44988,-15.44499 4.17566,-15.92048 16.90889,-26.568657 0,0 8.47814,16.421267 35.15101,33.660397"
id="path1-5"
sodipodi:nodetypes="ccc" />
<path
style="color:#000000;fill:none;stroke:#000064;stroke-width:0.2;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 152.79687,123.98633 c 3.81053,-13.22586 3.59005,-14.12993 11.10743,-20.53516 1.28968,1.82184 2.6029,3.71173 4.79882,6.23242 5.74333,6.59274 14.89333,15.32961 28.63477,24.21094 m 5.42773,-8.40039 c -12.9314,-8.35778 -21.35753,-16.45123 -26.52148,-22.37891 -5.16395,-5.927668 -6.90039,-9.374996 -6.90039,-9.374996 l -7.65039,-1.541015 C 148.87685,102.91706 147.61469,105.85252 143.1875,121.21875"
id="path1-5-8"
sodipodi:nodetypes="ccsccsccc" />
<path
style="fill:none;stroke:#000064;stroke-width:1;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none"
d="m 79.729342,122.0734 c 4.449844,-15.44499 4.175634,-15.92048 16.908794,-26.568654 0,0 8.478114,16.421264 35.150984,33.660394"
id="path1-7"
sodipodi:nodetypes="ccc" />
<path
style="color:#000000;fill:none;stroke:#000064;stroke-width:0.2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 84.533203,123.19336 c 3.810222,-13.22495 3.591776,-14.1312 11.107422,-20.53516 1.289696,1.82187 2.604775,3.71164 4.800785,6.23243 5.74333,6.59275 14.89136,15.32765 28.63281,24.20898 m 5.42969,-8.39844 c -12.93141,-8.35778 -21.35949,-16.45122 -26.52344,-22.3789 -5.16395,-5.927686 -6.90039,-9.375004 -6.90039,-9.375004 -0.69636,-1.348401 -1.967393,-2.307601 -3.45508,-2.607422 -1.487509,-0.300132 -3.03083,0.09145 -4.195313,1.064453 C 80.61325,102.12215 79.350982,105.05756 74.923828,120.42383"
id="path1-7-4"
sodipodi:nodetypes="ccsccscccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+108
View File
@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg1"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
sodipodi:docname="path.svg"
inkscape:export-filename="path-dark.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.75989759"
inkscape:cx="397.42198"
inkscape:cy="561.25984"
inkscape:window-width="1920"
inkscape:window-height="1123"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="M 128.28233,149.47912 154.23127,90.244656 86.340809,68.820468"
id="path3" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;fill-opacity:1;stroke-dasharray:none;stroke-opacity:1"
d="m 33.956804,113.31099 c -9.049189,-11.90893 5.40551,-40.570358 20.351208,-39.990271 26.687501,1.035822 4.06495,71.984581 30.695656,74.009711 19.642072,1.49368 41.962402,-34.42048 30.634382,-50.536241 C 99.663884,74.068616 50.763019,135.42833 33.956804,113.31099 Z"
id="path1"
sodipodi:nodetypes="sssss" />
<circle
style="fill:#ff0404;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path2"
cx="34.517788"
cy="113.66589"
r="1.5" />
<circle
style="fill:#ff0404;fill-opacity:1;stroke:#000064;stroke-width:0.2;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-dasharray:none"
id="path2-8"
cx="55.045853"
cy="73.566689"
r="1.5" />
<circle
style="fill:#ff0404;fill-opacity:1;stroke:#000064;stroke-width:0.2;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-dasharray:none"
id="path2-8-5"
cx="55.045834"
cy="73.831245"
r="1.5" />
<circle
style="fill:#ff0404;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path2-8-9"
cx="55.045834"
cy="73.831245"
r="1.5" />
<circle
style="fill:#ff0404;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path2-8-9-7"
cx="86.398613"
cy="68.897667"
r="1.5" />
<circle
style="fill:#ff0404;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path2-8-9-7-8"
cx="153.87553"
cy="90.112595"
r="1.5" />
<circle
style="fill:#ff0404;fill-opacity:1;stroke:#000064;stroke-width:0.2;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-dasharray:none"
id="path2-8-7"
cx="84.803238"
cy="147.51736"
r="1.5" />
<circle
style="fill:#ff0404;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path2-8-7-2"
cx="115.28235"
cy="96.705284"
r="1.5" />
<circle
style="fill:#ff0404;fill-opacity:1;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path2-8-9-7-9"
cx="127.92362"
cy="150.03427"
r="1.5" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

+181
View File
@@ -0,0 +1,181 @@
.. _gtk4-path-tool(1):
=================
gtk4-path-tool
=================
-----------------------
GskPath Utility
-----------------------
SYNOPSIS
--------
| **gtk4-path-tool** <COMMAND> [OPTIONS...] <PATH>
|
| **gtk4-path-tool** decompose [OPTIONS...] <PATH>
| **gtk4-path-tool** show [OPTIONS...] <PATH>
| **gtk4-path-tool** render [OPTIONS...] <PATH>
| **gtk4-path-tool** info [OPTIONS...] <PATH>
DESCRIPTION
-----------
``gtk4-path-tool`` can perform various tasks on paths. Paths are specified
in SVG syntax, as strings like "M 100 100 C 100 200 200 200 200 100 Z".
To read a path from a file, use a filename that starts with a '.' or a '/'.
To read a path from stdin, use '-'.
COMMANDS
--------
Decomposing
^^^^^^^^^^^
The ``decompose`` command approximates the path by one with simpler elements.
When used without options, the curves of the path are approximated by line
segments.
``--allow-quad``
Allow quadratic Bézier curves to be used in the generated path.
``--allow-cubic``
Allow cubic Bézier curves to be used in the generated path.
Showing
^^^^^^^
The ``show`` command displays the given path in a window. The interior
of the path is filled.
``--fill-rule=VALUE``
The fill rule that is used to determine what areas are inside the path.
The possible values are ``winding`` or ``even-odd``. The default is ``winding``.
``--fg-color=COLOR``
The color that is used to fill the interior of the path or stroke the path.
If not specified, black is used.
``--bg-color=COLOR``
The color that is used to render the background behind the path.
If not specified, white is used.
``--stroke``
Stroke the path instead of filling it.
``--line-width=VALUE``
The line width to use for the stroke. ``VALUE`` must be a positive number.
The default line width is 1.
``--line-cap=VALUE``
The cap style to use at line ends. The possible values are ``butt``, ``round``
or ``square``. See the SVG specification for details on these styles.
The default cap style is ``butt``.
``--line-join=VALUE``
The join style to use at line joins. The possible values are ``miter``,
``miter-clip``, ``round``, ``bevel`` or ``arcs``. See the SVG specification
for details on these styles.
The default join style is ``miter``.
``--miter-limit=VALUE``
The limit at which to clip miters at line joins. The default value is 4.
``--dashes=VALUE``
The dash pattern to use for this stroke. A dash pattern is specified by
a comma-separated list of alternating non-negative numbers. Each number
provides the length of alternate "on" and "off" portions of the stroke.
If the dash pattern is empty, dashing is disabled, which is the default.
See the SVG specification for details on dashing.
``--dash-offset=VALUE``
The offset into the dash pattern where dashing should begin.
The default value is 0.
Rendering
^^^^^^^^^
The ``render`` command renders the given path as a PNG image.
The interior of the path is filled.
``--fill-rule=VALUE``
The fill rule that is used to determine what areas are inside the path.
The possible values are ``winding`` or ``even-odd``. The default is ``winding``.
``--fg-color=COLOR``
The color that is used to fill the interior of the path or stroke the path.
If not specified, black is used.
``--bg-color=COLOR``
The color that is used to render the background behind the path.
If not specified, white is used.
``--output-file=FILE``
The file to save the PNG image to.
If not specified, "path.png" is used.
``--stroke``
Stroke the path instead of filling it.
``--line-width=VALUE``
The line width to use for the stroke. ``VALUE`` must be a positive number.
The default line width is 1.
``--line-cap=VALUE``
The cap style to use at line ends. The possible values are ``butt``, ``round``
or ``square``. See the SVG specification for details on these styles.
The default cap style is ``butt``.
``--line-join=VALUE``
The join style to use at line joins. The possible values are ``miter``,
``miter-clip``, ``round``, ``bevel`` or ``arcs``. See the SVG specification
for details on these styles.
The default join style is ``miter``.
``--miter-limit=VALUE``
The limit at which to clip miters at line joins. The default value is 4.
``--dashes=VALUE``
The dash pattern to use for this stroke. A dash pattern is specified by
a comma-separated list of alternating non-negative numbers. Each number
provides the length of alternate "on" and "off" portions of the stroke.
If the dash pattern is empty, dashing is disabled, which is the default.
See the SVG specification for details on dashing.
``--dash-offset=VALUE``
The offset into the dash pattern where dashing should begin.
The default value is 0.
Info
^^^^
The ``info`` command shows various information about the given path,
such as its bounding box.
REFERENCES
----------
- SVG Path Specification, https://www.w3.org/TR/SVG2/paths.html
+1
View File
@@ -77,6 +77,7 @@ if get_option('man-pages') and rst2man.found()
[ 'gtk4-query-settings', '1', ],
[ 'gtk4-rendernode-tool', '1' ],
[ 'gtk4-update-icon-cache', '1', ],
[ 'gtk4-path-tool', '1', ],
]
if get_option('demos')
+10 -10
View File
@@ -297,7 +297,7 @@ gdk_clipboard_read_local_async (GdkClipboard *clipboard,
{
GOutputStream *output_stream;
GIOStream *stream;
stream = gdk_pipe_io_stream_new ();
output_stream = g_io_stream_get_output_stream (stream);
gdk_clipboard_write_async (clipboard,
@@ -767,7 +767,7 @@ gdk_clipboard_read_value_internal (GdkClipboard *clipboard,
GdkContentFormats *formats;
GValue *value;
GTask *task;
task = g_task_new (clipboard, cancellable, callback, user_data);
g_task_set_priority (task, io_priority);
g_task_set_source_tag (task, source_tag);
@@ -946,7 +946,7 @@ gdk_clipboard_read_texture_finish (GdkClipboard *clipboard,
value = g_task_propagate_pointer (G_TASK (result), error);
if (!value)
return NULL;
return g_value_dup_object (value);
}
@@ -1011,7 +1011,7 @@ gdk_clipboard_read_text_finish (GdkClipboard *clipboard,
value = g_task_propagate_pointer (G_TASK (result), error);
if (!value)
return NULL;
return g_value_dup_string (value);
}
@@ -1110,7 +1110,7 @@ gdk_clipboard_write_async (GdkClipboard *clipboard,
GError *error = NULL;
g_assert (gtype != G_TYPE_INVALID);
g_value_init (&value, gtype);
if (gdk_content_provider_get_value (priv->content, &value, &error))
{
@@ -1126,7 +1126,7 @@ gdk_clipboard_write_async (GdkClipboard *clipboard,
{
g_task_return_error (task, error);
}
g_value_unset (&value);
}
else
@@ -1148,7 +1148,7 @@ gdk_clipboard_write_finish (GdkClipboard *clipboard,
g_return_val_if_fail (g_task_is_valid (result, clipboard), FALSE);
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_clipboard_write_async, FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
return g_task_propagate_boolean (G_TASK (result), error);
}
static gboolean
@@ -1248,8 +1248,8 @@ gdk_clipboard_set_content (GdkClipboard *clipboard,
* Sets the clipboard to contain the value collected from the given varargs.
*
* Values should be passed the same way they are passed to other value
* collecting APIs, such as [`method@GObject.Object.set`] or
* [`func@GObject.signal_emit`].
* collecting APIs, such as [method@GObject.Object.set] or
* [func@GObject.signal_emit].
*
* ```c
* gdk_clipboard_set (clipboard, GTK_TYPE_STRING, "Hello World");
@@ -1263,7 +1263,7 @@ gdk_clipboard_set (GdkClipboard *clipboard,
...)
{
va_list args;
g_return_if_fail (GDK_IS_CLIPBOARD (clipboard));
va_start (args, type);
+2 -1
View File
@@ -1528,7 +1528,8 @@ gdk_gl_context_check_extensions (GdkGLContext *context)
if (gdk_gl_context_get_use_es (context))
{
priv->has_unpack_subimage = epoxy_has_gl_extension ("GL_EXT_unpack_subimage");
priv->has_unpack_subimage = gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 0)) ||
epoxy_has_gl_extension ("GL_EXT_unpack_subimage");
priv->has_khr_debug = epoxy_has_gl_extension ("GL_KHR_debug");
}
else
+2 -2
View File
@@ -153,7 +153,7 @@ gdk_gl_texture_find_format (gboolean use_es,
if (gdk_memory_format_alpha (format) != alpha)
continue;
if (!gdk_memory_format_gl_format (format, use_es, gl_major, gl_minor, &q_internal_format, &q_format, &q_type, &q_swizzle))
if (!gdk_memory_format_gl_format (format, use_es, gl_major, gl_minor, &q_internal_format, &q_format, &q_type, q_swizzle))
continue;
if (q_format != gl_format || q_type != gl_type)
@@ -188,7 +188,7 @@ gdk_gl_texture_do_download (GdkGLTexture *self,
FALSE,
major, minor,
&gl_internal_format,
&gl_format, &gl_type, &gl_swizzle))
&gl_format, &gl_type, gl_swizzle))
{
if (download->stride == expected_stride &&
download->format == format)
+2 -2
View File
@@ -739,12 +739,12 @@ gdk_memory_format_gl_format (GdkMemoryFormat format,
guint *out_internal_format,
guint *out_format,
guint *out_type,
GLint (*out_swizzle)[4])
GLint out_swizzle[4])
{
*out_internal_format = memory_formats[format].gl.internal_format;
*out_format = memory_formats[format].gl.format;
*out_type = memory_formats[format].gl.type;
memcpy (out_swizzle, &memory_formats[format].gl.swizzle, sizeof(GLint) * 4);
memcpy (out_swizzle, memory_formats[format].gl.swizzle, sizeof(GLint) * 4);
if (gles)
{
+1 -1
View File
@@ -52,7 +52,7 @@ gboolean gdk_memory_format_gl_format (GdkMemoryFormat
guint *out_internal_format,
guint *out_format,
guint *out_type,
GLint (*out_gizzle)[4]);
GLint out_gizzle[4]);
void gdk_memory_convert (guchar *dest_data,
gsize dest_stride,
+12 -1
View File
@@ -2857,7 +2857,10 @@ gdk_event_translate (MSG *msg,
break;
case WM_SYSCOMMAND:
switch (msg->wParam)
/* From: https://learn.microsoft.com/en-us/windows/win32/menurc/wm-syscommand?redirectedfrom=MSDN
* To obtain the correct result when testing the value of wParam,
* an application must combine the value 0xFFF0 with the wParam value by using the bitwise AND operator. */
switch (msg->wParam & 0xFFF0)
{
case SC_MINIMIZE:
case SC_RESTORE:
@@ -2964,6 +2967,14 @@ gdk_event_translate (MSG *msg,
windowpos = (WINDOWPOS *) msg->lParam;
windowpos->cx = our_mmi.ptMaxSize.x;
windowpos->cy = our_mmi.ptMaxSize.y;
if (!_gdk_win32_surface_lacks_wm_decorations (window) &&
!(windowpos->flags & SWP_NOCLIENTSIZE) &&
window->width == impl->next_layout.configured_width &&
window->height == impl->next_layout.configured_height)
{
impl->inhibit_configure = TRUE;
}
}
impl->maximizing = FALSE;
+1 -8
View File
@@ -1230,18 +1230,11 @@ get_effective_window_decorations (GdkSurface *window,
*decoration |= GDK_DECOR_MINIMIZE;
return TRUE;
}
else if (impl->hint_flags & GDK_HINT_MAX_SIZE)
{
*decoration = GDK_DECOR_ALL | GDK_DECOR_MAXIMIZE;
*decoration |= GDK_DECOR_MINIMIZE;
return TRUE;
}
else
{
*decoration = (GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE);
*decoration = GDK_DECOR_ALL;
return TRUE;
}
+4 -5
View File
@@ -1456,7 +1456,7 @@ memory_format_gl_format (GdkMemoryFormat data_format,
guint *gl_internalformat,
guint *gl_format,
guint *gl_type,
GLint (*gl_swizzle)[4])
GLint gl_swizzle[4])
{
GdkMemoryDepth depth;
@@ -1577,7 +1577,7 @@ gsk_gl_command_queue_do_upload_texture_chunk (GskGLCommandQueue *self,
&gl_internalformat,
&gl_format,
&gl_type,
&gl_swizzle);
gl_swizzle);
gdk_texture_downloader_init (&downloader, texture);
gdk_texture_downloader_set_format (&downloader, data_format);
@@ -1595,8 +1595,7 @@ gsk_gl_command_queue_do_upload_texture_chunk (GskGLCommandQueue *self,
{
glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, data);
}
else if (stride % bpp == 0 &&
(gdk_gl_context_check_version (self->context, NULL, "3.0") || gdk_gl_context_has_unpack_subimage (self->context)))
else if (stride % bpp == 0 && gdk_gl_context_has_unpack_subimage (self->context))
{
glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
@@ -1684,7 +1683,7 @@ gsk_gl_command_queue_upload_texture_chunks (GskGLCommandQueue *self,
&gl_internalformat,
&gl_format,
&gl_type,
&gl_swizzle);
gl_swizzle);
glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL);
+114 -16
View File
@@ -61,6 +61,7 @@ struct _GskContourClass
GskContour * (* reverse) (const GskContour *contour);
int (* get_winding) (const GskContour *contour,
const graphene_point_t *point);
gsize (* get_n_points) (const GskContour *contour);
gboolean (* get_closest_point) (const GskContour *contour,
const graphene_point_t *point,
float threshold,
@@ -76,6 +77,11 @@ struct _GskContourClass
float (* get_curvature) (const GskContour *contour,
GskRealPathPoint *point,
graphene_point_t *center);
void (* add_segment) (const GskContour *contour,
GskPathBuilder *builder,
gboolean emit_move_to,
GskRealPathPoint *start,
GskRealPathPoint *end);
};
/* {{{ Utilities */
@@ -399,6 +405,14 @@ gsk_standard_contour_get_winding (const GskContour *contour,
return winding;
}
static gsize
gsk_standard_contour_get_n_points (const GskContour *contour)
{
GskStandardContour *self = (GskStandardContour *) contour;
return self->n_ops;
}
static gboolean
gsk_standard_contour_get_closest_point (const GskContour *contour,
const graphene_point_t *point,
@@ -420,8 +434,8 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
if (dist <= threshold)
{
*out_dist = dist;
result->data.std.idx = 0;
result->data.std.t = 0;
result->idx = 0;
result->t = 0;
return TRUE;
}
@@ -437,7 +451,8 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
continue;
gsk_curve_init (&c, self->ops[i]);
if (gsk_curve_get_closest_point (&c, point, threshold, &distance, &t))
if (gsk_curve_get_closest_point (&c, point, threshold, &distance, &t) &&
distance < threshold)
{
best_idx = i;
best_t = t;
@@ -448,8 +463,8 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
if (best_idx != G_MAXUINT)
{
*out_dist = threshold;
result->data.std.idx = best_idx;
result->data.std.t = best_t;
result->idx = best_idx;
result->t = best_t;
return TRUE;
}
@@ -464,14 +479,14 @@ gsk_standard_contour_get_position (const GskContour *contour,
GskStandardContour *self = (GskStandardContour *) contour;
GskCurve curve;
if (G_UNLIKELY (point->data.std.idx == 0))
if (G_UNLIKELY (point->idx == 0))
{
*position = self->points[0];
return;
}
gsk_curve_init (&curve, self->ops[point->data.std.idx]);
gsk_curve_get_point (&curve, point->data.std.t, position);
gsk_curve_init (&curve, self->ops[point->idx]);
gsk_curve_get_point (&curve, point->t, position);
}
static void
@@ -485,19 +500,19 @@ gsk_standard_contour_get_tangent (const GskContour *contour,
gsize idx;
float t;
if (G_UNLIKELY (point->data.std.idx == 0))
if (G_UNLIKELY (point->idx == 0))
{
graphene_vec2_init (tangent, 1, 0);
return;
}
idx = point->data.std.idx;
t = point->data.std.t;
idx = point->idx;
t = point->t;
if (t == 0 && direction == GSK_PATH_START)
{
/* Look at the previous segment */
if (idx > 0)
if (idx > 1)
{
idx--;
t = 1;
@@ -518,7 +533,7 @@ gsk_standard_contour_get_tangent (const GskContour *contour,
}
else if (self->flags & GSK_PATH_CLOSED)
{
idx = 0;
idx = 1;
t = 0;
}
}
@@ -535,11 +550,76 @@ gsk_standard_contour_get_curvature (const GskContour *contour,
GskStandardContour *self = (GskStandardContour *) contour;
GskCurve curve;
if (G_UNLIKELY (point->data.std.idx == 0))
if (G_UNLIKELY (point->idx == 0))
return 0;
gsk_curve_init (&curve, self->ops[point->data.std.idx]);
return gsk_curve_get_curvature (&curve, point->data.std.t, center);
gsk_curve_init (&curve, self->ops[point->idx]);
return gsk_curve_get_curvature (&curve, point->t, center);
}
static void
add_curve (GskCurve *curve,
GskPathBuilder *builder,
gboolean *emit_move_to)
{
if (*emit_move_to)
{
const graphene_point_t *s;
s = gsk_curve_get_start_point (curve);
gsk_path_builder_move_to (builder, s->x, s->y);
*emit_move_to = FALSE;
}
gsk_curve_builder_to (curve, builder);
}
static void
gsk_standard_contour_add_segment (const GskContour *contour,
GskPathBuilder *builder,
gboolean emit_move_to,
GskRealPathPoint *start,
GskRealPathPoint *end)
{
GskStandardContour *self = (GskStandardContour *) contour;
GskCurve c, c1, c2;
gsk_curve_init (&c, self->ops[start->idx]);
if (start->idx == end->idx)
{
gsk_curve_segment (&c, start->t, end->t, &c1);
add_curve (&c1, builder, &emit_move_to);
return;
}
if (start->t == 0)
{
add_curve (&c, builder, &emit_move_to);
}
else if (start->t < 1)
{
gsk_curve_split (&c, start->t, &c1, &c2);
add_curve (&c2, builder, &emit_move_to);
}
for (gsize i = start->idx + 1; i < end->idx; i++)
{
gsk_curve_init (&c, self->ops[i]);
add_curve (&c, builder, &emit_move_to);
}
gsk_curve_init (&c, self->ops[end->idx]);
if (c.op == GSK_PATH_CLOSE)
c.op = GSK_PATH_LINE;
if (end->t == 1)
{
add_curve (&c, builder, &emit_move_to);
}
else if (end->t > 0)
{
gsk_curve_split (&c, end->t, &c1, &c2);
add_curve (&c1, builder, &emit_move_to);
}
}
static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
@@ -556,10 +636,12 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
gsk_standard_contour_foreach,
gsk_standard_contour_reverse,
gsk_standard_contour_get_winding,
gsk_standard_contour_get_n_points,
gsk_standard_contour_get_closest_point,
gsk_standard_contour_get_position,
gsk_standard_contour_get_tangent,
gsk_standard_contour_get_curvature,
gsk_standard_contour_add_segment,
};
/* You must ensure the contour has enough size allocated,
@@ -706,6 +788,12 @@ gsk_contour_get_closest_point (const GskContour *self,
return self->klass->get_closest_point (self, point, threshold, result, out_dist);
}
gsize
gsk_contour_get_n_points (const GskContour *self)
{
return self->klass->get_n_points (self);
}
void
gsk_contour_get_position (const GskContour *self,
GskRealPathPoint *point,
@@ -731,6 +819,16 @@ gsk_contour_get_curvature (const GskContour *self,
return self->klass->get_curvature (self, point, center);
}
void
gsk_contour_add_segment (const GskContour *self,
GskPathBuilder *builder,
gboolean emit_move_to,
GskRealPathPoint *start,
GskRealPathPoint *end)
{
self->klass->add_segment (self, builder, emit_move_to, start, end);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */
+7
View File
@@ -57,6 +57,7 @@ void gsk_contour_get_start_end (const GskContou
graphene_point_t *end);
int gsk_contour_get_winding (const GskContour *self,
const graphene_point_t *point);
gsize gsk_contour_get_n_points (const GskContour *self);
gboolean gsk_contour_get_closest_point (const GskContour *self,
const graphene_point_t *point,
float threshold,
@@ -72,5 +73,11 @@ void gsk_contour_get_tangent (const GskContou
float gsk_contour_get_curvature (const GskContour *self,
GskRealPathPoint *point,
graphene_point_t *center);
void gsk_contour_add_segment (const GskContour *self,
GskPathBuilder *builder,
gboolean emit_move_to,
GskRealPathPoint *start,
GskRealPathPoint *end);
G_END_DECLS
+17 -7
View File
@@ -1,5 +1,5 @@
/* GSK - The GTK Scene Kit
* Copyright 2016 Endless
* Copyright 2016 Endless
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -42,8 +42,6 @@
* @GSK_REPEAT_NODE: A node that repeats the child's contents
* @GSK_CLIP_NODE: A node that clips its child to a rectangular area
* @GSK_ROUNDED_CLIP_NODE: A node that clips its child to a rounded rectangle
* @GSK_FILL_NODE: A node that fills a path
* @GSK_STROKE_NODE: A node that strokes a path
* @GSK_SHADOW_NODE: A node that draws a shadow below its child
* @GSK_BLEND_NODE: A node that blends two children together
* @GSK_CROSS_FADE_NODE: A node that cross-fades between two children
@@ -53,7 +51,9 @@
* @GSK_GL_SHADER_NODE: A node that uses OpenGL fragment shaders to render
* @GSK_TEXTURE_SCALE_NODE: A node drawing a `GdkTexture` scaled and filtered (Since: 4.10)
* @GSK_MASK_NODE: A node that masks one child with another (Since: 4.10)
* @GSK_FILL_NODE: A node that fills a path
* @GSK_STROKE_NODE: A node that strokes a path
* The type of a node determines what the node is rendering.
*/
typedef enum {
@@ -76,8 +76,6 @@ typedef enum {
GSK_REPEAT_NODE,
GSK_CLIP_NODE,
GSK_ROUNDED_CLIP_NODE,
GSK_FILL_NODE,
GSK_STROKE_NODE,
GSK_SHADOW_NODE,
GSK_BLEND_NODE,
GSK_CROSS_FADE_NODE,
@@ -86,7 +84,9 @@ typedef enum {
GSK_DEBUG_NODE,
GSK_GL_SHADER_NODE,
GSK_TEXTURE_SCALE_NODE,
GSK_MASK_NODE
GSK_MASK_NODE,
GSK_FILL_NODE,
GSK_STROKE_NODE
} GskRenderNodeType;
/**
@@ -220,6 +220,11 @@ typedef enum {
*
* The default line cap style is `GSK_LINE_CAP_BUTT`.
*
* <picture>
* <source srcset="caps-dark.png" media="(prefers-color-scheme: dark)">
* <img alt="Line Cap Styles" src="caps-light.png">
* </picture>
*
* New entries may be added in future versions.
*
* Since: 4.14
@@ -242,6 +247,11 @@ typedef enum {
*
* The default line join style is `GSK_LINE_JOIN_MITER`.
*
* <picture>
* <source srcset="join-dark.png" media="(prefers-color-scheme: dark)">
* <img alt="Line Join Styles" src="join-light.png">
* </picture>
*
* New entries may be added in future versions.
*
* Since: 4.14
+189 -7
View File
@@ -30,18 +30,26 @@
* GskPath:
*
* A `GskPath` describes lines and curves that are more complex
* than simple rectangles. Paths can used for rendering (filling or
* stroking) and for animations (e.g. as trajectories).
* than simple rectangles.
*
* Paths can used for rendering (filling or stroking) and for animations
* (e.g. as trajectories).
*
* `GskPath` is an immutable, opaque, reference-counted struct.
* After creation, you cannot change the types it represents.
* Instead, new `GskPath` objects have to be created.
*
* The [struct@Gsk.PathBuilder] structure is meant to help in this endeavor.
* After creation, you cannot change the types it represents. Instead,
* new `GskPath` objects have to be created. The [struct@Gsk.PathBuilder]
* structure is meant to help in this endeavor.
*
* Conceptually, a path consists of zero or more contours (continous, connected
* curves), each of which may or may not be closed. Contours are typically
* constructed from Bézier segments.
*
* <picture>
* <source srcset="path-dark.png" media="(prefers-color-scheme: dark)">
* <img alt="A Path" src="path-light.png">
* </picture>
*
* Since: 4.14
*/
struct _GskPath
@@ -420,6 +428,7 @@ gsk_path_get_bounds (GskPath *self,
*
* Returns: `TRUE` if the path has bounds, `FALSE` if the path is known
* to be empty and have no bounds.
* Since: 4.14
*/
gboolean
gsk_path_get_stroke_bounds (GskPath *self,
@@ -490,6 +499,72 @@ gsk_path_in_fill (GskPath *self,
}
}
/**
* gsk_path_get_start_point:
* @self: a `GskPath`
* @result: (out caller-allocates): return location for point
*
* Gets the start point of the path.
*
* An empty path has no points, so `FALSE`
* is returned in this case.
*
* Returns: `TRUE` if @result was filled
*
* Since: 4.14
*/
gboolean
gsk_path_get_start_point (GskPath *self,
GskPathPoint *result)
{
GskRealPathPoint *res = (GskRealPathPoint *) result;
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (result != NULL, FALSE);
if (self->n_contours == 0)
return FALSE;
res->contour = 0;
res->idx = 1;
res->t = 0;
return TRUE;
}
/**
* gsk_path_get_end_point:
* @self: a `GskPath`
* @result: (out caller-allocates): return location for point
*
* Gets the end point of the path.
*
* An empty path has no points, so `FALSE`
* is returned in this case.
*
* Returns: `TRUE` if @result was filled
*
* Since: 4.14
*/
gboolean
gsk_path_get_end_point (GskPath *self,
GskPathPoint *result)
{
GskRealPathPoint *res = (GskRealPathPoint *) result;
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (result != NULL, FALSE);
if (self->n_contours == 0)
return FALSE;
res->contour = self->n_contours - 1;
res->idx = gsk_contour_get_n_points (self->contours[self->n_contours - 1]) - 1;
res->t = 1;
return TRUE;
}
/**
* gsk_path_get_closest_point:
* @self: a `GskPath`
@@ -532,7 +607,6 @@ gsk_path_get_closest_point (GskPath *self,
{
found = TRUE;
res->contour = i;
res->path = self;
threshold = distance;
}
}
@@ -939,6 +1013,22 @@ parse_circle (const char **p,
* [SVG path syntax](https://www.w3.org/TR/SVG11/paths.html#PathData),
* as e.g. produced by [method@Gsk.Path.to_string].
*
* A high-level summary of the syntax:
*
* - `M x y` Move to `(x, y)`
* - `L x y` Add a line from the current point to `(x, y)`
* - `Q x1 y1 x2 y2` Add a quadratic Bézier from the current point to `(x2, y2)`, with control point `(x1, y1)`
* - `C x1 y1 x2 y2 x3 y3` Add a cubic Bézier from the current point to `(x3, y3)`, with control points `(x1, y1)` and `(x2, y2)`
* - `Z` Close the contour by drawing a line back to the start point
* - `H x` Add a horizontal line from the current point to the given x value
* - `V y` Add a vertical line from the current point to the given y value
* - `T x2 y2` Add a quadratic Bézier, using the reflection of the previous segments' control point as control point
* - `S x2 y2 x3 y3` Add a cubic Bézier, using the reflection of the previous segments' second control point as first control point
* - `A rx ry r l s x y` Add an elliptical arc from the current point to `(x, y)` with radii rx and ry. See the SVG documentation for how the other parameters influence the arc.
*
* All the commands have lowercase variants that interpret coordinates
* relative to the current point.
*
* Returns: (nullable): a new `GskPath`, or `NULL`
* if @string could not be parsed
*
@@ -1323,3 +1413,95 @@ error:
return NULL;
}
/**
* gsk_path_get_position:
* @self: a `GskPath`
* @point: a `GskPathPoint` on @path
* @position: (out caller-allocates): Return location for
* the coordinates of the point
*
* Gets the position of the point.
*
* Since: 4.14
*/
void
gsk_path_get_position (GskPath *self,
const GskPathPoint *point,
graphene_point_t *position)
{
GskRealPathPoint *p = (GskRealPathPoint *) point;
g_return_if_fail (self != NULL);
g_return_if_fail (point != NULL);
g_return_if_fail (position != NULL);
g_return_if_fail (p->contour < self->n_contours);
gsk_contour_get_position (self->contours[p->contour], p, position);
}
/**
* gsk_path_get_tangent:
* @self: a `GskPath`
* @point: a `GskPathPoint` on @path
* @direction: the direction for which to return the tangent
* @tangent: (out caller-allocates): Return location for
* the tangent at the point
*
* Gets the tangent of the path at the point.
*
* Note that certain points on a path may not have a single
* tangent, such as sharp turns. At such points, there are
* two tangents -- the direction of the path going into the
* point, and the direction coming out of it. The @direction
* argument lets you choose which one to get.
*
* Since: 4.14
*/
void
gsk_path_get_tangent (GskPath *self,
const GskPathPoint *point,
GskPathDirection direction,
graphene_vec2_t *tangent)
{
GskRealPathPoint *p = (GskRealPathPoint *) point;
g_return_if_fail (self != NULL);
g_return_if_fail (point != NULL);
g_return_if_fail (tangent != NULL);
g_return_if_fail (p->contour < self->n_contours);
gsk_contour_get_tangent (self->contours[p->contour], p, direction, tangent);
}
/**
* gsk_path_get_curvature:
* @self: a `GskPath`
* @point: a `GskPathPoint` on @path
* @center: (out caller-allocates) (nullable): Return location for
* the center of the osculating circle
*
* Calculates the curvature of the path at the point.
*
* Optionally, returns the center of the osculating circle as well.
*
* If the curvature is infinite (at line segments), zero is returned,
* and @center is not modified.
*
* Returns: The curvature of the path at the given point
*
* Since: 4.14
*/
float
gsk_path_get_curvature (GskPath *self,
const GskPathPoint *point,
graphene_point_t *center)
{
GskRealPathPoint *p = (GskRealPathPoint *) point;
g_return_val_if_fail (self != NULL, 0);
g_return_val_if_fail (point != NULL, 0);
g_return_val_if_fail (p->contour < self->n_contours, 0);
return gsk_contour_get_curvature (self->contours[p->contour], p, center);
}
+25
View File
@@ -41,6 +41,8 @@ G_BEGIN_DECLS
* By default, [method@Gsk.Path.foreach] will only emit a path with all operations
* flattened to straight lines to allow for maximum compatibility. The only
* operations emitted will be `GSK_PATH_MOVE`, `GSK_PATH_LINE` and `GSK_PATH_CLOSE`.
*
* Since: 4.14
*/
typedef enum
{
@@ -110,12 +112,35 @@ gboolean gsk_path_in_fill (GskPath
const graphene_point_t *point,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_4_14
gboolean gsk_path_get_start_point (GskPath *self,
GskPathPoint *result);
GDK_AVAILABLE_IN_4_14
gboolean gsk_path_get_end_point (GskPath *self,
GskPathPoint *result);
GDK_AVAILABLE_IN_4_14
gboolean gsk_path_get_closest_point (GskPath *self,
const graphene_point_t *point,
float threshold,
GskPathPoint *result);
GDK_AVAILABLE_IN_4_14
void gsk_path_get_position (GskPath *self,
const GskPathPoint *point,
graphene_point_t *position);
GDK_AVAILABLE_IN_4_14
void gsk_path_get_tangent (GskPath *self,
const GskPathPoint *point,
GskPathDirection direction,
graphene_vec2_t *tangent);
GDK_AVAILABLE_IN_4_14
float gsk_path_get_curvature (GskPath *self,
const GskPathPoint *point,
graphene_point_t *center);
GDK_AVAILABLE_IN_4_14
gboolean gsk_path_foreach (GskPath *self,
GskPathForeachFlags flags,
+173
View File
@@ -54,6 +54,9 @@
* either common shapes like [method@Gsk.PathBuilder.add_circle]
* or by adding from other paths like [method@Gsk.PathBuilder.add_path].
*
* The `gsk_path_builder_add_*` methods always add complete contours,
* and do not use or modify the current point.
*
* The other option is to define each line and curve manually with
* the `gsk_path_builder_*_to` group of functions. You start with
* a call to [method@Gsk.PathBuilder.move_to] to set the starting point
@@ -63,6 +66,8 @@
* back with a line to the starting point.
*
* This is similar for how paths are drawn in Cairo.
*
* Since: 4.14
*/
struct _GskPathBuilder
@@ -388,9 +393,13 @@ void
gsk_path_builder_add_cairo_path (GskPathBuilder *self,
const cairo_path_t *path)
{
graphene_point_t current;
g_return_if_fail (self != NULL);
g_return_if_fail (path != NULL);
current = self->current_point;
for (gsize i = 0; i < path->num_data; i += path->data[i].header.length)
{
const cairo_path_data_t *data = &path->data[i];
@@ -421,6 +430,9 @@ gsk_path_builder_add_cairo_path (GskPathBuilder *self,
break;
}
}
gsk_path_builder_end_current (self);
self->current_point = current;
}
/**
@@ -442,8 +454,12 @@ void
gsk_path_builder_add_rect (GskPathBuilder *self,
const graphene_rect_t *rect)
{
graphene_point_t current;
g_return_if_fail (self != NULL);
current = self->current_point;
gsk_path_builder_move_to (self, rect->origin.x, rect->origin.y);
gsk_path_builder_rel_line_to (self, rect->size.width, 0);
@@ -451,6 +467,79 @@ gsk_path_builder_add_rect (GskPathBuilder *self,
gsk_path_builder_rel_line_to (self, - rect->size.width, 0);
gsk_path_builder_close (self);
self->current_point = current;
}
/**
* gsk_path_builder_add_rounded_rect:
* @self: a #GskPathBuilder
* @rect: the rounded rect
*
* Adds @rect as a new contour to the path built in @self.
*
* Since: 4.14
**/
void
gsk_path_builder_add_rounded_rect (GskPathBuilder *self,
const GskRoundedRect *rect)
{
graphene_point_t current;
g_return_if_fail (self != NULL);
g_return_if_fail (rect != NULL);
current = self->current_point;
gsk_path_builder_move_to (self,
rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width,
rect->bounds.origin.y);
/* top */
gsk_path_builder_line_to (self,
rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_TOP_RIGHT].width,
rect->bounds.origin.y);
/* topright corner */
gsk_path_builder_svg_arc_to (self,
rect->corner[GSK_CORNER_TOP_RIGHT].width,
rect->corner[GSK_CORNER_TOP_RIGHT].height,
0, FALSE, TRUE,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_RIGHT].height);
/* right */
gsk_path_builder_line_to (self,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y + rect->bounds.size.height - rect->corner[GSK_CORNER_BOTTOM_RIGHT].height);
/* bottomright corner */
gsk_path_builder_svg_arc_to (self,
rect->corner[GSK_CORNER_BOTTOM_RIGHT].width,
rect->corner[GSK_CORNER_BOTTOM_RIGHT].height,
0, FALSE, TRUE,
rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_BOTTOM_RIGHT].width,
rect->bounds.origin.y + rect->bounds.size.height);
/* bottom */
gsk_path_builder_line_to (self,
rect->bounds.origin.x + rect->corner[GSK_CORNER_BOTTOM_LEFT].width,
rect->bounds.origin.y + rect->bounds.size.height);
/* bottomleft corner */
gsk_path_builder_svg_arc_to (self,
rect->corner[GSK_CORNER_BOTTOM_LEFT].width,
rect->corner[GSK_CORNER_BOTTOM_LEFT].height,
0, FALSE, TRUE,
rect->bounds.origin.x,
rect->bounds.origin.y + rect->bounds.size.height - rect->corner[GSK_CORNER_BOTTOM_LEFT].height);
/* left */
gsk_path_builder_line_to (self,
rect->bounds.origin.x,
rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_LEFT].height);
/* topleft corner */
gsk_path_builder_svg_arc_to (self,
rect->corner[GSK_CORNER_TOP_LEFT].width,
rect->corner[GSK_CORNER_TOP_LEFT].height,
0, FALSE, TRUE,
rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width,
rect->bounds.origin.y);
/* done */
gsk_path_builder_close (self);
self->current_point = current;
}
static gboolean
@@ -482,16 +571,22 @@ gsk_path_builder_add_circle (GskPathBuilder *self,
const graphene_point_t *center,
float radius)
{
graphene_point_t current;
g_return_if_fail (self != NULL);
g_return_if_fail (center != NULL);
g_return_if_fail (radius > 0);
current = self->current_point;
gsk_path_builder_move_to (self, center->x + radius, center->y);
gsk_spline_decompose_arc (center, radius,
GSK_PATH_TOLERANCE_DEFAULT,
0, 2 * M_PI,
circle_contour_curve, self);
gsk_path_builder_close (self);
self->current_point = current;
}
/**
@@ -983,3 +1078,81 @@ gsk_path_builder_add_layout (GskPathBuilder *self,
cairo_destroy (cr);
cairo_surface_destroy (surface);
}
/**
* gsk_path_builder_add_segment:
* @self: a `GskPathBuilder`
* @path: the `GskPath` to take the segment to
* @start: the point on @path to start at
* @end: the point on @path to end at
*
* Adds to @self the segment of @path from @start to @end.
*
* If @start is equal to or after @end, the path will first add the
* segment from @start to the end of the path, and then add the segment
* from the beginning to @end. If the path is closed, these segments
* will be connected.
*
* Note that this method always adds a path with the given start point
* and end point. To add a closed path, use [method@Gsk.PathBuilder.add_path].
*
* Since: 4.14
*/
void
gsk_path_builder_add_segment (GskPathBuilder *self,
GskPath *path,
const GskPathPoint *start,
const GskPathPoint *end)
{
GskRealPathPoint *s = (GskRealPathPoint *) start;
GskRealPathPoint *e = (GskRealPathPoint *) end;
const GskContour *contour;
gsize n_contours = gsk_path_get_n_contours (path);
graphene_point_t current;
g_return_if_fail (self != NULL);
g_return_if_fail (path != NULL);
g_return_if_fail (start != NULL);
g_return_if_fail (end != NULL);
g_return_if_fail (s->contour < n_contours);
g_return_if_fail (e->contour < n_contours);
current = self->current_point;
contour = gsk_path_get_contour (path, s->contour);
if (s->contour == e->contour)
{
if (gsk_path_point_compare (start, end) < 0)
{
gsk_contour_add_segment (contour, self, TRUE, s, e);
goto out;
}
else if (n_contours == 1)
{
gsk_contour_add_segment (contour, self, TRUE,
s,
&(GskRealPathPoint) { s->contour, gsk_contour_get_n_points (contour) - 1, 1 });
gsk_contour_add_segment (contour, self, FALSE,
&(GskRealPathPoint) { s->contour, 1, 0 },
e);
goto out;
}
}
gsk_contour_add_segment (contour, self, TRUE,
s,
&(GskRealPathPoint) { s->contour, gsk_contour_get_n_points (contour) - 1, 1 });
for (gsize i = (s->contour + 1) % n_contours; i != e->contour; i = (i + 1) % n_contours)
gsk_path_builder_add_contour (self, gsk_contour_dup (gsk_path_get_contour (path, i)));
contour = gsk_path_get_contour (path, e->contour);
gsk_contour_add_segment (contour, self, FALSE,
&(GskRealPathPoint) { e->contour, 1, 0 },
e);
out:
gsk_path_builder_end_current (self);
self->current_point = current;
}
+8 -1
View File
@@ -65,10 +65,17 @@ GDK_AVAILABLE_IN_4_14
void gsk_path_builder_add_rect (GskPathBuilder *self,
const graphene_rect_t *rect);
GDK_AVAILABLE_IN_4_14
void gsk_path_builder_add_rounded_rect (GskPathBuilder *self,
const GskRoundedRect *rect);
GDK_AVAILABLE_IN_4_14
void gsk_path_builder_add_circle (GskPathBuilder *self,
const graphene_point_t *center,
float radius);
GDK_AVAILABLE_IN_4_14
void gsk_path_builder_add_segment (GskPathBuilder *self,
GskPath *path,
const GskPathPoint *start,
const GskPathPoint *end);
GDK_AVAILABLE_IN_4_14
void gsk_path_builder_move_to (GskPathBuilder *self,
float x,
+55 -72
View File
@@ -39,6 +39,8 @@
* don't a reference to the path object they are obtained from. It is the
* callers responsibility to keep a reference to the path as long as the
* `GskPathPoint` is used.
*
* Since: 4.14
*/
G_DEFINE_BOXED_TYPE (GskPathPoint, gsk_path_point,
@@ -65,91 +67,72 @@ gsk_path_point_free (GskPathPoint *point)
}
/**
* gsk_path_point_get_position:
* @path: a `GskPath`
* @point: a `GskPathPoint` on @path
* @position: (out caller-allocates): Return location for
* the coordinates of the point
* gsk_path_point_equal:
* @point1: a `GskPathPoint`
* @point2: another `GskPathPoint`
*
* Gets the position of the point.
* Returns whether the two path points refer to the same
* location on all paths.
*
* Since: 4.14
* Note that the start- and endpoint of a closed contour
* will compare nonequal according to this definition.
* Use [method@Gsk.Path.is_closed] to find out if the
* start- and endpoint of a concrete path refer to the
* same location.
*
* Return: `TRUE` if @point1 and @point2 are equal
*/
void
gsk_path_point_get_position (GskPath *path,
const GskPathPoint *point,
graphene_point_t *position)
gboolean
gsk_path_point_equal (const GskPathPoint *point1,
const GskPathPoint *point2)
{
GskRealPathPoint *self = (GskRealPathPoint *) point;
const GskContour *contour = gsk_path_get_contour (path, self->contour);
const GskRealPathPoint *p1 = (const GskRealPathPoint *) point1;
const GskRealPathPoint *p2 = (const GskRealPathPoint *) point2;
g_return_if_fail (path == self->path);
g_return_if_fail (contour != NULL);
if (p1->contour == p2->contour)
{
if ((p1->idx == p2->idx && p1->t == p2->t) ||
(p1->idx + 1 == p2->idx && p1->t == 1 && p2->t == 0) ||
(p1->idx == p2->idx + 1 && p1->t == 0 && p2->t == 1))
return TRUE;
}
gsk_contour_get_position (contour, self, position);
return FALSE;
}
/**
* gsk_path_point_get_tangent:
* @path: a `GskPath`
* @point: a `GskPathPoint` on @path
* @direction: the direction for which to return the tangent
* @tangent: (out caller-allocates): Return location for
* the tangent at the point
* gsk_path_point_compare:
* @point1: a `GskPathPoint`
* @point2: another `GskPathPoint`
*
* Gets the tangent of the path at the point.
* Returns whether @point1 is before or after @point2.
*
* Note that certain points on a path may not have a single
* tangent, such as sharp turns. At such points, there are
* two tangents -- the direction of the path going into the
* point, and the direction coming out of it. The @direction
* argument lets you choose which one to get.
*
* Since: 4.14
* Returns: -1 if @point1 is before @point2,
* 1 if @point1 is after @point2,
* 0 if they are equal
*/
void
gsk_path_point_get_tangent (GskPath *path,
const GskPathPoint *point,
GskPathDirection direction,
graphene_vec2_t *tangent)
int
gsk_path_point_compare (const GskPathPoint *point1,
const GskPathPoint *point2)
{
GskRealPathPoint *self = (GskRealPathPoint *) point;
const GskContour *contour = gsk_path_get_contour (path, self->contour);
const GskRealPathPoint *p1 = (const GskRealPathPoint *) point1;
const GskRealPathPoint *p2 = (const GskRealPathPoint *) point2;
g_return_if_fail (path == self->path);
g_return_if_fail (contour != NULL);
if (gsk_path_point_equal (point1, point2))
return 0;
gsk_contour_get_tangent (contour, self, direction, tangent);
}
/**
* gsk_path_point_get_curvature:
* @path: a `GskPath`
* @point: a `GskPathPoint` on @path
* @center: (out caller-allocates): Return location for
* the center of the osculating circle
*
* Calculates the curvature of the path at the point.
*
* Optionally, returns the center of the osculating circle as well.
*
* If the curvature is infinite (at line segments), zero is returned,
* and @center is not modified.
*
* Returns: The curvature of the path at the given point
*
* Since: 4.14
*/
float
gsk_path_point_get_curvature (GskPath *path,
const GskPathPoint *point,
graphene_point_t *center)
{
GskRealPathPoint *self = (GskRealPathPoint *) point;
const GskContour *contour = gsk_path_get_contour (path, self->contour);
g_return_val_if_fail (path == self->path, 0);
g_return_val_if_fail (contour != NULL, 0);
return gsk_contour_get_curvature (contour, self, center);
if (p1->contour < p2->contour)
return -1;
else if (p1->contour > p2->contour)
return 1;
else if (p1->idx < p2->idx)
return -1;
else if (p1->idx > p2->idx)
return 1;
else if (p1->t < p2->t)
return -1;
else if (p1->t > p2->t)
return 1;
return 0;
}
+4 -12
View File
@@ -49,19 +49,11 @@ GDK_AVAILABLE_IN_4_14
void gsk_path_point_free (GskPathPoint *point);
GDK_AVAILABLE_IN_4_14
void gsk_path_point_get_position (GskPath *path,
const GskPathPoint *point,
graphene_point_t *position);
gboolean gsk_path_point_equal (const GskPathPoint *point1,
const GskPathPoint *point2);
GDK_AVAILABLE_IN_4_14
void gsk_path_point_get_tangent (GskPath *path,
const GskPathPoint *point,
GskPathDirection direction,
graphene_vec2_t *tangent);
GDK_AVAILABLE_IN_4_14
float gsk_path_point_get_curvature (GskPath *path,
const GskPathPoint *point,
graphene_point_t *center);
int gsk_path_point_compare (const GskPathPoint *point1,
const GskPathPoint *point2);
G_END_DECLS
+2 -8
View File
@@ -7,15 +7,9 @@ G_BEGIN_DECLS
struct _GskRealPathPoint
{
GskPath *path;
gsize contour;
union {
struct {
unsigned int idx;
float t;
} std;
} data;
gsize idx;
float t;
};
G_STATIC_ASSERT (sizeof (GskRealPathPoint) <= sizeof (GskPathPoint));
-1
View File
@@ -35,7 +35,6 @@ G_BEGIN_DECLS
#define GSK_SERIALIZATION_ERROR (gsk_serialization_error_quark ())
typedef struct _GskRenderNode GskRenderNode;
typedef struct _GskColorStop GskColorStop;
typedef struct _GskShadow GskShadow;
+24 -4
View File
@@ -3970,7 +3970,7 @@ gsk_repeat_node_draw (GskRenderNode *node,
scale_x *= width / self->child_bounds.size.width;
scale_y *= height / self->child_bounds.size.height;
cairo_surface_set_device_scale (surface, scale_x, scale_y);
cairo_surface_set_device_offset (surface,
cairo_surface_set_device_offset (surface,
- self->child_bounds.origin.x * scale_x,
- self->child_bounds.origin.y * scale_y);
@@ -4141,7 +4141,7 @@ gsk_clip_node_diff (GskRenderNode *node1,
gsk_render_node_diff_impossible (node1, node2, region);
}
}
static void
gsk_clip_node_class_init (gpointer g_class,
gpointer class_data)
@@ -4372,6 +4372,15 @@ gsk_rounded_clip_node_get_clip (const GskRenderNode *node)
/* }}} */
/* {{{ GSK_FILL_NODE */
/**
* GskFillNode:
*
* A render node filling the area given by [struct@Gsk.Path]
* and [enum@Gsk.FillRule] with the child node.
*
* Since: 4.14
*/
struct _GskFillNode
{
GskRenderNode render_node;
@@ -4572,6 +4581,15 @@ gsk_fill_node_get_fill_rule (const GskRenderNode *node)
/* }}} */
/* {{{ GSK_STROKE_NODE */
/**
* GskStrokeNode:
*
* A render node that will fill the area determined by stroking the the given
* [struct@Gsk.Path] using the [struct@Gsk.Stroke] attributes.
*
* Since: 4.14
*/
struct _GskStrokeNode
{
GskRenderNode render_node;
@@ -4670,8 +4688,10 @@ gsk_stroke_node_class_init (gpointer g_class,
* @path: (transfer none): The path describing the area to stroke
* @stroke: (transfer none): The stroke attributes to use
*
* Creates a #GskRenderNode that will stroke the @child along the given
* @path using the attributes defined in @stroke.
* Creates a #GskRenderNode that will fill the outline generated by stroking
* the given @path using the attributes defined in @stroke.
*
* The area is filled with @child.
*
* Returns: (transfer none) (type GskStrokeNode): A new #GskRenderNode
*
+1 -1
View File
@@ -14,7 +14,7 @@ typedef struct _GskRenderNodeClass GskRenderNodeClass;
* We don't add an "n-types" value to avoid having to handle
* it in every single switch.
*/
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_MASK_NODE + 1)
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_STROKE_NODE + 1)
extern GType gsk_render_node_types[];
+2
View File
@@ -26,6 +26,8 @@
*
* A `GskStroke` struct collects the parameters that influence
* the operation of stroking a path.
*
* Since: 4.14
*/
G_DEFINE_BOXED_TYPE (GskStroke, gsk_stroke, gsk_stroke_copy, gsk_stroke_free)
+1
View File
@@ -29,6 +29,7 @@ typedef struct _GskPath GskPath;
typedef struct _GskPathBuilder GskPathBuilder;
typedef struct _GskPathPoint GskPathPoint;
typedef struct _GskRenderer GskRenderer;
typedef struct _GskRenderNode GskRenderNode;
typedef struct _GskStroke GskStroke;
typedef struct _GskTransform GskTransform;
+2 -2
View File
@@ -234,8 +234,8 @@
* API as they come in. Usually `GtkCellArea` is only interested in
* button events, however some customized derived areas can be implemented
* who are interested in handling other events. Handling an event can
* trigger the [`signal@Gtk.CellArea::focus-changed`] signal to fire; as well
* as [`signal@Gtk.CellArea::add-editable`] in the case that an editable cell
* trigger the [signal@Gtk.CellArea::focus-changed] signal to fire; as well
* as [signal@Gtk.CellArea::add-editable] in the case that an editable cell
* was clicked and needs to start editing. You can call
* [method@Gtk.CellArea.stop_editing] at any time to cancel any cell editing
* that is currently in progress.
+1 -1
View File
@@ -1140,7 +1140,7 @@ gtk_tree_view_class_init (GtkTreeViewClass *class)
* @column: (nullable): the `GtkTreeViewColumn` in which the activation occurred
*
* The "row-activated" signal is emitted when the method
* [`method@Gtk.TreeView.row_activated`] is called.
* [method@Gtk.TreeView.row_activated] is called.
*
* This signal is emitted when the user double-clicks a treeview row with the
* [property@Gtk.TreeView:activate-on-single-click] property set to %FALSE,
+4 -4
View File
@@ -937,13 +937,13 @@ gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column)
if ((!alternative && priv->sort_order == GTK_SORT_ASCENDING) ||
(alternative && priv->sort_order == GTK_SORT_DESCENDING))
{
gtk_widget_remove_css_class (arrow, "descending");
gtk_widget_add_css_class (arrow, "ascending");
gtk_widget_remove_css_class (arrow, "ascending");
gtk_widget_add_css_class (arrow, "descending");
}
else
{
gtk_widget_remove_css_class (arrow, "ascending");
gtk_widget_add_css_class (arrow, "descending");
gtk_widget_remove_css_class (arrow, "descending");
gtk_widget_add_css_class (arrow, "ascending");
}
}
+8 -1
View File
@@ -1798,6 +1798,8 @@ gtk_column_view_scroll_to_column (GtkColumnView *self,
gtk_adjustment_get_page_size (self->hadjustment));
gtk_adjustment_set_value (self->hadjustment, new_value);
g_clear_pointer (&scroll_info, gtk_scroll_info_unref);
}
void
@@ -2214,9 +2216,14 @@ gtk_column_view_scroll_to (GtkColumnView *self,
if (column && (flags & GTK_LIST_SCROLL_FOCUS))
gtk_column_view_set_focus_column (self, column, FALSE);
gtk_list_view_scroll_to (self->listview, pos, flags, scroll);
gtk_list_view_scroll_to (self->listview,
pos,
flags,
scroll ? gtk_scroll_info_ref (scroll) : NULL);
if (column)
gtk_column_view_scroll_to_column (self, column, scroll);
else
g_clear_pointer (&scroll, gtk_scroll_info_unref);
}
+3 -3
View File
@@ -27,9 +27,9 @@
* `GtkListHeader` is used by list widgets to represent the headers they
* display.
*
* The `GtkListHeader`s are managed just like [class@gtk.ListItem]s via
* their factory, but provide a different set of properties suitable for
* managing the header instead of individual items.
* `GtkListHeader` objects are managed just like [class@Gtk.ListItem]
* objects via their factory, but provide a different set of properties suitable
* for managing the header instead of individual items.
*
* Since: 4.12
*/
+4 -3
View File
@@ -27,13 +27,14 @@
/**
* GtkListItem:
*
* `GtkListItem` is used by list widgets to represent items in a `GListModel`.
* `GtkListItem` is used by list widgets to represent items in a
* [iface@Gio.ListModel].
*
* The `GtkListItem`s are managed by the list widget (with its factory)
* `GtkListItem` objects are managed by the list widget (with its factory)
* and cannot be created by applications, but they need to be populated
* by application code. This is done by calling [method@Gtk.ListItem.set_child].
*
* `GtkListItem`s exist in 2 stages:
* `GtkListItem` objects exist in 2 stages:
*
* 1. The unbound stage where the listitem is not currently connected to
* an item in the list. In that case, the [property@Gtk.ListItem:item]
+2 -2
View File
@@ -1510,8 +1510,8 @@ gtk_menu_button_set_child (GtkMenuButton *menu_button,
NULL,
NULL,
NULL,
(GtkGizmoFocusFunc)gtk_widget_focus_self,
(GtkGizmoGrabFocusFunc)gtk_widget_grab_focus_self);
(GtkGizmoFocusFunc)gtk_widget_focus_child,
NULL);
gtk_widget_set_layout_manager (inner_widget, gtk_bin_layout_new ());
gtk_widget_set_hexpand (inner_widget, TRUE);
+2 -2
View File
@@ -33,12 +33,12 @@
/**
* GtkOverlayLayout:
*
* `GtkOverlayLayout` is the layout manager used by `GtkOverlay`.
* `GtkOverlayLayout` is the layout manager used by [class@Gtk.Overlay].
*
* It places widgets as overlays on top of the main child.
*
* This is not a reusable layout manager, since it expects its widget
* to be a `GtkOverlay`. It only listed here so that its layout
* to be a `GtkOverlay`. It is only listed here so that its layout
* properties get documented.
*/
+25 -5
View File
@@ -3977,9 +3977,11 @@ gtk_text_insert_at_cursor (GtkText *self,
if (priv->editable)
{
begin_change (self);
gtk_text_reset_im_context (self);
gtk_editable_insert_text (GTK_EDITABLE (self), str, -1, &pos);
gtk_text_set_selection_bounds (self, pos, pos);
end_change (self);
}
}
@@ -3999,12 +4001,14 @@ gtk_text_delete_from_cursor (GtkText *self,
return;
}
begin_change (self);
if (priv->selection_bound != priv->current_pos)
{
gtk_text_delete_selection (self);
gtk_text_schedule_im_reset (self);
gtk_text_reset_im_context (self);
return;
goto done;
}
switch (type)
@@ -4074,6 +4078,8 @@ gtk_text_delete_from_cursor (GtkText *self,
gtk_text_reset_im_context (self);
}
done:
end_change (self);
gtk_text_pend_cursor_blink (self);
}
@@ -4089,12 +4095,14 @@ gtk_text_backspace (GtkText *self)
return;
}
begin_change (self);
if (priv->selection_bound != priv->current_pos)
{
gtk_text_delete_selection (self);
gtk_text_schedule_im_reset (self);
gtk_text_reset_im_context (self);
return;
goto done;
}
prev_pos = gtk_text_move_logically (self, priv->current_pos, -1);
@@ -4147,6 +4155,8 @@ gtk_text_backspace (GtkText *self)
gtk_widget_error_bell (GTK_WIDGET (self));
}
done:
end_change (self);
gtk_text_pend_cursor_blink (self);
}
@@ -4191,7 +4201,11 @@ gtk_text_cut_clipboard (GtkText *self)
if (priv->editable)
{
if (priv->selection_bound != priv->current_pos)
gtk_text_delete_selection (self);
{
begin_change (self);
gtk_text_delete_selection (self);
end_change (self);
}
}
else
{
@@ -4209,9 +4223,15 @@ gtk_text_paste_clipboard (GtkText *self)
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
if (priv->editable)
gtk_text_paste (self, gtk_widget_get_clipboard (GTK_WIDGET (self)));
{
begin_change (self);
gtk_text_paste (self, gtk_widget_get_clipboard (GTK_WIDGET (self)));
end_change (self);
}
else
gtk_widget_error_bell (GTK_WIDGET (self));
{
gtk_widget_error_bell (GTK_WIDGET (self));
}
gtk_text_update_handles (self);
}
+2
View File
@@ -744,5 +744,7 @@ gtk_viewport_scroll_to (GtkViewport *viewport,
gtk_adjustment_animate_to_value (viewport->adjustment[GTK_ORIENTATION_HORIZONTAL], x);
gtk_adjustment_animate_to_value (viewport->adjustment[GTK_ORIENTATION_VERTICAL], y);
g_clear_pointer (&scroll, gtk_scroll_info_unref);
}
+14 -1
View File
@@ -1312,10 +1312,22 @@ gtk_inspector_object_tree_select_object (GtkInspectorObjectTree *wt,
GTK_LIST_SCROLL_SELECT | GTK_LIST_SCROLL_FOCUS,
NULL);
g_signal_emit (wt, signals[OBJECT_SELECTED], 0, object); // FIXME
g_signal_emit (wt, signals[OBJECT_SELECTED], 0, object);
g_object_unref (row_item);
}
static void
on_selected_item (GtkSingleSelection *selection,
GParamSpec *pspec,
GtkInspectorObjectTree *wt)
{
GObject *selected = gtk_single_selection_get_selected_item (selection);
GtkTreeListRow *row = GTK_TREE_LIST_ROW (selected);
GObject *object = gtk_tree_list_row_get_item (row);
g_signal_emit (wt, signals[OBJECT_SELECTED], 0, object);
g_object_unref (object);
}
void
gtk_inspector_object_tree_set_display (GtkInspectorObjectTree *wt,
GdkDisplay *display)
@@ -1329,4 +1341,5 @@ gtk_inspector_object_tree_set_display (GtkInspectorObjectTree *wt,
wt->priv->selection = gtk_single_selection_new (g_object_ref (G_LIST_MODEL (wt->priv->tree_model)));
gtk_column_view_set_model (GTK_COLUMN_VIEW (wt->priv->list),
GTK_SELECTION_MODEL (wt->priv->selection));
g_signal_connect (wt->priv->selection, "notify::selected-item", G_CALLBACK (on_selected_item), wt);
}
+1 -1
View File
@@ -23,7 +23,7 @@
#include <glib-object.h>
#include <gtk/gtk.h>
#include "gtkprintbackendprivate.h"
#include "gtk/print/gtkprintbackendprivate.h"
G_BEGIN_DECLS
+2 -2
View File
@@ -21,7 +21,7 @@
#include <glib-object.h>
#include <cpdb/cpdb-frontend.h>
#include <gtk/gtkprinterprivate.h>
#include <gtk/print/gtkprinterprivate.h>
G_BEGIN_DECLS
@@ -43,4 +43,4 @@ void gtk_printer_cpdb_set_printer_obj (GtkPrinterCpdb
G_END_DECLS
#endif /* __GTK_PRINTER_CPDB_H__ */
#endif /* __GTK_PRINTER_CPDB_H__ */
+7 -1
View File
@@ -425,9 +425,15 @@ tools/gtk-builder-tool-screenshot.c
tools/gtk-builder-tool-simplify.c
tools/gtk-builder-tool-validate.c
tools/gtk-launch.c
tools/gtk-path-tool.c
tools/gtk-path-tool-decompose.c
tools/gtk-path-tool-info.c
tools/gtk-path-tool-render.c
tools/gtk-path-tool-show.c
tools/gtk-path-tool-utils.c
tools/gtk-rendernode-tool.c
tools/gtk-rendernode-tool-info.c
tools/gtk-rendernode-tool-show.c
tools/gtk-rendernode-tool-render.c
tools/gtk-rendernode-tool-show.c
tools/gtk-rendernode-tool-utils.c
tools/updateiconcache.c
+1075 -813
View File
File diff suppressed because it is too large Load Diff
+1195 -896
View File
File diff suppressed because it is too large Load Diff
+1013 -741
View File
File diff suppressed because it is too large Load Diff
+1207 -1669
View File
File diff suppressed because it is too large Load Diff
+246 -6
View File
@@ -423,33 +423,269 @@ test_foreach (void)
}
/* Test the basics of the path point api */
typedef struct _GskRealPathPoint GskRealPathPoint;
struct _GskRealPathPoint
{
gsize contour;
gsize idx;
float t;
};
static void
test_path_point (void)
{
GskPath *path;
GskPathPoint point;
GskRealPathPoint *rp = (GskRealPathPoint *)&point;
gboolean ret;
graphene_point_t pos, center;
graphene_vec2_t t1, t2, mx;
float curvature;
path = gsk_path_parse ("M0,0L100,0L100,100L0,100Z");
path = gsk_path_parse ("M 0 0 L 100 0 L 100 100 L 0 100 Z");
ret = gsk_path_get_start_point (path, &point);
g_assert_true (ret);
g_assert_true (rp->contour == 0);
g_assert_true (rp->idx == 1);
g_assert_true (rp->t == 0);
ret = gsk_path_get_end_point (path, &point);
g_assert_true (ret);
g_assert_true (rp->contour == 0);
g_assert_true (rp->idx == 4);
g_assert_true (rp->t == 1);
ret = gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (200, 200), INFINITY, &point);
g_assert_true (ret);
gsk_path_point_get_position (path, &point, &pos);
gsk_path_point_get_tangent (path, &point, GSK_PATH_START, &t1);
gsk_path_point_get_tangent (path, &point, GSK_PATH_END, &t2);
curvature = gsk_path_point_get_curvature (path, &point, &center);
g_assert_true (rp->contour == 0);
g_assert_true (rp->idx == 2);
g_assert_true (rp->t == 1);
gsk_path_get_position (path, &point, &pos);
gsk_path_get_tangent (path, &point, GSK_PATH_START, &t1);
gsk_path_get_tangent (path, &point, GSK_PATH_END, &t2);
curvature = gsk_path_get_curvature (path, &point, &center);
g_print ("%g %g\n", graphene_vec2_get_x (&t1), graphene_vec2_get_y (&t1));
g_assert_true (graphene_point_equal (&pos, &GRAPHENE_POINT_INIT (100, 100)));
g_assert_true (graphene_vec2_equal (&t1, graphene_vec2_y_axis ()));
graphene_vec2_negate (graphene_vec2_x_axis (), &mx);
g_assert_true (graphene_vec2_equal (&t2, &mx));
g_assert_true (curvature == 0);
ret = gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (100, 50), INFINITY, &point);
g_assert_true (ret);
g_assert_true (rp->contour == 0);
g_assert_true (rp->idx == 2);
g_assert_true (rp->t == 0.5);
gsk_path_unref (path);
}
/* Test that gsk_path_builder_add_segment yields the expected results */
static void
test_path_segments (void)
{
struct {
const char *path;
graphene_point_t p1;
graphene_point_t p2;
const char *result;
} tests[] = {
{
"M 0 0 L 100 0 L 50 50 Z",
{ 100, 0 },
{ 50, 50 },
"M 100 0 L 50 50"
},
{
"M 0 0 L 100 0 L 50 50 Z",
{ 50, 0 },
{ 70, 0 },
"M 50 0 L 70 0"
},
{
"M 0 0 L 100 0 L 50 50 Z",
{ 70, 0 },
{ 50, 0 },
"M 70 0 L 100 0 L 50 50 L 0 0 L 50 0"
},
{
"M 0 0 L 100 0 L 50 50 Z",
{ 50, 0 },
{ 50, 50 },
"M 50 0 L 100 0 L 50 50"
},
{
"M 0 0 L 100 0 L 50 50 Z",
{ 100, 0 },
{ 100, 0 },
"M 100 0 L 50 50 L 0 0 L 100 0"
}
};
for (unsigned int i = 0; i < G_N_ELEMENTS (tests); i++)
{
GskPath *path;
GskPathPoint p1, p2;
GskPathBuilder *builder;
GskPath *result;
char *str;
path = gsk_path_parse (tests[i].path);
g_assert_true (gsk_path_get_closest_point (path, &tests[i].p1, INFINITY, &p1));
g_assert_true (gsk_path_get_closest_point (path, &tests[i].p2, INFINITY, &p2));
builder = gsk_path_builder_new ();
gsk_path_builder_add_segment (builder, path, &p1, &p2);
result = gsk_path_builder_free_to_path (builder);
str = gsk_path_to_string (result);
g_assert_cmpstr (str, ==, tests[i].result);
g_free (str);
gsk_path_unref (path);
}
}
static void
test_bad_in_fill (void)
{
GskPath *path;
gboolean inside;
/* A fat Cantarell W */
path = gsk_path_parse ("M -2 694 M 206.1748046875 704 L 390.9371337890625 704 L 551.1888427734375 99.5035400390625 L 473.0489501953125 99.5035400390625 L 649.1048583984375 704 L 828.965087890625 704 L 1028.3077392578125 10 L 857.8111572265625 10 L 710.0489501953125 621.251708984375 L 775.9720458984375 598.426513671875 L 614.5245361328125 14.0489501953125 L 430.2237548828125 14.0489501953125 L 278.6783447265625 602.230712890625 L 330.0909423828125 602.230712890625 L 195.88818359375 10 L 5.7342529296875 10 L 206.1748046875 704 Z");
/* The midpoint of the right foot of a fat Cantarell X */
inside = gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (552.360107, 704.000000), GSK_FILL_RULE_WINDING);
g_assert_false (inside);
gsk_path_unref (path);
}
/* Test that path_in_fill implicitly closes contours. I think this is wrong,
* but it is what "everybody" does.
*/
static void
test_unclosed_in_fill (void)
{
GskPath *path;
path = gsk_path_parse ("M 0 0 L 0 100 L 100 100 L 100 0 Z");
g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (50, 50), GSK_FILL_RULE_WINDING));
gsk_path_unref (path);
path = gsk_path_parse ("M 0 0 L 0 100 L 100 100 L 100 0");
g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (50, 50), GSK_FILL_RULE_WINDING));
gsk_path_unref (path);
}
/* Test that all the gsk_path_builder_add methods close the current
* contour in the end and do not change the current point.
*/
static void
test_path_builder_add (void)
{
GskPathBuilder *builder;
GskPath *path, *path2;
char *s;
cairo_path_t *cpath;
cairo_surface_t *surface;
cairo_t *cr;
PangoLayout *layout;
GskPathPoint point1, point2;
#define N_ADD_METHODS 8
path = gsk_path_parse ("M 10 10 L 100 100");
gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (50, 50), INFINITY, &point1);
gsk_path_get_end_point (path, &point2);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 100, 100);
cr = cairo_create (surface);
cairo_move_to (cr, 10, 10);
cairo_line_to (cr, 20, 30);
cpath = cairo_copy_path (cr);
layout = pango_cairo_create_layout (cr);
pango_layout_set_text (layout, "ABC", -1);
for (unsigned int i = 0; i < N_ADD_METHODS; i++)
{
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, 123, 456);
switch (i)
{
case 0:
gsk_path_builder_add_path (builder, path);
break;
case 1:
gsk_path_builder_add_reverse_path (builder, path);
break;
case 2:
gsk_path_builder_add_segment (builder, path, &point1, &point2);
break;
case 3:
gsk_path_builder_add_cairo_path (builder, cpath);
break;
case 4:
gsk_path_builder_add_layout (builder, layout);
break;
case 5:
gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (0, 0, 10, 10));
break;
case 6:
{
GskRoundedRect rect;
gsk_rounded_rect_init (&rect,
&GRAPHENE_RECT_INIT (0, 0, 100, 100),
&GRAPHENE_SIZE_INIT (10, 20),
&GRAPHENE_SIZE_INIT (20, 30),
&GRAPHENE_SIZE_INIT (0, 0),
&GRAPHENE_SIZE_INIT (10, 10));
gsk_path_builder_add_rounded_rect (builder, &rect);
}
break;
case 7:
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 0), 10);
break;
default:
g_assert_not_reached ();
}
gsk_path_builder_rel_line_to (builder, 10, 0);
path2 = gsk_path_builder_free_to_path (builder);
s = gsk_path_to_string (path2);
g_assert_true (g_str_has_prefix (s, "M 123 456"));
g_assert_true (g_str_has_suffix (s, "M 123 456 L 133 456"));
g_free (s);
gsk_path_unref (path2);
}
g_object_unref (layout);
cairo_path_destroy (cpath);
cairo_destroy (cr);
cairo_surface_destroy (surface);
gsk_path_unref (path);
}
@@ -463,6 +699,10 @@ main (int argc, char *argv[])
g_test_add_func ("/path/rect", test_rect_path);
g_test_add_func ("/path/foreach", test_foreach);
g_test_add_func ("/path/point", test_path_point);
g_test_add_func ("/path/segments", test_path_segments);
g_test_add_func ("/path/bad-in-fill", test_bad_in_fill);
g_test_add_func ("/path/unclosed-in-fill", test_unclosed_in_fill);
g_test_add_func ("/path/builder/add", test_path_builder_add);
return g_test_run ();
}
+162
View File
@@ -582,6 +582,166 @@ test_parse (void)
}
}
#define N_PATHS 3
static void
test_in_fill_union (void)
{
GskPath *path, *paths[N_PATHS];
GskPathBuilder *builder;
guint i, j, k;
for (i = 0; i < 100; i++)
{
builder = gsk_path_builder_new ();
for (k = 0; k < N_PATHS; k++)
{
paths[k] = create_random_path (G_MAXUINT);
gsk_path_builder_add_path (builder, paths[k]);
}
path = gsk_path_builder_free_to_path (builder);
for (j = 0; j < 100; j++)
{
graphene_point_t test = GRAPHENE_POINT_INIT (g_test_rand_double_range (-1000, 1000),
g_test_rand_double_range (-1000, 1000));
GskFillRule fill_rule;
for (fill_rule = GSK_FILL_RULE_WINDING; fill_rule <= GSK_FILL_RULE_EVEN_ODD; fill_rule++)
{
guint n_in_fill = 0;
gboolean in_fill;
for (k = 0; k < N_PATHS; k++)
{
if (gsk_path_in_fill (paths[k], &test, GSK_FILL_RULE_EVEN_ODD))
n_in_fill++;
}
in_fill = gsk_path_in_fill (path, &test, GSK_FILL_RULE_EVEN_ODD);
switch (fill_rule)
{
case GSK_FILL_RULE_WINDING:
if (n_in_fill == 0)
g_assert_false (in_fill);
else if (n_in_fill == 1)
g_assert_true (in_fill);
/* else we can't say anything because the winding rule doesn't give enough info */
break;
case GSK_FILL_RULE_EVEN_ODD:
g_assert_cmpint (in_fill, ==, n_in_fill & 1);
break;
default:
g_assert_not_reached ();
break;
}
}
}
gsk_path_unref (path);
for (k = 0; k < N_PATHS; k++)
gsk_path_unref (paths[k]);
}
}
#undef N_PATHS
/* This is somewhat sucky because using foreach breaks up the contours
* (like rects and circles) and replaces everything with the standard
* contour.
* But at least it extensively tests the standard contour.
*/
static gboolean
rotate_path_cb (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
gpointer user_data)
{
GskPathBuilder **builders = user_data;
switch (op)
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builders[0], pts[0].x, pts[0].y);
gsk_path_builder_move_to (builders[1], pts[0].y, -pts[0].x);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (builders[0]);
gsk_path_builder_close (builders[1]);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builders[0], pts[1].x, pts[1].y);
gsk_path_builder_line_to (builders[1], pts[1].y, -pts[1].x);
break;
case GSK_PATH_QUAD:
gsk_path_builder_quad_to (builders[0], pts[1].x, pts[1].y, pts[2].x, pts[2].y);
gsk_path_builder_quad_to (builders[1], pts[1].y, -pts[1].x, pts[2].y, -pts[2].x);
break;
case GSK_PATH_CUBIC:
gsk_path_builder_cubic_to (builders[0], pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
gsk_path_builder_cubic_to (builders[1], pts[1].y, -pts[1].x, pts[2].y, -pts[2].x, pts[3].y, -pts[3].x);
break;
default:
g_assert_not_reached ();
return FALSE;
}
return TRUE;
}
static void
test_in_fill_rotated (void)
{
GskPath *path;
GskPathBuilder *builders[2];
GskPath *paths[2];
guint i, j;
#define N_FILL_RULES 2
/* if this triggers, you added a new enum value to GskFillRule, so the define above needs
* an update */
g_assert_null (g_enum_get_value (g_type_class_ref (GSK_TYPE_FILL_RULE), N_FILL_RULES));
for (i = 0; i < 100; i++)
{
path = create_random_path (G_MAXUINT);
builders[0] = gsk_path_builder_new ();
builders[1] = gsk_path_builder_new ();
/* Use -1 here because we want all the flags, even future additions */
gsk_path_foreach (path, -1, rotate_path_cb, builders);
gsk_path_unref (path);
paths[0] = gsk_path_builder_free_to_path (builders[0]);
paths[1] = gsk_path_builder_free_to_path (builders[1]);
for (j = 0; j < 100; j++)
{
GskFillRule fill_rule = g_random_int_range (0, N_FILL_RULES);
float x = g_test_rand_double_range (-1000, 1000);
float y = g_test_rand_double_range (-1000, 1000);
g_assert_cmpint (gsk_path_in_fill (paths[0], &GRAPHENE_POINT_INIT (x, y), fill_rule),
==,
gsk_path_in_fill (paths[1], &GRAPHENE_POINT_INIT (y, -x), fill_rule));
g_assert_cmpint (gsk_path_in_fill (paths[0], &GRAPHENE_POINT_INIT (y, x), fill_rule),
==,
gsk_path_in_fill (paths[1], &GRAPHENE_POINT_INIT (x, -y), fill_rule));
}
gsk_path_unref (paths[0]);
gsk_path_unref (paths[1]);
}
#undef N_FILL_RULES
}
int
main (int argc,
char *argv[])
@@ -590,6 +750,8 @@ main (int argc,
g_test_add_func ("/path/create", test_create);
g_test_add_func ("/path/parse", test_parse);
g_test_add_func ("/path/in-fill-union", test_in_fill_union);
g_test_add_func ("/path/in-fill-rotated", test_in_fill_rotated);
return g_test_run ();
}
+1 -1
View File
@@ -10,7 +10,7 @@ test_calendar_set_day (void)
cal = gtk_calendar_new ();
tz = g_time_zone_new_identifier ("MET");
tz = g_time_zone_new_identifier ("Europe/Brussels");
g_assert_nonnull (tz);
dt = g_date_time_new (tz, 1970, 3, 1, 0, 0, 0);
g_assert_nonnull (dt);
+131
View File
@@ -0,0 +1,131 @@
/* Copyright 2023 Red Hat, Inc.
*
* GTK+ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* GLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GTK+; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include <gtk/gtk.h>
#include "gtk-path-tool.h"
#include <glib/gi18n-lib.h>
static gboolean
foreach_cb (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
gpointer user_data)
{
GskPathBuilder *builder = user_data;
switch (op)
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (builder);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_QUAD:
gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y);
break;
case GSK_PATH_CUBIC:
gsk_path_builder_cubic_to (builder, pts[1].x, pts[1].y,
pts[2].x, pts[2].y,
pts[3].x, pts[3].y);
break;
default:
g_assert_not_reached ();
}
return TRUE;
}
void
do_decompose (int *argc, const char ***argv)
{
GError *error = NULL;
gboolean allow_quad = FALSE;
gboolean allow_curve = FALSE;
char **args = NULL;
GOptionContext *context;
GOptionEntry entries[] = {
{ "allow-quad", 0, 0, G_OPTION_ARG_NONE, &allow_quad, N_("Allow quadratic Bézier curves"), NULL },
{ "allow-cubic", 0, 0, G_OPTION_ARG_NONE, &allow_curve, N_("Allow cubic Bézier curves"), NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("PATH") },
{ NULL, },
};
GskPathForeachFlags flags;
GskPath *path, *result;
GskPathBuilder *builder;
g_set_prgname ("gtk4-path-tool decompose");
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, _("Decompose a path."));
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
g_printerr ("%s\n", error->message);
g_error_free (error);
exit (1);
}
g_option_context_free (context);
if (args == NULL)
{
g_printerr ("%s\n", _("No paths given."));
exit (1);
}
path = get_path (args[0]);
flags = 0;
if (allow_quad)
flags |= GSK_PATH_FOREACH_ALLOW_QUAD;
if (allow_curve)
flags |= GSK_PATH_FOREACH_ALLOW_CUBIC;
builder = gsk_path_builder_new ();
gsk_path_foreach (path, flags, foreach_cb, builder);
result = gsk_path_builder_free_to_path (builder);
if (result)
{
char *str = gsk_path_to_string (result);
g_print ("%s\n", str);
g_free (str);
}
else
{
g_printerr ("%s\n", _("That didn't work out."));
exit (1);
}
}
+76
View File
@@ -0,0 +1,76 @@
/* Copyright 2023 Red Hat, Inc.
*
* GTK+ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* GLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GTK+; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include <gtk/gtk.h>
#include "gtk-path-tool.h"
#include <glib/gi18n-lib.h>
void
do_info (int *argc, const char ***argv)
{
GError *error = NULL;
char **args = NULL;
GOptionContext *context;
GOptionEntry entries[] = {
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("PATH") },
{ NULL, },
};
GskPath *path;
graphene_rect_t bounds;
g_set_prgname ("gtk4-path-tool info");
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, _("Print information about a path."));
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
g_printerr ("%s\n", error->message);
g_error_free (error);
exit (1);
}
g_option_context_free (context);
if (args == NULL)
{
g_printerr ("%s\n", _("No paths given."));
exit (1);
}
path = get_path (args[0]);
if (gsk_path_is_empty (path))
g_print ("%s\n", _("Path is empty."));
else
{
if (gsk_path_is_closed (path))
g_print ("%s\n", _("Path is closed"));
if (gsk_path_get_bounds (path, &bounds))
g_print ("%s: %g %g %g %g\n", _("Bounds"),
bounds.origin.x, bounds.origin.y,
bounds.size.width, bounds.size.height);
}
}
+208
View File
@@ -0,0 +1,208 @@
/* Copyright 2023 Red Hat, Inc.
*
* GTK+ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* GLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GTK+; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <glib/gi18n-lib.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include "gtk-path-tool.h"
void
do_render (int *argc,
const char ***argv)
{
GError *error = NULL;
const char *fill = "winding";
const char *fg_color = "black";
const char *bg_color = "white";
gboolean do_stroke = FALSE;
double line_width = 1;
const char *cap = "butt";
const char *join = "miter";
double miter_limit = 4;
const char *dashes = NULL;
double dash_offset = 0;
const char *output_file = NULL;
char **args = NULL;
GOptionContext *context;
const GOptionEntry entries[] = {
{ "fill-rule", 0, 0, G_OPTION_ARG_STRING, &fill, N_("Fill rule (winding, even-odd)"), N_("VALUE") },
{ "fg-color", 0, 0, G_OPTION_ARG_STRING, &fg_color, N_("Foreground color"), N_("COLOR") },
{ "bg-color", 0, 0, G_OPTION_ARG_STRING, &bg_color, N_("Background color"), N_("COLOR") },
{ "stroke", 0, 0, G_OPTION_ARG_NONE, &do_stroke, N_("Stroke the path instead of filling it"), NULL },
{ "line-width", 0, 0, G_OPTION_ARG_DOUBLE, &line_width, N_("Line width (number)"), N_("VALUE") },
{ "line-cap", 0, 0, G_OPTION_ARG_STRING, &cap, N_("Line cap (butt, round, square)"), N_("VALUE") },
{ "line-join", 0, 0, G_OPTION_ARG_STRING, &join, N_("Line join (miter, miter-clip, round, bevel, arcs)"), N_("VALUE") },
{ "miter-limit", 0, 0, G_OPTION_ARG_DOUBLE, &miter_limit, N_("Miter limit (number)"), N_("VALUE") },
{ "dashes", 0, 0, G_OPTION_ARG_STRING, &dashes, N_("Dash pattern (comma-separated numbers)"), N_("VALUE") },
{ "dash-offset", 0, 0, G_OPTION_ARG_DOUBLE, &dash_offset, N_("Dash offset (number)"), N_("VALUE") },
{ "output", 0, 0, G_OPTION_ARG_FILENAME, &output_file, N_("The output file"), N_("FILE") },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, NULL, N_("PATH") },
{ NULL, }
};
GskPath *path;
GskFillRule fill_rule;
GdkRGBA fg, bg;
graphene_rect_t bounds;
GskRenderNode *fg_node, *nodes[2], *node;
GdkSurface *surface;
GskRenderer *renderer;
GdkTexture *texture;
const char *filename;
GskLineCap line_cap;
GskLineJoin line_join;
GskStroke *stroke;
if (gdk_display_get_default () == NULL)
{
g_printerr ("%s\n", _("Could not initialize windowing system"));
exit (1);
}
g_set_prgname ("gtk4-path-tool render");
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, _("Render the path to a png image."));
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
g_printerr ("%s\n", error->message);
g_error_free (error);
exit (1);
}
g_option_context_free (context);
if (args == NULL)
{
g_printerr ("%s\n", _("No path specified"));
exit (1);
}
if (g_strv_length (args) > 1)
{
g_printerr ("%s\n", _("Can only render a single path"));
exit (1);
}
path = get_path (args[0]);
fill_rule = get_enum_value (GSK_TYPE_FILL_RULE, _("fill rule"), fill);
get_color (&fg, fg_color);
get_color (&bg, bg_color);
line_cap = get_enum_value (GSK_TYPE_LINE_CAP, _("line cap"), cap);
line_join = get_enum_value (GSK_TYPE_LINE_JOIN, _("line join"), join);
stroke = gsk_stroke_new (line_width);
gsk_stroke_set_line_cap (stroke, line_cap);
gsk_stroke_set_line_join (stroke, line_join);
gsk_stroke_set_miter_limit (stroke, miter_limit);
if (dashes != NULL)
{
GArray *d = g_array_new (FALSE, FALSE, sizeof (float));
char **strings;
strings = g_strsplit (dashes, ",", 0);
for (unsigned int i = 0; strings[i]; i++)
{
char *end = NULL;
float f;
f = (float) g_ascii_strtod (strings[i], &end);
if (*end != '\0')
{
char *msg = g_strdup_printf (_("Failed to parse '%s' as number"), strings[i]);
g_printerr ("%s\n", msg);
exit (1);
}
g_array_append_val (d, f);
}
g_strfreev (strings);
gsk_stroke_set_dash (stroke, (const float *)d->data, d->len);
g_array_unref (d);
}
gsk_stroke_set_dash_offset (stroke, dash_offset);
if (do_stroke)
gsk_path_get_stroke_bounds (path, stroke, &bounds);
else
gsk_path_get_bounds (path, &bounds);
graphene_rect_inset (&bounds, -10, -10);
nodes[0] = gsk_color_node_new (&bg, &bounds);
fg_node = gsk_color_node_new (&fg, &bounds);
if (do_stroke)
nodes[1] = gsk_stroke_node_new (fg_node, path, stroke);
else
nodes[1] = gsk_fill_node_new (fg_node, path, fill_rule);
node = gsk_container_node_new (nodes, 2);
gsk_render_node_unref (fg_node);
gsk_render_node_unref (nodes[0]);
gsk_render_node_unref (nodes[1]);
surface = gdk_surface_new_toplevel (gdk_display_get_default ());
renderer = gsk_renderer_new_for_surface (surface);
texture = gsk_renderer_render_texture (renderer, node, &bounds);
filename = output_file ? output_file : "path.png";
if (!gdk_texture_save_to_png (texture, filename))
{
char *msg = g_strdup_printf (_("Saving png to '%s' failed"), filename);
g_printerr ("%s\n", msg);
exit (1);
}
if (output_file == NULL)
{
char *msg = g_strdup_printf (_("Output written to '%s'."), filename);
g_print ("%s\n", msg);
g_free (msg);
}
g_object_unref (texture);
gsk_renderer_unrealize (renderer);
g_object_unref (renderer);
g_object_unref (surface);
gsk_render_node_unref (node);
gsk_path_unref (path);
g_strfreev (args);
}
+232
View File
@@ -0,0 +1,232 @@
/* Copyright 2023 Red Hat, Inc.
*
* GTK+ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* GLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GTK+; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <glib/gi18n-lib.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include "gtk-path-tool.h"
#include "path-view.h"
static void
show_path_fill (GskPath *path,
GskFillRule fill_rule,
const GdkRGBA *fg_color,
const GdkRGBA *bg_color)
{
GtkWidget *window, *sw, *child;
window = gtk_window_new ();
gtk_window_set_title (GTK_WINDOW (window), _("Path Preview"));
// gtk_window_set_default_size (GTK_WINDOW (window), 700, 500);
sw = gtk_scrolled_window_new ();
gtk_scrolled_window_set_propagate_natural_width (GTK_SCROLLED_WINDOW (sw), TRUE);
gtk_scrolled_window_set_propagate_natural_height (GTK_SCROLLED_WINDOW (sw), TRUE);
gtk_window_set_child (GTK_WINDOW (window), sw);
child = path_view_new (path);
g_object_set (child,
"do-fill", TRUE,
"fill-rule", fill_rule,
"fg-color", fg_color,
"bg-color", bg_color,
NULL);
gtk_widget_set_hexpand (child, TRUE);
gtk_widget_set_vexpand (child, TRUE);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child);
gtk_window_present (GTK_WINDOW (window));
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
g_main_context_iteration (NULL, TRUE);
}
static void
show_path_stroke (GskPath *path,
GskStroke *stroke,
const GdkRGBA *fg_color,
const GdkRGBA *bg_color)
{
GtkWidget *window, *sw, *child;
window = gtk_window_new ();
gtk_window_set_title (GTK_WINDOW (window), _("Path Preview"));
// gtk_window_set_default_size (GTK_WINDOW (window), 700, 500);
sw = gtk_scrolled_window_new ();
gtk_scrolled_window_set_propagate_natural_width (GTK_SCROLLED_WINDOW (sw), TRUE);
gtk_scrolled_window_set_propagate_natural_height (GTK_SCROLLED_WINDOW (sw), TRUE);
gtk_window_set_child (GTK_WINDOW (window), sw);
child = path_view_new (path);
g_object_set (child,
"do-fill", FALSE,
"stroke", stroke,
"fg-color", fg_color,
"bg-color", bg_color,
NULL);
gtk_widget_set_hexpand (child, TRUE);
gtk_widget_set_vexpand (child, TRUE);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child);
gtk_window_present (GTK_WINDOW (window));
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
g_main_context_iteration (NULL, TRUE);
}
void
do_show (int *argc,
const char ***argv)
{
GError *error = NULL;
gboolean do_stroke = FALSE;
const char *fill = "winding";
const char *fg_color = "black";
const char *bg_color = "white";
double line_width = 1;
const char *cap = "butt";
const char *join = "miter";
double miter_limit = 4;
const char *dashes = NULL;
double dash_offset = 0;
char **args = NULL;
GOptionContext *context;
const GOptionEntry entries[] = {
{ "fill-rule", 0, 0, G_OPTION_ARG_STRING, &fill, N_("Fill rule (winding, even-odd)"), N_("VALUE") },
{ "fg-color", 0, 0, G_OPTION_ARG_STRING, &fg_color, N_("Foreground color"), N_("COLOR") },
{ "bg-color", 0, 0, G_OPTION_ARG_STRING, &bg_color, N_("Background color"), N_("COLOR") },
{ "stroke", 0, 0, G_OPTION_ARG_NONE, &do_stroke, N_("Stroke the path instead of filling it"), NULL },
{ "line-width", 0, 0, G_OPTION_ARG_DOUBLE, &line_width, N_("Line width (number)"), N_("VALUE") },
{ "line-cap", 0, 0, G_OPTION_ARG_STRING, &cap, N_("Line cap (butt, round, square)"), N_("VALUE") },
{ "line-join", 0, 0, G_OPTION_ARG_STRING, &join, N_("Line join (miter, miter-clip, round, bevel, arcs)"), N_("VALUE") },
{ "miter-limit", 0, 0, G_OPTION_ARG_DOUBLE, &miter_limit, N_("Miter limit (number)"), N_("VALUE") },
{ "dashes", 0, 0, G_OPTION_ARG_STRING, &dashes, N_("Dash pattern (comma-separated numbers)"), N_("VALUE") },
{ "dash-offset", 0, 0, G_OPTION_ARG_DOUBLE, &dash_offset, N_("Dash offset (number)"), N_("VALUE") },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, NULL, N_("PATH") },
{ NULL, }
};
GskPath *path;
GskFillRule fill_rule;
GdkRGBA fg;
GdkRGBA bg;
GskLineCap line_cap;
GskLineJoin line_join;
GskStroke *stroke;
if (gdk_display_get_default () == NULL)
{
g_printerr ("%s\n", _("Could not initialize windowing system"));
exit (1);
}
g_set_prgname ("gtk4-path-tool show");
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, _("Display the path."));
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
g_printerr ("%s\n", error->message);
g_error_free (error);
exit (1);
}
g_option_context_free (context);
if (args == NULL)
{
g_printerr ("%s\n", _("No path specified"));
exit (1);
}
if (g_strv_length (args) > 1)
{
g_printerr ("%s\n", _("Can only show a single path"));
exit (1);
}
path = get_path (args[0]);
fill_rule = get_enum_value (GSK_TYPE_FILL_RULE, _("fill rule"), fill);
get_color (&fg, fg_color);
get_color (&bg, bg_color);
line_cap = get_enum_value (GSK_TYPE_LINE_CAP, _("line cap"), cap);
line_join = get_enum_value (GSK_TYPE_LINE_JOIN, _("line join"), join);
stroke = gsk_stroke_new (line_width);
gsk_stroke_set_line_cap (stroke, line_cap);
gsk_stroke_set_line_join (stroke, line_join);
gsk_stroke_set_miter_limit (stroke, miter_limit);
if (dashes != NULL)
{
GArray *d = g_array_new (FALSE, FALSE, sizeof (float));
char **strings;
strings = g_strsplit (dashes, ",", 0);
for (unsigned int i = 0; strings[i]; i++)
{
char *end = NULL;
float f;
f = (float) g_ascii_strtod (strings[i], &end);
if (*end != '\0')
{
char *msg = g_strdup_printf (_("Failed to parse '%s' as number"), strings[i]);
g_printerr ("%s\n", msg);
exit (1);
}
g_array_append_val (d, f);
}
g_strfreev (strings);
gsk_stroke_set_dash (stroke, (const float *)d->data, d->len);
g_array_unref (d);
}
gsk_stroke_set_dash_offset (stroke, dash_offset);
if (do_stroke)
show_path_stroke (path, stroke, &fg, &bg);
else
show_path_fill (path, fill_rule, &fg, &bg);
gsk_path_unref (path);
g_strfreev (args);
}
+139
View File
@@ -0,0 +1,139 @@
/* Copyright 2023 Red Hat, Inc.
*
* GTK+ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* GLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GTK+; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include <gtk/gtk.h>
#include <gio/gio.h>
#ifdef G_OS_UNIX
#include <gio/gunixinputstream.h>
#endif
#include "gtk-path-tool.h"
#include <glib/gi18n-lib.h>
GskPath *
get_path (const char *arg)
{
char *buffer = NULL;
gsize len;
GError *error = NULL;
GskPath *path;
if (arg[0] == '.' || arg[0] == '/')
{
if (!g_file_get_contents (arg, &buffer, &len, &error))
{
g_printerr ("%s\n", error->message);
exit (1);
}
}
#ifdef G_OS_UNIX
else if (strcmp (arg, "-") == 0)
{
GInputStream *in;
GOutputStream *out;
in = g_unix_input_stream_new (0, FALSE);
out = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
if (g_output_stream_splice (out, in, 0, NULL, &error) < 0)
{
g_printerr (_("Failed to read from standard input: %s\n"), error->message);
exit (1);
}
if (!g_output_stream_close (out, NULL, &error))
{
g_printerr (_("Error reading from standard input: %s\n"), error->message);
exit (1);
}
buffer = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (out));
g_object_unref (out);
g_object_unref (in);
}
#endif
else
buffer = g_strdup (arg);
g_strstrip (buffer);
path = gsk_path_parse (buffer);
if (path == NULL)
{
g_printerr (_("Failed to parse '%s' as path.\n"), arg);
exit (1);
}
g_free (buffer);
return path;
}
int
get_enum_value (GType type,
const char *type_nick,
const char *str)
{
GEnumClass *class = g_type_class_ref (type);
GEnumValue *value;
int val;
value = g_enum_get_value_by_nick (class, str);
if (value)
val = value->value;
else
{
GString *s;
s = g_string_new ("");
g_string_append_printf (s, _("Failed to parse '%s' as %s."), str, type_nick);
g_string_append (s, "\n");
g_string_append (s, _("Possible values: "));
for (unsigned int i = 0; i < class->n_values; i++)
{
if (i > 0)
g_string_append (s, ", ");
g_string_append (s, class->values[i].value_nick);
}
g_printerr ("%s\n", s->str);
g_string_free (s, TRUE);
exit (1);
}
g_type_class_unref (class);
return val;
}
void
get_color (GdkRGBA *rgba,
const char *str)
{
if (!gdk_rgba_parse (rgba, str))
{
char *msg = g_strdup_printf (_("Could not parse '%s' as color"), str);
g_printerr ("%s\n", msg);
exit (1);
}
}
+139
View File
@@ -0,0 +1,139 @@
/* Copyright 2023 Red Hat, Inc.
*
* GTK+ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* GLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GTK+; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <glib/gi18n-lib.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include "gtk-path-tool.h"
static void G_GNUC_NORETURN
usage (void)
{
g_print (_("Usage:\n"
" gtk4-path-tool [COMMAND] [OPTION…] PATH\n"
"\n"
"Perform various tasks on paths.\n"
"\n"
"Commands:\n"
" decompose Decompose the path\n"
" show Display the path in a window\n"
" render Render the path as an image\n"
" info Print information about the path\n"
"\n"));
exit (1);
}
/* A simplified version of g_log_writer_default_would_drop(), to avoid
* bumping up the required version of GLib to 2.68
*/
static gboolean
would_drop (GLogLevelFlags level,
const char *domain)
{
#if GLIB_CHECK_VERSION (2, 68, 0)
return g_log_writer_default_would_drop (level, domain);
#else
return (level & (G_LOG_LEVEL_ERROR |
G_LOG_LEVEL_CRITICAL |
G_LOG_LEVEL_WARNING)) == 0;
#endif
}
static GLogWriterOutput
log_writer_func (GLogLevelFlags level,
const GLogField *fields,
gsize n_fields,
gpointer user_data)
{
gsize i;
const char *domain = NULL;
const char *message = NULL;
for (i = 0; i < n_fields; i++)
{
if (g_strcmp0 (fields[i].key, "GLIB_DOMAIN") == 0)
domain = fields[i].value;
else if (g_strcmp0 (fields[i].key, "MESSAGE") == 0)
message = fields[i].value;
}
if (message != NULL && !would_drop (level, domain))
{
const char *prefix;
switch (level & G_LOG_LEVEL_MASK)
{
case G_LOG_LEVEL_ERROR:
prefix = "ERROR";
break;
case G_LOG_LEVEL_CRITICAL:
prefix = "CRITICAL";
break;
case G_LOG_LEVEL_WARNING:
prefix = "WARNING";
break;
default:
prefix = "INFO";
break;
}
g_printerr ("%s-%s: %s\n", domain, prefix, message);
}
return G_LOG_WRITER_HANDLED;
}
int
main (int argc, const char *argv[])
{
g_set_prgname ("gtk4-path-tool");
g_log_set_writer_func (log_writer_func, NULL, NULL);
gtk_init_check ();
gtk_test_register_all_types ();
if (argc < 2)
usage ();
if (strcmp (argv[1], "--help") == 0)
usage ();
argv++;
argc--;
if (strcmp (argv[0], "decompose") == 0)
do_decompose (&argc, &argv);
else if (strcmp (argv[0], "info") == 0)
do_info (&argc, &argv);
else if (strcmp (argv[0], "render") == 0)
do_render (&argc, &argv);
else if (strcmp (argv[0], "show") == 0)
do_show (&argc, &argv);
else
usage ();
return 0;
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
void do_info (int *argc, const char ***argv);
void do_decompose (int *argc, const char ***argv);
void do_render (int *argc, const char ***argv);
void do_show (int *argc, const char ***argv);
GskPath *get_path (const char *arg);
int get_enum_value (GType type,
const char *type_nick,
const char *str);
void get_color (GdkRGBA *rgba,
const char *str);
+7
View File
@@ -23,6 +23,13 @@ if win32_enabled
endif
gtk_tools = [
['gtk4-path-tool', ['gtk-path-tool.c',
'gtk-path-tool-decompose.c',
'gtk-path-tool-info.c',
'gtk-path-tool-render.c',
'gtk-path-tool-show.c',
'gtk-path-tool-utils.c',
'path-view.c'], [libgtk_dep]],
['gtk4-query-settings', ['gtk-query-settings.c'], [libgtk_dep]],
['gtk4-builder-tool', ['gtk-builder-tool.c',
'gtk-builder-tool-simplify.c',
+271
View File
@@ -0,0 +1,271 @@
/* Copyright 2023 Red Hat, Inc.
*
* GTK+ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* GLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GTK+; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include "path-view.h"
struct _PathView
{
GtkWidget parent_instance;
GskPath *path;
GskStroke *stroke;
graphene_rect_t bounds;
GskFillRule fill_rule;
GdkRGBA fg;
GdkRGBA bg;
int padding;
gboolean do_fill;
};
enum {
PROP_PATH = 1,
PROP_DO_FILL,
PROP_STROKE,
PROP_FILL_RULE,
PROP_FG_COLOR,
PROP_BG_COLOR,
N_PROPERTIES
};
static GParamSpec *properties[N_PROPERTIES] = { NULL, };
struct _PathViewClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (PathView, path_view, GTK_TYPE_WIDGET)
static void
path_view_init (PathView *self)
{
self->do_fill = TRUE;
self->stroke = gsk_stroke_new (1);
self->fill_rule = GSK_FILL_RULE_WINDING;
self->fg = (GdkRGBA) { 0, 0, 0, 1};
self->bg = (GdkRGBA) { 1, 1, 1, 1};
self->padding = 10;
}
static void
path_view_dispose (GObject *object)
{
PathView *self = PATH_VIEW (object);
g_clear_pointer (&self->path, gsk_path_unref);
g_clear_pointer (&self->stroke, gsk_stroke_free);
G_OBJECT_CLASS (path_view_parent_class)->dispose (object);
}
static void
path_view_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
PathView *self = PATH_VIEW (object);
switch (prop_id)
{
case PROP_PATH:
g_value_set_boxed (value, self->path);
break;
case PROP_DO_FILL:
g_value_set_boolean (value, self->do_fill);
break;
case PROP_STROKE:
g_value_set_boxed (value, self->stroke);
break;
case PROP_FILL_RULE:
g_value_set_enum (value, self->fill_rule);
break;
case PROP_FG_COLOR:
g_value_set_boxed (value, &self->fg);
break;
case PROP_BG_COLOR:
g_value_set_boxed (value, &self->bg);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
update_bounds (PathView *self)
{
if (self->do_fill)
gsk_path_get_bounds (self->path, &self->bounds);
else
gsk_path_get_stroke_bounds (self->path, self->stroke, &self->bounds);
}
static void
path_view_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
PathView *self = PATH_VIEW (object);
switch (prop_id)
{
case PROP_PATH:
g_clear_pointer (&self->path, gsk_path_unref);
self->path = g_value_dup_boxed (value);
update_bounds (self);
gtk_widget_queue_resize (GTK_WIDGET (self));
break;
case PROP_DO_FILL:
self->do_fill = g_value_get_boolean (value);
update_bounds (self);
gtk_widget_queue_resize (GTK_WIDGET (self));
break;
case PROP_STROKE:
gsk_stroke_free (self->stroke);
self->stroke = g_value_get_boxed (value);
update_bounds (self);
gtk_widget_queue_resize (GTK_WIDGET (self));
break;
case PROP_FILL_RULE:
self->fill_rule = g_value_get_enum (value);
gtk_widget_queue_draw (GTK_WIDGET (self));
break;
case PROP_FG_COLOR:
self->fg = *(GdkRGBA *) g_value_get_boxed (value);
gtk_widget_queue_draw (GTK_WIDGET (self));
break;
case PROP_BG_COLOR:
self->bg = *(GdkRGBA *) g_value_get_boxed (value);
gtk_widget_queue_draw (GTK_WIDGET (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
path_view_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
PathView *self = PATH_VIEW (widget);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
*minimum = *natural = (int) ceilf (self->bounds.size.width) + 2 * self->padding;
else
*minimum = *natural = (int) ceilf (self->bounds.size.height) + 2 * self->padding;
}
static void
path_view_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
PathView *self = PATH_VIEW (widget);
graphene_rect_t bounds = self->bounds;
graphene_rect_inset (&bounds, - self->padding, - self->padding);
gtk_snapshot_save (snapshot);
gtk_snapshot_append_color (snapshot, &self->bg, &self->bounds);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (self->padding, self->padding));
if (self->do_fill)
gtk_snapshot_push_fill (snapshot, self->path, self->fill_rule);
else
gtk_snapshot_push_stroke (snapshot, self->path, self->stroke);
gtk_snapshot_append_color (snapshot, &self->fg, &self->bounds);
gtk_snapshot_pop (snapshot);
gtk_snapshot_restore (snapshot);
}
static void
path_view_class_init (PathViewClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->dispose = path_view_dispose;
object_class->get_property = path_view_get_property;
object_class->set_property = path_view_set_property;
widget_class->measure = path_view_measure;
widget_class->snapshot = path_view_snapshot;
properties[PROP_PATH]
= g_param_spec_boxed ("path", NULL, NULL,
GSK_TYPE_PATH,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_DO_FILL]
= g_param_spec_boolean ("do-fill", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_STROKE]
= g_param_spec_boxed ("stroke", NULL, NULL,
GSK_TYPE_STROKE,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_FILL_RULE]
= g_param_spec_enum ("fill-rule", NULL, NULL,
GSK_TYPE_FILL_RULE,
GSK_FILL_RULE_WINDING,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_FG_COLOR]
= g_param_spec_boxed ("fg-color", NULL, NULL,
GDK_TYPE_RGBA,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_BG_COLOR]
= g_param_spec_boxed ("bg-color", NULL, NULL,
GDK_TYPE_RGBA,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, N_PROPERTIES, properties);
}
GtkWidget *
path_view_new (GskPath *path)
{
return g_object_new (PATH_TYPE_VIEW,
"path", path,
NULL);
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <gtk/gtk.h>
#define PATH_TYPE_VIEW (path_view_get_type ())
G_DECLARE_FINAL_TYPE (PathView, path_view, PATH, VIEW, GtkWidget)
GtkWidget * path_view_new (GskPath *path);